Compare commits

...
127 Commits
Author SHA1 Message Date
prafull-opensignlabsandGitHub c20611b50b Merge pull request #965 from OpenSignLabs/multiuser_issue
fix: change send email hint, email validation, remove underline from stamp and image modal
2024-07-24 12:00:06 +05:30
prafull-opensignlabs 15d73ee62d fix: change send email hint, email validation, remove underline for stamp modal 2024-07-24 11:58:55 +05:30
AmolandGitHub a334cda9aa Merge pull request #964 from OpenSignLabs/multiuser_issue 2024-07-23 19:44:46 +05:30
prafull-opensignlabs 69d4254b44 feat: add validation for teams and organization 2024-07-23 19:43:36 +05:30
prafull-opensignlabsandGitHub 95d2794754 Merge pull request #962 from OpenSignLabs/multiuser_issue
fix: updated pdf should be downloadable from reports
2024-07-23 11:28:59 +05:30
prafull-opensignlabs 65ce0d430c fix: updated pdf should be downloadable from reports 2024-07-23 11:27:37 +05:30
prafull-opensignlabsandGitHub e7a98d55f0 Merge pull request #961 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: signer selection removed after clicking widget settings and some widget's issue
2024-07-22 19:17:07 +05:30
RaktimaNXG d247c6d0e7 fix: signer selection removed after clicking widget settings, widget placeholder's name field text getting change issue 2024-07-22 16:18:10 +05:30
prafull-opensignlabsandGitHub 20e0277113 Merge pull request #957 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: switch staging base URL and app ID to production for public-sign.
2024-07-22 09:57:16 +05:30
RaktimaNXG 2ed3609c46 fix: change staging url to production for public-sign 2024-07-20 18:03:01 +05:30
AmolandGitHub 2e41dc20c1 Merge pull request #956 from OpenSignLabs/raktima-opensignlabs-patch-11 2024-07-20 13:02:25 +05:30
RaktimaNXG 50a8f93014 fix: unable to finish document issue in public-sign 2024-07-20 12:54:49 +05:30
RaktimaandGitHub 3803b29835 Merge pull request #955 from OpenSignLabs/multiuser_issue
build(deps): update depenceies
2024-07-19 19:37:20 +05:30
prafull-opensignlabs e18586887e build(deps): update depenceies 2024-07-19 19:35:25 +05:30
prafull-opensignlabsandGitHub d137c081ee Merge pull request #954 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: error of something went wrong on finish button in guest login flow
2024-07-19 18:53:26 +05:30
prafull-opensignlabsandGitHub 62dfe32d32 Merge pull request #953 from OpenSignLabs/multiuser_issue
fix: some tab not available in menu
2024-07-19 18:52:59 +05:30
RaktimaNXG f801192a8c fix: error of something went wrong on finish button in guest login flow 2024-07-19 18:01:47 +05:30
prafull-opensignlabs 13d161e3c8 fix: some tab not available in menu 2024-07-19 18:00:53 +05:30
prafull-opensignlabsandGitHub a249baea66 Merge pull request #952 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: the quick-send flow in-progress report shows the finish button even though the current user isn't a signer
2024-07-19 16:46:22 +05:30
RaktimaNXG f6f5b08d43 fix: the quick-send flow in-progress report shows the finish button even though the current user isn't a signer 2024-07-19 16:31:43 +05:30
prafull-opensignlabsandGitHub a1f4e9ea74 Merge pull request #949 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: add CDN for icons, issue in public-template flow
2024-07-19 11:48:30 +05:30
RaktimaNXG 5eed145f4b fix: refactor code 2024-07-19 11:27:52 +05:30
AmolandGitHub 546a0f8e87 Merge pull request #950 from OpenSignLabs/multiuser_issue 2024-07-19 11:15:53 +05:30
prafull-opensignlabs 975c4b079a fix: alerts are not visible is login screen 2024-07-19 11:13:53 +05:30
RaktimaNXG 09e0ac2415 fix: handle condition when sendInOrder enabled to make public template 2024-07-19 09:52:01 +05:30
RaktimaNXG 6140aea31a fix: refactor code 2024-07-18 21:06:51 +05:30
RaktimaNXG c59c921a8b fix: shift the fonts.css file to CDN, ensure emails are sent to all signers when sendInOrder is set to false in public-sign, and add validation for the sequence of public roles when sendInOrder is enabled 2024-07-18 18:56:42 +05:30
AmolandGitHub 4b1e13d39a Merge pull request #948 from OpenSignLabs/multiuser_issue
fix: subscribe to newsletter is not working
2024-07-18 18:44:04 +05:30
prafull-opensignlabs e33e0be735 fix: subscribe to newsletter is not working 2024-07-18 18:42:39 +05:30
AmolandGitHub 2ff4e372d6 Merge pull request #946 from OpenSignLabs/multiuser_issue
fix: when set USE_LOCAL=true get undefined endpoint error in server
2024-07-18 18:37:20 +05:30
prafull-opensignlabs 9cfffe3fbd fix: when set USE_LOCAL=true get undefined endpoint error in server 2024-07-18 17:20:59 +05:30
AmolandGitHub 82ef278a0e Merge pull request #945 from OpenSignLabs/prafull-opensignlabs-patch-22
Update Docker.yml
2024-07-18 16:30:31 +05:30
prafull-opensignlabsandGitHub 574d2d05a4 Update Docker.yml 2024-07-18 16:29:11 +05:30
AmolandGitHub 38f72c7415 Merge pull request #944 from OpenSignLabs/prafull-opensignlabs-patch-21
build (deps): update node package in docker
2024-07-18 16:19:02 +05:30
prafull-opensignlabsandGitHub e00704a43c build (deps): update node package in docker 2024-07-18 15:59:12 +05:30
prafull-opensignlabsandGitHub 778e28fae0 Merge pull request #942 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: unexpected modal issue when the settings icon is clicked on text input type widgets.
2024-07-17 17:59:04 +05:30
RaktimaNXG 1574f86654 fix: add comment to using base url from local storage 2024-07-17 17:57:06 +05:30
RaktimaNXG df17bdcd68 fix: download and print issue during public sign 2024-07-17 16:38:31 +05:30
RaktimaandGitHub f290fd875f Merge branch 'staging' into raktima-opensignlabs-patch-11 2024-07-17 15:19:03 +05:30
AndrewandGitHub caeb055fc2 Merge pull request #941 from OpenSignLabs/multiuser_issue 2024-07-17 15:14:59 +05:30
prafull-opensignlabs 1eafc113e6 fix: migration function 2024-07-17 15:13:14 +05:30
RaktimaNXG 4e46795e0f fix: unexpected modal issue when the settings icon is clicked on text input widgets. 2024-07-17 15:11:06 +05:30
prafull-opensignlabs f7087dfb29 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multiuser_issue 2024-07-17 12:25:12 +05:30
prafull-opensignlabsandGitHub e6a7cda3b6 Merge pull request #939 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: wrong docs appears need-your-sign report, tour still appears even click on don't show again in recipientSignPdf flow
2024-07-17 11:35:14 +05:30
RaktimaNXG db120ecda7 fix: remove console 2024-07-17 11:23:16 +05:30
prafull-opensignlabs 9bfe8b0e26 refactor: remove premium alert 2024-07-17 11:19:32 +05:30
RaktimaNXG c1438be9e0 fix: mismatch report in need-your-sign report, unnecessary tour appears after click on don't show again in recipients signature flow 2024-07-17 09:47:30 +05:30
AndrewandGitHub f4d7e33f97 Merge pull request #937 from OpenSignLabs/migrate_db 2024-07-16 17:40:23 +05:30
prafull-opensignlabs 4ec7fdb078 fix: update migration script to setup db 2024-07-16 17:34:14 +05:30
prafull-opensignlabsandGitHub 3413d0914d Merge pull request #934 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: tailwind css not applied on production mode after load public-sign script in public profile
2024-07-16 16:58:17 +05:30
RaktimaNXG 793b6a5550 add comment 2024-07-16 16:19:58 +05:30
RaktimaNXG 77894f6754 fix: handle css file for public-sign script 2024-07-16 16:13:43 +05:30
RaktimaNXG 812928a6a3 fix: tailwind css not applied on production mode after load public-sign script in public profile 2024-07-16 13:31:52 +05:30
prafull-opensignlabsandGitHub bb398d13a2 Merge pull request #933 from OpenSignLabs/multiuser_issue
fix: provide route to upgrade from v1 to v2 for self hosting
2024-07-16 13:15:04 +05:30
prafull-opensignlabs 2d7a893424 fix: provide route to upgrade from v1 to v2 for self hosting 2024-07-16 13:13:08 +05:30
RaktimaandGitHub 62e943161a Merge pull request #922 from OpenSignLabs/multiuser_issue
feat: change manager role with editor, add admin panel for self-hosting
2024-07-12 19:22:47 +05:30
prafull-opensignlabs 2604156c7f feat: change manager role with editot, add admin panel for self-hosting 2024-07-12 19:07:27 +05:30
prafull-opensignlabsandGitHub 1e04bb4dbe Merge pull request #921 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: webpack build error, restrict space in public username
2024-07-12 14:41:24 +05:30
RaktimaNXG 0ac3a70a83 fix: webpack build error, restrict space in public username 2024-07-12 13:00:36 +05:30
prafull-opensignlabsandGitHub 2f1ad45f92 Merge pull request #920 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: public profile user name already exist issue
2024-07-12 11:30:47 +05:30
RaktimaNXG 8263e3247e fix: handle condition to enter username 2024-07-12 11:28:36 +05:30
RaktimaNXG 9666e41880 fix: public profile user name already exist issue 2024-07-12 11:24:49 +05:30
prafull-opensignlabsandGitHub e50f062a91 Merge pull request #915 from OpenSignLabs/raktima-opensignlabs-patch-11
feat : implement public profile functionality and develop script for public sign-in
2024-07-11 10:10:45 +05:30
RaktimaNXG 1c8f401060 fix: change public profile url and console staging url 2024-07-11 09:59:06 +05:30
RaktimaNXG a8e764a62c Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-07-11 09:55:31 +05:30
AmolandGitHub 43bc1f3e17 Update README.md 2024-07-11 01:22:47 +05:30
RaktimaNXG eebed949cd fix: handle getTemplate cloud function condition 2024-07-10 18:28:45 +05:30
AmolandGitHub c7cd9e56f8 Merge pull request #916 from OpenSignLabs/multiuser_issue 2024-07-10 16:46:08 +05:30
prafull-opensignlabs 1c9fb5ebcb feat: ask reason for decline/revoke document 2024-07-10 16:44:20 +05:30
RaktimaNXG 1339259c3b feat : add cloud function for public sign 2024-07-10 12:36:22 +05:30
RaktimaNXG 27a069e1a2 feat :implement public profile functionality and develop script for public sign-in 2024-07-10 12:35:10 +05:30
AmolandGitHub 7a69537255 Merge pull request #913 from OpenSignLabs/multi_user 2024-07-09 20:13:31 +05:30
prafull-opensignlabs 9569b78e66 refactor: commented unncessary btn in report 2024-07-09 19:37:03 +05:30
AmolandGitHub 266b0f27f5 Merge pull request #911 from OpenSignLabs/multi_user 2024-07-09 18:11:34 +05:30
prafull-opensignlabs 00e5c48828 feat: make profile portion from sidebar redirectable, restrict menu 2024-07-09 18:10:14 +05:30
AmolandGitHub c9e010e993 Update USAGE.md 2024-07-09 12:23:06 +05:30
prafull-opensignlabsandGitHub 5b52825ea6 Merge pull request #910 from OpenSignLabs/multi_user
feat: migration script for multi-user classes
2024-07-09 10:05:22 +05:30
prafull-opensignlabs 100b97cf1a refactor: restricted staging plan 2024-07-08 22:16:28 +05:30
prafull-opensignlabs 7a3216a81c feat: migration script for multi-user classes 2024-07-08 20:39:00 +05:30
AndrewandGitHub c6412be70d Merge pull request #909 from OpenSignLabs/multi_user 2024-07-08 19:08:02 +05:30
prafull-opensignlabs 954dbb3375 fix: teams are not visible in add user form, after upgrade subscription some menu are not visible 2024-07-08 19:06:37 +05:30
AndrewandGitHub c996825665 Merge pull request #908 from OpenSignLabs/multi_user 2024-07-08 17:05:53 +05:30
prafull-opensignlabs 5fc1a58bdb feat: add login validation for user and fix warning message in teams and users report 2024-07-08 17:04:12 +05:30
prafull-opensignlabsandGitHub 5c947181a2 Merge pull request #907 from OpenSignLabs/multi_user
build(deps): update parse package
2024-07-08 11:42:59 +05:30
prafull-opensignlabs 2e8b2fa525 build(deps): update parse package 2024-07-08 11:38:15 +05:30
AndrewandGitHub 1e6e5f9dba Merge pull request #903 from OpenSignLabs/multi_user 2024-07-06 18:28:59 +05:30
prafull-opensignlabs fa2e169df4 build(deps): update dependencies 2024-07-06 18:27:48 +05:30
AndrewandGitHub 174023c62f Merge pull request #902 from OpenSignLabs/multi_user 2024-07-06 17:38:45 +05:30
prafull-opensignlabs 1198460c97 fix: remove unncessary localstorage variables 2024-07-06 17:32:40 +05:30
prafull-opensignlabs 07954c8bb6 fix: remove unnecessary button from shared template 2024-07-06 16:59:14 +05:30
AndrewandGitHub d4d9521c31 Merge pull request #901 from OpenSignLabs/multi_user 2024-07-05 00:18:11 +05:30
prafull-opensignlabs d77477f3d1 fix: team list is not visible and change teams class 2024-07-04 20:56:57 +05:30
AndrewandGitHub ff36faa96c Merge pull request #898 from OpenSignLabs/multi_user 2024-07-03 23:32:38 +05:30
prafull-opensignlabs 450de195a7 refactor: change name from department to team 2024-07-03 20:47:19 +05:30
prafull-opensignlabs 57b23d7f97 feat: add share-with opt in templates as well as restrict non-team user 2024-07-03 18:01:51 +05:30
prafull-opensignlabs a246f74e11 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multi_user 2024-07-03 15:58:30 +05:30
prafull-opensignlabsandGitHub 89c687ed06 Merge pull request #897 from OpenSignLabs/raktima-opensignlabs-patch-10
fix: incorrect signer is being assigned to widgets
2024-07-03 15:19:18 +05:30
RaktimaNXG f7e2fa8aec fix: incorrect signer is being assigned to widgets 2024-07-03 11:25:52 +05:30
prafull-opensignlabs 8a2960511c feat: add share with functionality of template 2024-07-02 20:54:30 +05:30
prafull-opensignlabs 832e5c2181 fix: multiple user get added with same email 2024-07-02 13:25:28 +05:30
prafull-opensignlabs 00d900c2b2 fix: department not visible realtime in users reports, remove inactive department from dropdown 2024-07-02 12:30:50 +05:30
prafull-opensignlabs 47ef685bcc Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multi_user 2024-07-02 10:44:35 +05:30
prafull-opensignlabsandGitHub 9f4aff65f2 Merge pull request #896 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/npm_and_yarn-1167fe84be
build(deps): bump the npm_and_yarn group in /apps/OpenSignServer with 2 updates
2024-07-02 10:43:33 +05:30
prafull-opensignlabs 2eddb49b8e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multi_user 2024-07-02 10:19:18 +05:30
prafull-opensignlabsandGitHub 63f1bb9fc7 Merge pull request #895 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/npm_and_yarn-4d67123cbe
build(deps-dev): bump postcss from 8.4.38 to 8.4.39 in /apps/OpenSign in the npm_and_yarn group
2024-07-02 10:19:05 +05:30
prafull-opensignlabs e73b055cc2 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multi_user 2024-07-02 10:16:26 +05:30
prafull-opensignlabs abd7cf9b71 fix: duplicate entries in ancestors array and login issue 2024-07-02 00:27:29 +05:30
dependabot[bot]andGitHub 2dbd41ce65 build(deps): bump the npm_and_yarn group
Bumps the npm_and_yarn group in /apps/OpenSignServer with 2 updates: [parse-server](https://github.com/parse-community/parse-server) and [ws](https://github.com/websockets/ws).


Updates `parse-server` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/parse-community/parse-server/releases)
- [Changelog](https://github.com/parse-community/parse-server/blob/alpha/CHANGELOG.md)
- [Commits](https://github.com/parse-community/parse-server/compare/7.0.0...7.1.0)

Updates `ws` from 7.5.9 to 7.5.10
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.9...7.5.10)

---
updated-dependencies:
- dependency-name: parse-server
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: ws
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 18:36:29 +00:00
dependabot[bot]andGitHub 25ef7c9f39 build(deps-dev): bump postcss
Bumps the npm_and_yarn group in /apps/OpenSign with 1 update: [postcss](https://github.com/postcss/postcss).


Updates `postcss` from 8.4.38 to 8.4.39
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.38...8.4.39)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 18:35:49 +00:00
AmolandGitHub 81a4daa5e8 Merge pull request #894 from pravinOpenSign/patch-2
Update README.md
2024-07-01 20:26:20 +05:30
pravinandGitHub 3f9e6d1f81 Update README.md
OpenSign Images update
2024-07-01 20:10:39 +05:30
AmolandGitHub 1507f113f4 Merge pull request #893 from OpenSignLabs/raktima-opensignlabs-patch-10
fix: font size and font color issue of text box after take another text box
2024-07-01 19:31:19 +05:30
prafull-opensignlabs d46ba8f307 feat: add create department, user form as well as reports for them 2024-07-01 16:47:01 +05:30
RaktimaNXG 2426e7484c fix: font size and font color issue of text box after take another text box 2024-07-01 13:56:29 +05:30
prafull-opensignlabs 17b9fc772e feat: add department and add user form in reports 2024-06-28 21:12:58 +05:30
prafull-opensignlabs 7b2b71fea5 feat: add user from, show deparments, users in pages 2024-06-28 12:48:50 +05:30
prafull-opensignlabs 70a3adebd7 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into multi_user 2024-06-28 12:47:02 +05:30
AndrewandGitHub 9e012ef6c7 Merge pull request #885 from OpenSignLabs/raktima-opensignlabs-patch-10 2024-06-28 12:33:13 +05:30
RaktimaNXG 948e0d3270 remove console 2024-06-28 11:40:07 +05:30
RaktimaNXG 6e4f9ba0e9 remove console 2024-06-28 11:34:05 +05:30
RaktimaNXG a816ceb04b fix: issue displaying expired subscription after password change 2024-06-28 10:59:23 +05:30
RaktimaNXG f864f74f14 fix: not displaying already save font color and font size of text boxes in edit template flow 2024-06-27 19:30:45 +05:30
prafull-opensignlabs 2c715a77a2 feat: add departmentlist and userlist page 2024-06-27 19:00:55 +05:30
RaktimaNXG 9b4cdde80a fix: remove Add text from add recipient in mobile view 2024-06-27 18:45:10 +05:30
RaktimaNXG de2154e6fe fix: change add recipient and role ui design in mobile view and inconsistent font size and color combination in text widgets 2024-06-27 17:30:21 +05:30
RaktimaNXG 76f4ccfcf3 fix: inconsistent font size and color for all type text widgets in the template flow 2024-06-27 13:20:17 +05:30
RaktimaNXG af52bb30f5 fix: signer's list order issue after change order of signers in tamplate and placeholder flow 2024-06-27 12:23:39 +05:30
139 changed files with 231119 additions and 38277 deletions
+3 -3
View File
@@ -2,7 +2,7 @@ name: ci
on: on:
push: push:
branches: branches:
- 'main' - 'staging'
jobs: jobs:
docker: docker:
@@ -10,9 +10,9 @@ jobs:
strategy: strategy:
matrix: matrix:
include: include:
- image: amolshejole/OpenSign - image: opensign/opensign
dockerfile: apps/OpenSign/Dockerhubfile dockerfile: apps/OpenSign/Dockerhubfile
- image: amolshejole/OpenSignServer - image: opensign/opensignserver
dockerfile: apps/OpenSignServer/Dockerhubfile dockerfile: apps/OpenSignServer/Dockerhubfile
steps: steps:
- -
+24 -19
View File
@@ -11,7 +11,9 @@
<a href="https://www.opensignlabs.com/">Website</a> <a href="https://www.opensignlabs.com/">Website</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://www.opensignlabs.com">Docs</a> <a href="https://docs.opensignlabs.com">Help Docs</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1">API Docs</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
<a href="https://www.opensignlabs.com/blog">Blog</a> <a href="https://www.opensignlabs.com/blog">Blog</a>
<span>&nbsp;&nbsp;•&nbsp;&nbsp;</span> <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
@@ -22,7 +24,7 @@
<a href="https://www.linkedin.com/company/opensign%E2%84%A2/about/">LinkedIn</a> <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> </div>
@@ -42,29 +44,32 @@ Please star ⭐ the repo to support us! 😀
### Introduction ### 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 ### Features
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility. - **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 signing experience ever. - **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. - **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 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. - **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.  - **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. - **"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. - **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. - **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. - **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. - **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:** 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. - **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 src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'> <img alt="Dash_board" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/6e7b96f7-3154-4c25-bc1e-caf33925cc47" height='200'>
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'> <img alt="Widgets" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/7edb65bd-c265-4b3d-9758-38e79766c5c2" height='200'>
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/56835efd-c40e-42de-a206-20202c293876 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 ### 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 ### 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
View File
@@ -1,6 +1 @@
The community is working hard to put together a great usage guide. Please visit the official documentation [here](https://docs.opensignlabs.com).
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")
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
presets: ["@babel/preset-env", "@babel/preset-react"]
};
+5380 -591
View File
File diff suppressed because it is too large Load Diff
+32 -18
View File
@@ -4,24 +4,24 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@formkit/auto-animate": "^0.8.2", "@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.6.4", "@lottiefiles/dotlottie-react": "^0.7.2",
"@radix-ui/themes": "^3.0.5", "@radix-ui/themes": "^3.1.1",
"@react-pdf/renderer": "^3.4.4", "@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.5", "@reduxjs/toolkit": "^2.2.6",
"axios": "^1.7.2", "axios": "^1.7.2",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"nth-check": "^2.1.1", "nth-check": "^2.1.1",
"parse": "^5.1.0", "parse": "^5.3.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"print-js": "^1.6.0", "print-js": "^1.6.0",
"radix-ui": "^1.0.1", "radix-ui": "^1.0.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^2.10.2", "react-bootstrap": "^2.10.4",
"react-confetti": "^6.1.0", "react-confetti": "^6.1.0",
"react-cookie": "^7.1.4", "react-cookie": "^7.1.4",
"react-datepicker": "^6.9.0", "react-datepicker": "^7.2.0",
"react-dnd": "^16.0.1", "react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1", "react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3", "react-dnd-multi-backend": "^8.0.3",
@@ -30,16 +30,16 @@
"react-gtm-module": "^2.0.11", "react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-konva": "^18.2.10", "react-konva": "^18.2.10",
"react-pdf": "^9.0.0", "react-pdf": "^9.1.0",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-redux": "^9.1.2", "react-redux": "^9.1.2",
"react-rnd": "^10.4.11", "react-rnd": "^10.4.11",
"react-router-dom": "^6.22.3", "react-router-dom": "^6.25.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1", "react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0", "react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6", "react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.4", "react-tooltip": "^5.27.1",
"react-web-share": "^2.0.2", "react-web-share": "^2.0.2",
"reactour": "^1.19.4", "reactour": "^1.19.4",
"redux": "^5.0.1", "redux": "^5.0.1",
@@ -47,16 +47,18 @@
"regex-parser": "^2.3.0", "regex-parser": "^2.3.0",
"serve": "^14.2.3", "serve": "^14.2.3",
"styled-components": "^4.4.1", "styled-components": "^4.4.1",
"web-vitals": "^4.0.1", "web-vitals": "^4.2.2",
"ws": "^8.17.1" "ws": "^8.18.0"
}, },
"scripts": { "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": "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": "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\"", "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 && npm run build-webpack && react-scripts build",
"build-win": "npm run version-win && react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject", "eject": "react-scripts eject",
"release": "standard-version", "release": "standard-version",
@@ -86,18 +88,30 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.6",
"@babel/preset-env": "^7.24.6",
"@babel/preset-react": "^7.24.6",
"@babel/runtime-corejs2": "^7.24.7", "@babel/runtime-corejs2": "^7.24.7",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
"commitizen": "^4.3.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": "^8.57.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.3", "eslint-plugin-react": "^7.34.3",
"lint-staged": "^15.2.7", "lint-staged": "^15.2.7",
"postcss": "^8.4.38", "mini-css-extract-plugin": "^2.9.0",
"prettier": "^3.3.2", "postcss": "^8.4.39",
"prettier": "^3.3.3",
"pretty-quick": "^4.0.0", "pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.4" "tailwindcss": "^3.4.6",
"terser-webpack-plugin": "^5.3.10",
"webpack-cli": "^5.1.4"
}, },
"overrides": { "overrides": {
"nth-check": "$nth-check", "nth-check": "$nth-check",
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -11,7 +11,7 @@
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" <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" /> integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<link rel="stylesheet" href="/css/fonts.css" /> <link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" />
<title>OpenSign™</title> <title>OpenSign™</title>
</head> </head>
<body> <body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+37 -7
View File
@@ -18,6 +18,8 @@ import LazyPage from "./primitives/LazyPage";
import { isEnableSubscription } from "./constant/const"; import { isEnableSubscription } from "./constant/const";
import SSOVerify from "./pages/SSOVerify"; import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader"; import Loader from "./primitives/Loader";
import TeamList from "./pages/TeamList";
import UserList from "./pages/UserList";
const DebugPdf = lazy(() => import("./pages/DebugPdf")); const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword")); const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin")); const GuestLogin = lazy(() => import("./pages/GuestLogin"));
@@ -30,6 +32,8 @@ const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
const ManageSign = lazy(() => import("./pages/Managesign")); const ManageSign = lazy(() => import("./pages/Managesign"));
const GenerateToken = lazy(() => import("./pages/GenerateToken")); const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook")); const Webhook = lazy(() => import("./pages/Webhook"));
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => { const AppLoader = () => {
@@ -52,11 +56,9 @@ function App() {
const baseurl = process.env.REACT_APP_SERVERURL const baseurl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL ? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app"; : window.location.origin + "/api/app";
const appName = "contracts";
try { try {
localStorage.setItem("baseUrl", `${baseurl}/`); localStorage.setItem("baseUrl", `${baseurl}/`);
localStorage.setItem("parseAppId", appId); localStorage.setItem("parseAppId", appId);
localStorage.setItem("domain", appName);
setIsLoading(false); setIsLoading(false);
} catch (error) { } catch (error) {
console.log("err ", error); console.log("err ", error);
@@ -72,7 +74,21 @@ function App() {
<Routes> <Routes>
<Route element={<ValidateRoute />}> <Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} /> <Route exact path="/" element={<Login />} />
{isEnableSubscription && (
<Route path="/signup" element={<LazyPage Page={Signup} />} /> <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>
<Route element={<Validate />}> <Route element={<Validate />}>
<Route <Route
@@ -139,11 +155,6 @@ function App() {
path="/managesign" path="/managesign"
element={<LazyPage Page={ManageSign} />} element={<LazyPage Page={ManageSign} />}
/> />
<Route
path="/generatetoken"
element={<LazyPage Page={GenerateToken} />}
/>
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
<Route <Route
path="/template/:templateId" path="/template/:templateId"
element={<TemplatePlaceholder />} element={<TemplatePlaceholder />}
@@ -164,11 +175,30 @@ function App() {
path="/pdfRequestFiles/:docId" path="/pdfRequestFiles/:docId"
element={<PdfRequestFiles />} element={<PdfRequestFiles />}
/> />
{/* recipient signature route with no rowlevel data using docId from url */} {/* recipient signature route with no rowlevel data using docId from url */}
<Route <Route
path="/recipientSignPdf/:docId/:contactBookId" path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />} 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>
<Route path="/sso" element={<SSOVerify />} /> <Route path="/sso" element={<SSOVerify />} />
<Route path="*" element={<PageNotFound />} /> <Route path="*" element={<PageNotFound />} />
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

-27
View File
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Parse from "parse"; import Parse from "parse";
import axios from "axios";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
const AddSigner = (props) => { const AddSigner = (props) => {
@@ -10,8 +9,6 @@ const AddSigner = (props) => {
const [addYourself, setAddYourself] = useState(false); const [addYourself, setAddYourself] = useState(false);
const [isLoader, setIsLoader] = useState(false); const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false); const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => { useEffect(() => {
checkUserExist(); checkUserExist();
@@ -85,18 +82,6 @@ const AddSigner = (props) => {
} }
const user = await _user.save(); const user = await _user.save();
if (user) { 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(); const currentUser = Parse.User.current();
contactQuery.set( contactQuery.set(
"CreatedBy", "CreatedBy",
@@ -140,18 +125,6 @@ const AddSigner = (props) => {
if (err.code === 202) { if (err.code === 202) {
const params = { email: email }; const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params); 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(); const currentUser = Parse.User.current();
contactQuery.set( contactQuery.set(
"CreatedBy", "CreatedBy",
+194
View File
@@ -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");
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;
+232 -104
View File
@@ -1,24 +1,63 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import Parse from "parse"; import Parse from "parse";
import axios from "axios";
import Title from "./Title"; import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader"; 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 AddUser = (props) => {
const [name, setName] = useState(""); const [formdata, setFormdata] = useState({
const [phone, setPhone] = useState(""); name: "",
const [email, setEmail] = useState(""); phone: "",
email: "",
team: "",
password: "",
role: ""
});
const [isLoader, setIsLoader] = useState(false); const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false); const [teamList, setTeamList] = useState([]);
const parseBaseUrl = localStorage.getItem("baseUrl"); const role = ["OrgAdmin", "Editor", "User"];
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => {
getTeamList();
}, []);
const getTeamList = async () => {
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const team = new Parse.Query("contracts_Teams");
team.equalTo("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: extUser.OrganizationId.objectId
});
team.notEqualTo("IsActive", false);
const teamRes = await team.find();
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 () => { const checkUserExist = async () => {
try { try {
const res = await Parse.Cloud.run("getUserDetails", { const res = await Parse.Cloud.run("getUserDetails", {
email: email, email: formdata.email
userId: Parse.User.current().id
}); });
if (res) { if (res) {
return true; return true;
@@ -33,24 +72,46 @@ const AddUser = (props) => {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsLoader(true); setIsLoader(true);
const res = await checkUserExist(); const res = await checkUserExist();
if (res) { if (res) {
setIsUserExist(true); props.setIsAlert({ type: "danger", msg: "User already exist." });
setIsLoader(false); setIsLoader(false);
setTimeout(() => { setTimeout(() => {
setIsUserExist(false); props.setIsAlert({ type: "success", msg: "" });
}, 1000); }, 1000);
} else { } else {
try { try {
const contactQuery = new Parse.Object("contracts_Users"); const extUser = new Parse.Object("contracts_Users");
contactQuery.set("Name", name); extUser.set("Name", formdata.name);
contactQuery.set("Phone", phone); if (formdata.phone) {
contactQuery.set("Email", email); extUser.set("Phone", formdata.phone);
contactQuery.set("UserRole", "contracts_User"); }
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")) { if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", { extUser.set("TenantId", {
__type: "Pointer", __type: "Pointer",
className: "partners_Tenant", className: "partners_Tenant",
objectId: localStorage.getItem("TenantId") objectId: localStorage.getItem("TenantId")
@@ -60,89 +121,69 @@ const AddUser = (props) => {
try { try {
const _users = Parse.Object.extend("User"); const _users = Parse.Object.extend("User");
const _user = new _users(); const _user = new _users();
_user.set("name", name); _user.set("name", formdata.name);
_user.set("username", email); _user.set("username", formdata.email);
_user.set("email", email); _user.set("email", formdata.email);
_user.set("password", email); _user.set("password", formdata.password);
if (phone) { if (formdata.phone) {
_user.set("phone", phone); _user.set("phone", formdata.phone);
} }
const user = await _user.save(); const user = await _user.save();
if (user) { 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(); const currentUser = Parse.User.current();
contactQuery.set( extUser.set(
"CreatedBy", "CreatedBy",
Parse.User.createWithoutData(currentUser.id) Parse.User.createWithoutData(currentUser.id)
); );
contactQuery.set("UserId", user); extUser.set("UserId", user);
const acl = new Parse.ACL(); const acl = new Parse.ACL();
acl.setPublicReadAccess(true); acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true); acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true); acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(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)); const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) { if (props.closePopup) {
props.closePopup(); props.closePopup();
} }
if (props.handleUserData) { 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); props.handleUserData(parseData);
} }
setIsLoader(false); setIsLoader(false);
setFormdata({
setName(""); name: "",
setPhone(""); email: "",
setEmail(""); phone: "",
team: "",
role: ""
});
} }
} catch (err) { } catch (err) {
console.log("err ", err); console.log("err ", err);
if (err.code === 202) { if (err.code === 202) {
const params = { email: email }; const params = { email: formdata.email };
const userRes = await Parse.Cloud.run("getUserId", params); 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(); const currentUser = Parse.User.current();
contactQuery.set( extUser.set(
"CreatedBy", "CreatedBy",
Parse.User.createWithoutData(currentUser.id) Parse.User.createWithoutData(currentUser.id)
); );
contactQuery.set("UserId", { extUser.set("UserId", {
__type: "Pointer", __type: "Pointer",
className: "_User", className: "_User",
objectId: userRes.id objectId: userRes.id
@@ -153,55 +194,75 @@ const AddUser = (props) => {
acl.setReadAccess(currentUser.id, true); acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(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)); const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) { if (props.closePopup) {
props.closePopup(); props.closePopup();
} }
if (props.handleUserData) { 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); props.handleUserData(parseData);
} }
setIsLoader(false); setIsLoader(false);
setName(""); setFormdata({
setPhone(""); name: "",
setEmail(""); email: "",
phone: "",
team: "",
role: ""
});
} }
} }
} catch (err) { } catch (err) {
// console.log("err", err); console.log("err", err);
setIsLoader(false); 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 // Define a function to handle the "add yourself" checkbox
const handleReset = () => { const handleReset = () => {
setName(""); setFormdata({
setPhone(""); name: "",
setEmail(""); 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 ( 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"} /> <Title title={"Add User"} />
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isLoader && ( {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 /> <Loader />
</div> </div>
)} )}
<div className="w-full mx-auto"> <div className="w-full mx-auto">
<form onSubmit={handleSubmit}> <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"> <div className="mb-3">
<label <label
htmlFor="name" htmlFor="name"
@@ -212,9 +273,9 @@ const AddUser = (props) => {
</label> </label>
<input <input
type="text" type="text"
id="name" name="name"
value={name} value={formdata.name}
onChange={(e) => setName(e.target.value)} onChange={(e) => handleChange(e)}
required required
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 text-xs"
/> />
@@ -229,44 +290,111 @@ const AddUser = (props) => {
</label> </label>
<input <input
type="email" type="email"
id="email" name="email"
value={email} value={formdata.email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => handleChange(e)}
required required
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 text-xs"
/> />
</div> </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"> <div className="mb-3">
<label <label
htmlFor="phone" htmlFor="phone"
className="block text-xs text-gray-700 font-semibold" className="block text-xs text-gray-700 font-semibold"
> >
Phone Phone
<span className="text-[red] text-[13px]"> *</span> {/* <span className="text-[red] text-[13px]"> *</span> */}
</label> </label>
<input <input
type="text" type="text"
id="phone" name="phone"
value={phone} placeholder="optional"
onChange={(e) => setPhone(e.target.value)} value={formdata.phone}
required 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" className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/> />
</div> </div>
{isEnableSubscription && (
<div className="flex items-center mt-3 gap-2 text-white"> <div className="mb-3">
<button <label
type="submit" htmlFor="phone"
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none" 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 Submit
</button> </button>
<div <div
type="button" type="button"
onClick={() => handleReset()} 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>
</div> </div>
</form> </form>
+11 -11
View File
@@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom";
import Parse from "parse"; import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize"; import { useWindowSize } from "../hook/useWindowSize";
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils"; import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const"; import { isEnableSubscription, isStaging } from "../constant/const";
const Header = ({ showSidebar }) => { const Header = ({ showSidebar }) => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -31,8 +31,8 @@ const Header = ({ showSidebar }) => {
const getIsSubscribe = await checkIsSubscribed(); const getIsSubscribe = await checkIsSubscribed();
if (getIsSubscribe) { if (getIsSubscribe) {
const applogo = await getAppLogo(); const applogo = await getAppLogo();
if (applogo) { if (applogo?.logo) {
setAppLogo(applogo); setAppLogo(applogo?.logo);
} else { } else {
setAppLogo(localStorage.getItem("appLogo") || ""); setAppLogo(localStorage.getItem("appLogo") || "");
} }
@@ -53,22 +53,16 @@ const Header = ({ showSidebar }) => {
} }
let appdata = localStorage.getItem("userSettings"); let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo"); let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
let defaultmenuid = localStorage.getItem("defaultmenuid"); let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding"); let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
localStorage.clear(); localStorage.clear();
localStorage.setItem("appLogo", applogo); localStorage.setItem("appLogo", applogo);
localStorage.setItem("appName", appName);
localStorage.setItem("_appName", _appName);
localStorage.setItem("defaultmenuid", defaultmenuid); localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding); localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata); localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
@@ -91,7 +85,13 @@ const Header = ({ showSidebar }) => {
document.removeEventListener("click", closeMenuOnOutsideClick); document.removeEventListener("click", closeMenuOnOutsideClick);
}; };
}, [isOpen]); }, [isOpen]);
const handleConsoleRedirect = () => {
if (isStaging) {
window.open(" https://staging-console.opensignlabs.com/");
} else {
window.open("https://console.opensignlabs.com/");
}
};
return ( return (
<div className="op-navbar bg-base-100 shadow"> <div className="op-navbar bg-base-100 shadow">
<div className="flex-none"> <div className="flex-none">
@@ -194,7 +194,7 @@ const Header = ({ showSidebar }) => {
</li> </li>
<li <li
onClick={() => { onClick={() => {
window.open("https://console.opensignlabs.com/"); handleConsoleRedirect();
}} }}
> >
<span> <span>
@@ -51,14 +51,8 @@ const GoogleSignInBtn = ({
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
let applogo = localStorage.getItem("appLogo"); 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 defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding"); 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"); let userSettings = localStorage.getItem("userSettings");
localStorage.clear(); localStorage.clear();
@@ -66,14 +60,8 @@ const GoogleSignInBtn = ({
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
localStorage.setItem("appLogo", applogo); localStorage.setItem("appLogo", applogo);
localStorage.setItem("domain", domain);
localStorage.setItem("appversion", appversion);
localStorage.setItem("appTitle", appTitle);
localStorage.setItem("defaultmenuid", defaultmenuid); localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding); localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("_appName", _appName);
localStorage.setItem("_app_objectId", _app_objectId);
localStorage.setItem("appName", appName);
localStorage.setItem("userSettings", userSettings); localStorage.setItem("userSettings", userSettings);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
@@ -186,22 +174,16 @@ const GoogleSignInBtn = ({
let appdata = localStorage.getItem("userSettings"); let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo"); let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
let defaultmenuid = localStorage.getItem("defaultmenuid"); let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding"); let PageLanding = localStorage.getItem("PageLanding");
let domain = localStorage.getItem("domain");
let _appName = localStorage.getItem("_appName");
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
localStorage.clear(); localStorage.clear();
localStorage.setItem("appLogo", applogo); localStorage.setItem("appLogo", applogo);
localStorage.setItem("appName", appName);
localStorage.setItem("_appName", _appName);
localStorage.setItem("defaultmenuid", defaultmenuid); localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding); localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("domain", domain);
localStorage.setItem("userSettings", appdata); localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
@@ -31,7 +31,7 @@ const GetDashboard = (props) => {
<div <div
className={`${ className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]" 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} data-tut={col.widget.data.tourSection}
> >
<Suspense <Suspense
@@ -70,9 +70,9 @@ function DriveBody(props) {
props.sortingData(null, null, updatedData); props.sortingData(null, null, updatedData);
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Document/${docId}`, )}classes/contracts_Document/${docId}`,
updateName, updateName,
{ {
headers: { headers: {
@@ -104,7 +104,7 @@ function DriveBody(props) {
const signedUrl = data.SignedUrl; const signedUrl = data.SignedUrl;
//checking if document has completed and request signature flow //checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) { if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
} }
//checking if document has completed and signyour-self flow //checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) { else if (!signerExist && !isPlaceholder) {
@@ -112,7 +112,7 @@ function DriveBody(props) {
} }
//checking if document has declined by someone //checking if document has declined by someone
else if (isDecline) { else if (isDecline) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
//checking draft type document //checking draft type document
} else if ( } else if (
signerExist?.length > 0 && signerExist?.length > 0 &&
@@ -122,8 +122,8 @@ function DriveBody(props) {
navigate(`/placeHolderSign/${data.objectId}`); navigate(`/placeHolderSign/${data.objectId}`);
} }
//Inprogress document //Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) { else if (isPlaceholder?.length > 0 && signedUrl) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
} //placeholder draft document } //placeholder draft document
else if ( else if (
(signerExist?.length > 0 && (signerExist?.length > 0 &&
@@ -170,9 +170,7 @@ function DriveBody(props) {
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem("baseUrl")}classes/contracts_Document/${docId}`,
"_appName"
)}_Document/${docId}`,
data, data,
{ {
headers: { headers: {
@@ -219,7 +217,7 @@ function DriveBody(props) {
updateData = { updateData = {
Folder: { Folder: {
__type: "Pointer", __type: "Pointer",
className: `${localStorage.getItem("_appName")}_Document`, className: "contracts_Document",
objectId: moveFolderId objectId: moveFolderId
} }
}; };
@@ -231,9 +229,9 @@ function DriveBody(props) {
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Document/${updateDocId}`, )}classes/contracts_Document/${updateDocId}`,
updateData, updateData,
{ {
headers: { headers: {
@@ -1,23 +1,19 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import LoaderWithMsg from "../../primitives/LoaderWithMsg"; import LoaderWithMsg from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils"; import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError"; import HandleError from "../../primitives/HandleError";
import { useLocation, useNavigate } from "react-router-dom";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
function DraftDocument() { function DraftDocument() {
const navigate = useNavigate(); const navigate = useNavigate();
const query = useQuery();
const docId = query.get("docId");
const [isLoading, setIsLoading] = useState({ const [isLoading, setIsLoading] = useState({
isLoader: true, isLoader: true,
message: "This might take some time" 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(() => { useEffect(() => {
getDocumentDetails(); getDocumentDetails();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -53,7 +49,7 @@ function DraftDocument() {
const signedUrl = data.SignedUrl; const signedUrl = data.SignedUrl;
//checking if document has completed and request signature flow //checking if document has completed and request signature flow
if (data?.IsCompleted && signerExist?.length > 0) { if (data?.IsCompleted && signerExist?.length > 0) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
} }
//checking if document has completed and signyour-self flow //checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) { else if (!signerExist && !isPlaceholder) {
@@ -61,7 +57,7 @@ function DraftDocument() {
} }
//checking if document has declined by someone //checking if document has declined by someone
else if (isDecline) { else if (isDecline) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
//checking draft type document //checking draft type document
} else if ( } else if (
signerExist?.length > 0 && signerExist?.length > 0 &&
@@ -71,8 +67,8 @@ function DraftDocument() {
navigate(`/placeHolderSign/${data.objectId}`); navigate(`/placeHolderSign/${data.objectId}`);
} }
//Inprogress document //Inprogress document
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) { else if (isPlaceholder?.length > 0 && signedUrl) {
navigate(`/pdfRequestFiles/${data.objectId}`); navigate(`/recipientSignPdf/${data.objectId}`);
} //placeholder draft document } //placeholder draft document
else if ( else if (
(signerExist?.length > 0 && (signerExist?.length > 0 &&
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
import { isEnableSubscription } from "../../constant/const"; import { isEnableSubscription } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils"; import { radioButtonWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade"; import Upgrade from "../../primitives/Upgrade";
function DropdownWidgetOption(props) { function DropdownWidgetOption(props) {
const [dropdownOptionList, setDropdownOptionList] = useState([ const [dropdownOptionList, setDropdownOptionList] = useState([
@@ -214,6 +213,7 @@ function DropdownWidgetOption(props) {
onClick={handleAddInput} onClick={handleAddInput}
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer" className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
></i> ></i>
{isEnableSubscription && (
<div> <div>
{props.type === "checkbox" && !props.isSignYourself && ( {props.type === "checkbox" && !props.isSignYourself && (
<> <>
@@ -224,19 +224,20 @@ function DropdownWidgetOption(props) {
> >
Minimun check Minimun check
</label> </label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />} {!props.isSubscribe && isEnableSubscription && (
<Upgrade />
)}
<input <input
required required
defaultValue={0} defaultValue={0}
value={minCount} value={minCount}
disabled={props.isSubscribe ? false : true}
onChange={(e) => { onChange={(e) => {
const count = handleSetMinMax(e); const count = handleSetMinMax(e);
setMinCount(count); setMinCount(count);
}} }}
className={`${ className={`${
props.isSubscribe || !isEnableSubscription props.isSubscribe ? "" : "pointer-events-none"
? ""
: "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`} } op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/> />
<label <label
@@ -250,19 +251,19 @@ function DropdownWidgetOption(props) {
required required
defaultValue={0} defaultValue={0}
value={maxCount} value={maxCount}
disabled={props.isSubscribe ? false : true}
onChange={(e) => { onChange={(e) => {
const count = handleSetMinMax(e); const count = handleSetMinMax(e);
setMaxCount(count); setMaxCount(count);
}} }}
className={`${ className={`${
props.isSubscribe || !isEnableSubscription props.isSubscribe ? "" : "pointer-events-none"
? ""
: "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`} } op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/> />
</> </>
)} )}
</div> </div>
)}
</div> </div>
{["dropdown", radioButtonWidget].includes(props.type) && ( {["dropdown", radioButtonWidget].includes(props.type) && (
<> <>
@@ -344,15 +345,6 @@ function DropdownWidgetOption(props) {
</div> </div>
)} )}
</div> </div>
{props.type === "checkbox" &&
!props.isSignYourself &&
!isEnableSubscription && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<div <div
className={`${ className={`${
props.type === "checkbox" && !props.isSignYourself props.type === "checkbox" && !props.isSignYourself
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { checkIsSubscribed, getFileName } from "../../constant/Utils"; import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade"; import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const"; import { isEnableSubscription } from "../../constant/const";
// import SelectFolder from "../../premitives/SelectFolder"; // import SelectFolder from "../../premitives/SelectFolder";
@@ -134,19 +133,11 @@ const EditTemplate = ({ template, onSuccess }) => {
<div className="text-[12px]">No</div> <div className="text-[12px]">No</div>
</div> </div>
</div> </div>
{isEnableSubscription && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span <span
className={ className={
isSubscribe || !isEnableSubscription isSubscribe ? "font-semibold" : "font-semibold text-gray-300"
? "font-semibold"
: "font-semibold text-gray-300"
} }
> >
Auto reminder{" "} Auto reminder{" "}
@@ -154,7 +145,7 @@ const EditTemplate = ({ template, onSuccess }) => {
</span> </span>
<label <label
className={`${ className={`${
isSubscribe || !isEnableSubscription isSubscribe
? "cursor-pointer " ? "cursor-pointer "
: "pointer-events-none opacity-50" : "pointer-events-none opacity-50"
} relative block items-center mb-0`} } relative block items-center mb-0`}
@@ -167,6 +158,7 @@ const EditTemplate = ({ template, onSuccess }) => {
/> />
</label> </label>
</div> </div>
)}
{isSubscribe && formData?.AutomaticReminders === true && ( {isSubscribe && formData?.AutomaticReminders === true && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block"> <label className="block">
@@ -2,7 +2,7 @@ import React, { useState } from "react";
import { saveAs } from "file-saver"; import { saveAs } from "file-saver";
import axios from "axios"; import axios from "axios";
import { getBase64FromUrl } from "../../constant/Utils"; import { getBase64FromUrl } from "../../constant/Utils";
import { themeColor } from "../../constant/const"; import { themeColor, emailRegex } from "../../constant/const";
import printModule from "print-js"; import printModule from "print-js";
import Loader from "../../primitives/Loader"; import Loader from "../../primitives/Loader";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
@@ -19,8 +19,9 @@ function EmailComponent({
activeMailAdapter activeMailAdapter
}) { }) {
const [emailList, setEmailList] = useState([]); const [emailList, setEmailList] = useState([]);
const [emailValue, setEmailValue] = useState(); const [emailValue, setEmailValue] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [emailErr, setEmailErr] = useState(false);
//function for send email //function for send email
const sendEmail = async () => { const sendEmail = async () => {
setIsLoading(true); setIsLoading(true);
@@ -111,17 +112,30 @@ function EmailComponent({
//function for get email value //function for get email value
const handleEmailValue = (e) => { const handleEmailValue = (e) => {
const value = e.target.value; const value = e.target.value;
setEmailErr(false);
setEmailValue(value); setEmailValue(value);
}; };
//function for save email in array after press enter //function for save email in array after press enter
const handleEnterPress = (e) => { const handleEnterPress = (e) => {
const pattern = emailRegex;
const validate = emailValue?.match(pattern);
if (e.key === "Enter" && emailValue) { if (e.key === "Enter" && emailValue) {
setEmailList((prev) => [...prev, emailValue]); if (validate) {
const emailLowerCase = emailValue?.toLowerCase();
setEmailList((prev) => [...prev, emailLowerCase]);
setEmailValue(""); setEmailValue("");
} else {
setEmailErr(true);
}
} else if (e === "add" && emailValue) { } else if (e === "add" && emailValue) {
setEmailList((prev) => [...prev, emailValue]); if (validate) {
const emailLowerCase = emailValue?.toLowerCase();
setEmailList((prev) => [...prev, emailLowerCase]);
setEmailValue(""); setEmailValue("");
} else {
setEmailErr(true);
}
} }
}; };
@@ -179,7 +193,6 @@ function EmailComponent({
Successfully signed! Successfully signed!
</span> </span>
<div className="flex flex-row"> <div className="flex flex-row">
<div></div>
{!isAndroid && ( {!isAndroid && (
<button <button
onClick={handleToPrint} onClick={handleToPrint}
@@ -203,7 +216,6 @@ function EmailComponent({
Recipients added here will get a copy of the signed document. Recipients added here will get a copy of the signed document.
</p> </p>
{emailList.length > 0 ? ( {emailList.length > 0 ? (
<>
<div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]"> <div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]">
<div className="flex flex-row flex-wrap"> <div className="flex flex-row flex-wrap">
{emailList.map((data, ind) => { {emailList.map((data, ind) => {
@@ -227,48 +239,40 @@ function EmailComponent({
</div> </div>
{emailList.length <= 9 && ( {emailList.length <= 9 && (
<input <input
type="text" type="email"
value={emailValue} value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-transparent outline-none" className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-transparent outline-none"
onChange={handleEmailValue} onChange={handleEmailValue}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
onBlur={() => { onBlur={() => emailValue && handleEnterPress("add")}
if (emailValue) {
handleEnterPress("add");
}
}}
required required
/> />
)} )}
</div> </div>
</>
) : ( ) : (
<div> <div>
<input <input
type="text" type="email"
value={emailValue} value={emailValue}
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary" className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary"
onChange={handleEmailValue} onChange={handleEmailValue}
onKeyDown={handleEnterPress} onKeyDown={handleEnterPress}
placeholder="Add the email addresses" placeholder="Add an email address and hit enter"
onBlur={() => { onBlur={() => emailValue && handleEnterPress("add")}
if (emailValue) {
handleEnterPress("add");
}
}}
required required
/> />
</div> </div>
)} )}
{emailErr && (
<p className="text-xs text-[red] ml-1.5 mt-0.5">
please provide correct email address
</p>
)}
<button <button
className={`${ className={`${
emailValue ? "cursor-pointer" : "cursor-default" emailValue ? "cursor-pointer" : "cursor-default"
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`} } op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
onClick={() => { onClick={() => emailValue && handleEnterPress("add")}
if (emailValue) {
handleEnterPress("add");
}
}}
> >
<i className="fa-light fa-plus" aria-hidden="true"></i> <i className="fa-light fa-plus" aria-hidden="true"></i>
</button> </button>
+40 -56
View File
@@ -7,7 +7,6 @@ import {
} from "../../constant/Utils"; } from "../../constant/Utils";
import "../../styles/signature.css"; import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader"; import Loader from "../../primitives/Loader";
@@ -33,21 +32,18 @@ function Header({
isSignYourself, isSignYourself,
setIsEmail, setIsEmail,
completeBtnTitle, completeBtnTitle,
setIsEditTemplate setIsEditTemplate,
isPublicTemplate
}) { }) {
const filterPrefill = const filterPrefill =
signerPos && signerPos?.filter((data) => data.Role !== "prefill"); signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isDownloading, setIsDownloading] = useState(""); const [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner"); const isGuestSigner = localStorage.getItem("isGuestSigner");
//function for show decline alert //function for show decline alert
const handleDeclinePdfAlert = async () => { const handleDeclinePdfAlert = async () => {
const currentDecline = { const currentDecline = { currnt: "Sure", isDeclined: true };
currnt: "Sure",
isDeclined: true
};
setIsDecline(currentDecline); setIsDecline(currentDecline);
}; };
@@ -64,7 +60,7 @@ function Header({
}} }}
> >
<div className="flex justify-between items-center py-[5px] px-[10px] "> <div className="flex justify-between items-center py-[5px] px-[10px] ">
<div onClick={() => navigate(-1)}> <div onClick={() => window.history.go(-2)}>
<i <i
className="fa-light fa-arrow-left text-base-content" className="fa-light fa-arrow-left text-base-content"
aria-hidden="true" aria-hidden="true"
@@ -209,7 +205,7 @@ function Header({
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Portal> <DropdownMenu.Portal>
<DropdownMenu.Content <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} sideOffset={5}
> >
<DropdownMenu.Item <DropdownMenu.Item
@@ -275,7 +271,7 @@ function Header({
</button> </button>
)} )}
<button <button
onClick={() => navigate(-1)} onClick={() => window.history.go(-2)}
type="button" type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]" className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
> >
@@ -298,6 +294,17 @@ function Header({
) : isPdfRequestFiles ? ( ) : isPdfRequestFiles ? (
alreadySign ? ( alreadySign ? (
<div className="flex flex-row"> <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 && ( {isCompleted && (
<button <button
type="button" type="button"
@@ -313,19 +320,6 @@ function Header({
<span className="hidden lg:block">Certificate</span> <span className="hidden lg:block">Certificate</span>
</button> </button>
)} )}
<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>
<button <button
type="button" type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow" className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
@@ -343,7 +337,7 @@ function Header({
) : ( ) : (
<div className="flex" data-tut="reactourFifth"> <div className="flex" data-tut="reactourFifth">
<button <button
onClick={() => navigate(-1)} onClick={() => window.history.go(-2)}
type="button" type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]" className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
> >
@@ -364,40 +358,16 @@ function Header({
> >
Finish Finish
</button> </button>
<div className="op-dropdown op-dropdown-end"> <button
<div type="button"
tabIndex={0} className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
role="button"
className="op-btn op-btn-info op-btn-sm shadow"
>
<i
className="fa-light 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={() => onClick={() =>
handleDownloadPdf( handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
pdfDetails,
pdfUrl,
setIsDownloading
)
} }
> >
<span className="font-semibold text-[12px]"> <i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
<i <span className="hidden lg:block">Download</span>
className="fa-light fa-arrow-down" </button>
aria-hidden="true"
></i>{" "}
Download
</span>
</li>
</ul>
</div>
</> </>
)} )}
</div> </div>
@@ -443,10 +413,24 @@ function Header({
<span className="hidden lg:block ml-1">Mail</span> <span className="hidden lg:block ml-1">Mail</span>
</button> </button>
</div> </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"> <div className="flex">
<button <button
onClick={() => navigate(-1)} onClick={() => window.history.go(-2)}
type="button" type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]" className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
> >
+89 -15
View File
@@ -234,6 +234,9 @@ function Placeholder(props) {
setDraggingEnabled(false); setDraggingEnabled(false);
} }
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) { if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
if (props?.ispublicTemplate) {
props.handleUserDetails();
} else {
if (props.pos.type) { if (props.pos.type) {
if ( if (
props.pos.type === "signature" || props.pos.type === "signature" ||
@@ -254,6 +257,7 @@ function Placeholder(props) {
props.setSignKey(props.pos.key); props.setSignKey(props.pos.key);
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false); props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
} }
}
} else if ( } else if (
props.isPlaceholder && props.isPlaceholder &&
!props.isDragging && !props.isDragging &&
@@ -262,6 +266,10 @@ function Placeholder(props) {
if (props.pos.key === props.selectWidgetId) { if (props.pos.key === props.selectWidgetId) {
props.handleLinkUser(props.data.Id); props.handleLinkUser(props.data.Id);
props.setUniqueId(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) { } else if (!props.pos.type) {
if ( if (
@@ -291,6 +299,12 @@ function Placeholder(props) {
} }
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) { if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
handleWidgetIdandPopup(); handleWidgetIdandPopup();
} else if (
props?.isNeedSign &&
props?.uniqueId &&
props.data?.Id === props?.uniqueId
) {
handleWidgetIdandPopup();
} else if (props.isPlaceholder || props.isSignYourself) { } else if (props.isPlaceholder || props.isSignYourself) {
handleWidgetIdandPopup(); handleWidgetIdandPopup();
} }
@@ -303,7 +317,9 @@ function Placeholder(props) {
props?.setShowDropdown(true); props?.setShowDropdown(true);
} else if (props.pos.type === "checkbox") { } else if (props.pos.type === "checkbox") {
props?.setIsCheckbox(true); props?.setIsCheckbox(true);
} else if ( }
//condition to handle setting icon for signyour-self flow for all type text widgets
else if (
[ [
textInputWidget, textInputWidget,
textWidget, textWidget,
@@ -311,19 +327,33 @@ function Placeholder(props) {
"company", "company",
"job title", "job title",
"email" "email"
].includes(props.pos.type) ].includes(props.pos.type) &&
props.isSignYourself
) { ) {
props.handleTextSettingModal(true); props.handleTextSettingModal(true);
} else { } else {
props?.handleNameModal && props?.handleNameModal(true); props?.handleNameModal && props?.handleNameModal(true);
} }
//condition for only placeholder and template flow
if (props.data && props?.pos?.type !== textWidget) { if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId); props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id); props.setUniqueId(props?.data?.Id);
} else if (props.data && props.pos.type === textWidget) { 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.setTempSignerId(props.uniqueId);
props.setUniqueId(props?.data?.Id); 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.setSignKey(props.pos.key);
props.setWidgetType(props.pos.type); props.setWidgetType(props.pos.type);
@@ -340,10 +370,19 @@ function Placeholder(props) {
if (props.data && props?.pos?.type !== textWidget) { if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId); props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(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.data && props.pos.type === textWidget) { } else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId); props.setTempSignerId(props.uniqueId);
props.setSignerObjId(props?.data?.signerObjId); props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id); 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 //checking widget's type and open widget copy modal for required widgets
@@ -400,6 +439,9 @@ function Placeholder(props) {
<> <>
{(props.isPlaceholder || props.isSignYourself) && ( {(props.isPlaceholder || props.isSignYourself) && (
<> <>
{/* condition to add setting icon for signyour-self flow for particular text widgets
and also it have diffrent position
*/}
{[ {[
"checkbox", "checkbox",
textInputWidget, textInputWidget,
@@ -426,6 +468,7 @@ function Placeholder(props) {
}} }}
></i> ></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 && props.pos.isStamp) ||
(props?.pos?.type && (props?.pos?.type &&
!["date", "signature"].includes(props.pos.type) && !["date", "signature"].includes(props.pos.type) &&
@@ -448,7 +491,7 @@ function Placeholder(props) {
></i> ></i>
) )
)} )}
{/* condition for usericon for all widgets except text widgets and signyour-self flow */}
{props.pos.type !== textWidget && !props.isSignYourself && ( {props.pos.type !== textWidget && !props.isSignYourself && (
<i <i
data-tut="assignSigner" data-tut="assignSigner"
@@ -457,17 +500,26 @@ function Placeholder(props) {
e.stopPropagation(); e.stopPropagation();
props.handleLinkUser(props.data.Id); props.handleLinkUser(props.data.Id);
props.setUniqueId(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) => { onTouchEnd={(e) => {
e.stopPropagation(); e.stopPropagation();
props.handleLinkUser(props.data.Id); props.handleLinkUser(props.data.Id);
props.setUniqueId(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" }} style={{ color: "#188ae2", right: "32px", top: "-18px" }}
></i> ></i>
)} )}
</> </>
)} )}
{/* setting icon only for date widgets */}
{props.pos.type === "date" && selectDate && ( {props.pos.type === "date" && selectDate && (
<div <div
id="menu-container" id="menu-container"
@@ -483,6 +535,10 @@ function Placeholder(props) {
if (props.data) { if (props.data) {
props.setSignKey(props.pos.key); props.setSignKey(props.pos.key);
props.setUniqueId(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) => { onTouchEnd={(e) => {
@@ -491,6 +547,10 @@ function Placeholder(props) {
if (props.data) { if (props.data) {
props.setSignKey(props.pos.key); props.setSignKey(props.pos.key);
props.setUniqueId(props.data.Id); props.setUniqueId(props.data.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
);
props.setIsSelectId(checkIndex || 0);
} }
}} }}
> >
@@ -532,29 +592,37 @@ function Placeholder(props) {
</div> </div>
</div> </div>
)} )}
{/* copy icon for all widgets */}
<i <i
className="fa-light fa-copy icon" className="fa-light fa-copy icon"
onClick={(e) => handleCopyPlaceholder(e)} onClick={(e) => handleCopyPlaceholder(e)}
onTouchEnd={(e) => handleCopyPlaceholder(e)} onTouchEnd={(e) => handleCopyPlaceholder(e)}
style={{ color: "#188ae2", right: "12px", top: "-18px" }} style={{ color: "#188ae2", right: "12px", top: "-18px" }}
></i> ></i>
{/* delete icon for all widgets */}
<i <i
className="fa-light fa-circle-xmark icon" className="fa-light fa-circle-xmark icon"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
//condition for template and placeholder flow
if (props.data) { if (props.data) {
props.handleDeleteSign(props.pos.key, props.data.Id); props.handleDeleteSign(props.pos.key, props.data.Id);
} else { }
//condition for signyour-self flow
else {
props.handleDeleteSign(props.pos.key); props.handleDeleteSign(props.pos.key);
props.setIsStamp(false); props.setIsStamp(false);
} }
}} }}
//for mobile and tablet touch event
onTouchEnd={(e) => { onTouchEnd={(e) => {
e.stopPropagation(); e.stopPropagation();
//condition for template and placeholder flow
if (props.data) { if (props.data) {
props.handleDeleteSign(props.pos.key, props.data?.Id); props.handleDeleteSign(props.pos.key, props.data?.Id);
} else { }
//condition for signyour-self flow
else {
props.handleDeleteSign(props.pos.key); props.handleDeleteSign(props.pos.key);
props.setIsStamp(false); props.setIsStamp(false);
} }
@@ -621,13 +689,25 @@ function Placeholder(props) {
} }
}; };
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 ( return (
<Rnd <Rnd
id={props.pos.key} id={props.pos.key}
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""} data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
key={props.pos.key} key={props.pos.key}
lockAspectRatio={ lockAspectRatio={
// props.pos.type !== textWidget
![ ![
textWidget, textWidget,
"email", "email",
@@ -671,12 +751,7 @@ function Placeholder(props) {
props.pos.type !== "company" && props.pos.type !== "company" &&
props.pos.type !== "job title" && props.pos.type !== "job title" &&
"center", "center",
cursor: cursor: getCursor(),
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId
? "pointer"
: "not-allowed"
: "all-scroll",
zIndex: zIndex:
props.pos.type === "date" props.pos.type === "date"
? props.pos.key === props.selectWidgetId ? props.pos.key === props.selectWidgetId
@@ -775,7 +850,6 @@ function Placeholder(props) {
)} )}
{isTabAndMobile ? ( {isTabAndMobile ? (
<div <div
// className="sm:inline-block md:inline-block lg:hidden "
className="flex items-stretch" className="flex items-stretch"
style={{ style={{
left: xPos(props.pos, props.isSignYourself), left: xPos(props.pos, props.isSignYourself),
@@ -51,8 +51,8 @@ function PlaceholderBorder(props) {
style={{ style={{
borderColor: themeColor, borderColor: themeColor,
borderStyle: "dashed", borderStyle: "dashed",
minWidth: handleMinWidth(), minWidth: handleMinWidth() || 0,
minHeight: handleMinHeight(), minHeight: handleMinHeight() || 0,
borderWidth: "0.2px", borderWidth: "0.2px",
overflow: "hidden" overflow: "hidden"
}} }}
@@ -189,9 +189,7 @@ function PlaceholderCopy(props) {
<ModalUi <ModalUi
isOpen={props.isPageCopy} isOpen={props.isPageCopy}
title={"Copy to all pages"} title={"Copy to all pages"}
handleClose={() => { handleClose={() => handleUniqueId()}
props.setIsPageCopy(false);
}}
> >
<div className="h-full p-[20px] text-base-content"> <div className="h-full p-[20px] text-base-content">
{copyType.map((data, key) => { {copyType.map((data, key) => {
@@ -13,10 +13,11 @@ import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css"; import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css"; import "../../styles/signature.css";
import RegexParser from "regex-parser"; import RegexParser from "regex-parser";
import { emailRegex } from "../../constant/const";
const textWidgetCls = 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 bg-base-100 whitespace-pre-wrap"; "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";
const selectWidgetCls = const selectWidgetCls =
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none bg-base-100 text-base-content"; "w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
function PlaceholderType(props) { function PlaceholderType(props) {
const type = props?.pos?.type; const type = props?.pos?.type;
const [selectOption, setSelectOption] = useState(""); const [selectOption, setSelectOption] = useState("");
@@ -61,7 +62,7 @@ function PlaceholderType(props) {
if (validateType && validateType !== "text") { if (validateType && validateType !== "text") {
switch (validateType) { switch (validateType) {
case "email": case "email":
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; regexValidation = emailRegex;
validateExpression(regexValidation); validateExpression(regexValidation);
break; break;
case "number": case "number":
@@ -423,8 +424,10 @@ function PlaceholderType(props) {
) : ( ) : (
<div <div
style={{ style={{
color: "black", fontSize: props.pos.options?.fontSize
fontSize: "12px" ? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}} }}
> >
<span>{type}</span> <span>{type}</span>
@@ -522,8 +525,10 @@ function PlaceholderType(props) {
) : ( ) : (
<div <div
style={{ style={{
color: "black", fontSize: props.pos.options?.fontSize
fontSize: "12px" ? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}} }}
> >
<span>{type}</span> <span>{type}</span>
@@ -562,8 +567,10 @@ function PlaceholderType(props) {
) : ( ) : (
<div <div
style={{ style={{
color: "black", fontSize: props.pos.options?.fontSize
fontSize: "12px" ? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}} }}
> >
<span>{type}</span> <span>{type}</span>
@@ -602,15 +609,18 @@ function PlaceholderType(props) {
) : ( ) : (
<div <div
style={{ style={{
color: "black", fontSize: props.pos.options?.fontSize
fontSize: "12px" ? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}} }}
> >
<span>{type}</span> <span>{type}</span>
</div> </div>
); );
case "date": case "date":
return ( return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<div> <div>
<DatePicker <DatePicker
renderCustomHeader={({ date, changeYear, changeMonth }) => ( renderCustomHeader={({ date, changeYear, changeMonth }) => (
@@ -667,6 +677,16 @@ function PlaceholderType(props) {
} }
/> />
</div> </div>
) : (
<div className="text-[12px] text-black uppercase items-center">
<span>
{props.selectDate
? props.selectDate?.format
: props.pos?.options?.validation?.format
? props.pos?.options?.validation?.format
: "MM/dd/yyyy"}
</span>
</div>
); );
case "image": case "image":
return props.pos.SignUrl ? ( return props.pos.SignUrl ? (
@@ -720,8 +740,10 @@ function PlaceholderType(props) {
) : ( ) : (
<div <div
style={{ style={{
color: "black", fontSize: props.pos.options?.fontSize
fontSize: "12px" ? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
}} }}
> >
<span>{type}</span> <span>{type}</span>
@@ -61,15 +61,27 @@ const RecipientList = (props) => {
); );
//splice method is used to replace or add new value in array at specific index //splice method is used to replace or add new value in array at specific index
remainingItems.splice(index, 0, ...draggedItem); remainingItems.splice(index, 0, ...draggedItem);
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list //set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
props.setSignersData(remainingItems); props?.setSignersData(remainingItems);
props.setSignerObjId(remainingItems[index]?.objectId || ""); props?.setSignerObjId(remainingItems[index]?.objectId || "");
props.setIsSelectId(index); props?.setIsSelectId(index);
props.setContractName(remainingItems[index]?.className || ""); props?.setContractName(remainingItems[index]?.className || "");
props.setUniqueId(remainingItems[index]?.Id); props?.setUniqueId(remainingItems[index]?.Id);
props.setRoleName(remainingItems[index]?.Role); props?.setRoleName(remainingItems[index]?.Role);
props.setBlockColor(remainingItems[index]?.blockColor); 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 ( return (
<> <>
@@ -101,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`} } flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
style={{ style={{
background: background:
(!isMobile && isHover === ind) || props.isSelectListId === ind (!isMobile && isHover === ind) || props.uniqueId === obj.Id
? obj?.blockColor ? obj?.blockColor
? obj?.blockColor ? obj?.blockColor
: color[ind % color.length] : color[ind % color.length]
@@ -10,7 +10,8 @@ function RenderAllPdfPage({
setSignBtnPosition, setSignBtnPosition,
pageNumber, pageNumber,
signerPos, signerPos,
signerObjectId signerObjectId,
containerWH
}) { }) {
const [signPageNumber, setSignPageNumber] = useState([]); const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState(""); const [bookmarkColor, setBookmarkColor] = useState("");
@@ -48,7 +49,7 @@ function RenderAllPdfPage({
const timer = setTimeout(updateSize, 100); // match the transition duration const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [isHeader, pageContainer]); }, [isHeader, pageContainer, containerWH]);
//'function `addSignatureBookmark` is used to display the page where the user's signature is located. //'function `addSignatureBookmark` is used to display the page where the user's signature is located.
const addSignatureBookmark = (index) => { const addSignatureBookmark = (index) => {
const ispageNumber = signPageNumber.includes(index + 1); const ispageNumber = signPageNumber.includes(index + 1);
@@ -57,13 +58,12 @@ function RenderAllPdfPage({
<div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2"> <div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
<i <i
style={{ color: bookmarkColor || "red" }} style={{ color: bookmarkColor || "red" }}
className="fa-light fa-bookmark" className="fa-solid fa-bookmark"
></i> ></i>
</div> </div>
) )
); );
}; };
return ( return (
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block"> <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"> <div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
@@ -57,7 +57,10 @@ function RenderPdf({
setTempSignerId, setTempSignerId,
uniqueId, uniqueId,
pdfOriginalWH, pdfOriginalWH,
scale scale,
setIsSelectId,
ispublicTemplate,
handleUserDetails
}) { }) {
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view //check isGuestSigner is present in local if yes than handle login flow header in mobile view
@@ -194,6 +197,8 @@ function RenderPdf({
containerWH={containerWH} containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH} pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber} pageNumber={pageNumber}
ispublicTemplate={ispublicTemplate}
handleUserDetails={handleUserDetails}
/> />
</React.Fragment> </React.Fragment>
) )
@@ -319,6 +324,7 @@ function RenderPdf({
containerWH={containerWH} containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH} pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber} pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
/> />
</React.Fragment> </React.Fragment>
); );
@@ -428,7 +434,7 @@ function RenderPdf({
> >
<div <div
style={{ style={{
width: containerWH.width * scale width: containerWH?.width && containerWH?.width * scale
}} }}
ref={drop} ref={drop}
id="container" id="container"
@@ -501,6 +507,7 @@ function RenderPdf({
containerWH={containerWH} containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH} pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber} pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
/> />
</React.Fragment> </React.Fragment>
); );
+5 -5
View File
@@ -149,7 +149,6 @@ function SignPad({
const isWidgetType = currWidgetsDetails?.type; const isWidgetType = currWidgetsDetails?.type;
const signatureType = currWidgetsDetails?.signatureType; const signatureType = currWidgetsDetails?.signatureType;
const url = currWidgetsDetails?.SignUrl; const url = currWidgetsDetails?.SignUrl;
//checking widget type and draw type signature url //checking widget type and draw type signature url
if (isInitial) { if (isInitial) {
if (isWidgetType === "initials" && signatureType === "draw" && url) { if (isWidgetType === "initials" && signatureType === "draw" && url) {
@@ -164,8 +163,8 @@ function SignPad({
} }
} }
const trimmedName = currentUserName.trim(); const trimmedName = currentUserName && currentUserName.trim();
const firstCharacter = trimmedName.charAt(0); const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName; const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName); setSignValue(userName);
setFontSelect("Fasthand"); setFontSelect("Fasthand");
@@ -299,8 +298,9 @@ function SignPad({
<div className="flex flex-row justify-between mt-[3px]"> <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"> <div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
{isStamp ? ( {isStamp ? (
<span className="op-link-primary op-link"> <span className="text-base-content font-bold text-lg">
{widgetType === "image" {widgetType === "image" ||
currWidgetsDetails?.type === "image"
? "Upload image" ? "Upload image"
: "Upload stamp image"} : "Upload stamp image"}
</span> </span>
@@ -35,7 +35,9 @@ function SignerListComponent(props) {
className="flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1" 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"> <span className="text-[12px] text-center font-bold text-black uppercase">
{getFirstLetter(props?.obj?.Name || props?.obj?.Role)} {getFirstLetter(
props.obj?.Name || props.obj?.email || props.obj?.Role
)}
</span> </span>
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
@@ -9,9 +9,7 @@ function TextFontSetting(props) {
isOpen={props.isTextSetting} isOpen={props.isTextSetting}
reduceWidth={"max-w-[350px]"} reduceWidth={"max-w-[350px]"}
title={"Text field"} title={"Text field"}
handleClose={() => { handleClose={() => props.setIsTextSetting(false)}
props.setIsTextSetting(false);
}}
> >
<div className="h-full p-[20px]"> <div className="h-full p-[20px]">
<div className="flex items-center"> <div className="flex items-center">
@@ -19,7 +17,9 @@ function TextFontSetting(props) {
<select <select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs" className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={ value={
props.fontSize || props.currWidgetsDetails?.options?.fontSize props.fontSize ||
props.currWidgetsDetails?.options?.fontSize ||
"12"
} }
onChange={(e) => props.setFontSize(e.target.value)} onChange={(e) => props.setFontSize(e.target.value)}
> >
@@ -35,7 +35,9 @@ function TextFontSetting(props) {
<span>color: </span> <span>color: </span>
<select <select
value={ value={
props.fontColor || props.currWidgetsDetails?.options?.fontColor props.fontColor ||
props.currWidgetsDetails?.options?.fontColor ||
"black"
} }
onChange={(e) => props.setFontColor(e.target.value)} 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" className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
@@ -37,11 +37,10 @@ function WidgetComponent({
sendInOrder, sendInOrder,
isTemplateFlow, isTemplateFlow,
setBlockColor, setBlockColor,
blockColor, setIsAddSigner,
setIsAddSigner uniqueId
}) { }) {
const [isSignersModal, setIsSignersModal] = useState(false); const [isSignersModal, setIsSignersModal] = useState(false);
const [, dropdown] = useDrag({ const [, dropdown] = useDrag({
type: "BOX", type: "BOX",
item: { item: {
@@ -187,7 +186,6 @@ function WidgetComponent({
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const scrollContainerRef = useRef(null); const scrollContainerRef = useRef(null);
const [widget, setWidget] = useState([]); const [widget, setWidget] = useState([]);
const color = [ const color = [
"#93a3db", "#93a3db",
"#e6c3db", "#e6c3db",
@@ -245,86 +243,66 @@ function WidgetComponent({
? textWidgetData ? textWidgetData
: widget; : 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 ( return (
<> <>
{isMobile ? ( {isMobile ? (
!isMailSend && ( !isMailSend && (
<div <div id="navbar" className="fixed z-[99] bottom-0 right-0 w-full">
id="navbar"
className="fixed z-[99] bottom-0 right-0"
style={{ width: window.innerWidth + "px" }}
>
<div className="mx-1">
{isSigners && ( {isSigners && (
<div <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" data-tut="recipientArea"
className="py-[10px] flex justify-center items-center op-card" className="w-full op-select op-select-bordered pointer-events-none"
value={handleSelectRecipient()}
style={{ style={{
background: blockColor backgroundColor: isSelectListId
? blockColor
: isSelectListId
? color[isSelectListId % color.length] ? color[isSelectListId % color.length]
: color[0] : color[0]
}} }}
onClick={() => handleModal()}
> >
<span className="text-[13px] font-bold flex items-center"> <option value={handleSelectRecipient()}>
{title ? title : "Recipient"} {handleSelectRecipient()}
{signersdata[isSelectListId]?.Role && ( </option>
<div> </select>
{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>
)}
<div className="ml-[6px] text-[16px]"> <div className="w-[18%]">
<i className="fa-light fa-angle-down"></i>
</div>
</span>
</div>
)}
</div>
<div className="mx-1">
{handleAddSigner ? ( {handleAddSigner ? (
<div <button
data-tut="reactourAddbtn" data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => handleAddSigner()} onClick={() => handleAddSigner()}
className="op-btn op-btn-accent"
> >
<i className="fa-light fa-plus "></i> <i className="fa-light fa-plus "></i>
<span>Add role</span> </button>
</div>
) : ( ) : (
setIsAddSigner && ( setIsAddSigner && (
<div <button
data-tut="addRecipient" data-tut="addRecipient"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => setIsAddSigner(true)} onClick={() => setIsAddSigner(true)}
className="op-btn op-btn-accent"
> >
<i className="fa-light fa-plus"></i> <i className="fa-light fa-plus"></i>
<span>Add recipients</span> </button>
</div>
) )
)} )}
</div> </div>
</div>
)}
<div <div
data-tut="addWidgets" data-tut="addWidgets"
ref={scrollContainerRef} ref={scrollContainerRef}
@@ -392,6 +370,7 @@ function WidgetComponent({
sendInOrder={sendInOrder} sendInOrder={sendInOrder}
setSignersData={setSignersData} setSignersData={setSignersData}
setBlockColor={setBlockColor} setBlockColor={setBlockColor}
uniqueId={uniqueId}
/> />
</div> </div>
) : ( ) : (
@@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react";
import ModalUi from "../../primitives/ModalUi"; import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css"; import "../../styles/AddUser.css";
import RegexParser from "regex-parser"; import RegexParser from "regex-parser";
import { textInputWidget } from "../../constant/Utils"; import { textInputWidget, textWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade"; import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const"; import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip"; import Tooltip from "../../primitives/Tooltip";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
const WidgetNameModal = (props) => { const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({ const [formdata, setFormdata] = useState({
@@ -23,7 +23,7 @@ const WidgetNameModal = (props) => {
useEffect(() => { useEffect(() => {
if (props.defaultdata) { if (props.defaultdata) {
setFormdata({ setFormdata({
name: props.defaultdata?.options?.name || props?.widgetName || "", name: props.defaultdata?.options?.name || "",
defaultValue: props.defaultdata?.options?.defaultValue || "", defaultValue: props.defaultdata?.options?.defaultValue || "",
status: props.defaultdata?.options?.status || "required", status: props.defaultdata?.options?.status || "required",
hint: props.defaultdata?.options?.hint || "", hint: props.defaultdata?.options?.hint || "",
@@ -35,11 +35,11 @@ const WidgetNameModal = (props) => {
} else { } else {
setFormdata({ setFormdata({
...formdata, ...formdata,
name: props.defaultdata?.options?.name || props?.widgetName || "" name: props.defaultdata?.options?.name || ""
}); });
} }
// eslint-disable-next-line // eslint-disable-next-line
}, [props.defaultdata, props.widgetName]); }, [props.defaultdata]);
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
if (props.handleData) { if (props.handleData) {
@@ -106,22 +106,10 @@ const WidgetNameModal = (props) => {
handleClose={props.handleClose && props.handleClose} handleClose={props.handleClose && props.handleClose}
title={"Widget info"} 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 <form
onSubmit={handleSubmit} onSubmit={handleSubmit}
className={`${ className={`${
props.defaultdata?.type === textInputWidget || props.defaultdata?.type === textInputWidget ? "pt-0" : ""
props.widgetName === textInputWidget
? "pt-0"
: ""
} p-[20px] text-base-content`} } p-[20px] text-base-content`}
> >
<div className="mb-[0.75rem] text-[13px]"> <div className="mb-[0.75rem] text-[13px]">
@@ -137,9 +125,9 @@ const WidgetNameModal = (props) => {
required required
/> />
</div> </div>
{(props.defaultdata?.type === textInputWidget || {props.defaultdata?.type === textInputWidget && (
props.widgetName === textInputWidget) && (
<> <>
{isEnableSubscription && (
<div className="mb-[0.75rem]"> <div className="mb-[0.75rem]">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<label <label
@@ -188,7 +176,11 @@ const WidgetNameModal = (props) => {
</option> </option>
{inputOpt.map((data, ind) => { {inputOpt.map((data, ind) => {
return ( return (
<option className="text-[13px]" key={ind} value={data}> <option
className="text-[13px]"
key={ind}
value={data}
>
{data} {data}
</option> </option>
); );
@@ -197,7 +189,7 @@ const WidgetNameModal = (props) => {
</div> </div>
</div> </div>
</div> </div>
)}
<div className="mb-[0.75rem]"> <div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]"> <label htmlFor="name" className="text-[13px]">
Default value Default value
@@ -252,8 +244,7 @@ const WidgetNameModal = (props) => {
})} })}
</div> </div>
</div> </div>
{(props.defaultdata?.type === textInputWidget || {props.defaultdata?.type === textInputWidget && (
props?.widgetName === textInputWidget) && (
<div className="mb-[0.75rem]"> <div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]"> <label htmlFor="hint" className="text-[13px]">
Hint Hint
@@ -266,6 +257,63 @@ const WidgetNameModal = (props) => {
/> />
</div> </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> <div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary"> <button type="submit" className="op-btn op-btn-primary">
Save Save
+81 -19
View File
@@ -4,11 +4,14 @@ import Submenu from "./SubMenu";
import SocialMedia from "./SocialMedia"; import SocialMedia from "./SocialMedia";
import dp from "../../assets/images/dp.png"; import dp from "../../assets/images/dp.png";
import sidebarList from "../../json/menuJson"; import sidebarList from "../../json/menuJson";
import { useNavigate } from "react-router-dom";
import { isEnableSubscription } from "../../constant/const";
const Sidebar = ({ isOpen, closeSidebar }) => { const Sidebar = ({ isOpen, closeSidebar }) => {
const navigate = useNavigate();
const [menuList, setmenuList] = useState([]); const [menuList, setmenuList] = useState([]);
const [submenuOpen, setSubmenuOpen] = useState(false); const [submenuOpen, setSubmenuOpen] = useState(false);
let username = localStorage.getItem("username"); const username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp; const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("Extand_Class") const tenantname = localStorage.getItem("Extand_Class")
? JSON.parse(localStorage.getItem("Extand_Class"))?.[0]?.Company ? JSON.parse(localStorage.getItem("Extand_Class"))?.[0]?.Company
@@ -23,29 +26,76 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
const menuItem = async () => { const menuItem = async () => {
try { try {
if (localStorage.getItem("defaultmenuid")) { if (localStorage.getItem("defaultmenuid")) {
const menuId = localStorage.getItem("defaultmenuid") !== "VPh91h0ZHk"; const Extand_Class = localStorage.getItem("Extand_Class");
if (menuId) { const extClass = Extand_Class && JSON.parse(Extand_Class);
setmenuList(sidebarList); // console.log("extClass ", extClass);
} else { let userRole = "contracts_User";
const addUserForm = { if (extClass && extClass.length > 0) {
icon: "fa-light fa-user", userRole = extClass[0].UserRole;
title: "Add User", }
target: "_self", if (
pageType: "form", userRole === "contracts_Admin" ||
description: "", userRole === "contracts_OrgAdmin"
objectId: "lM0xRnM3iE" ) {
};
const newSidebarList = sidebarList.map((item) => { const newSidebarList = sidebarList.map((item) => {
if (item.title === "Settings") { if (item.title === "Settings") {
// Make a shallow copy of the item // Make a shallow copy of the item
const newItem = { ...item }; const newItem = { ...item };
// Insert addUserForm at the second position if (isEnableSubscription) {
newItem.children.splice(1, 0, addUserForm); 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 newItem;
} }
return item; return item;
}); });
setmenuList(newSidebarList); 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) { } catch (e) {
@@ -61,13 +111,19 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
closeSidebar(); closeSidebar();
setSubmenuOpen({}); setSubmenuOpen({});
}; };
const handleProfile = () => {
navigate("/profile");
};
return ( return (
<aside <aside
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[999] shadow-lg 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"}`} ${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
> >
<div className="flex px-2 py-3 gap-2 items-center shadow-md"> <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 <img
className="w-full h-full object-contain" className="w-full h-full object-contain"
src={image} src={image}
@@ -75,9 +131,15 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
/> />
</div> </div>
<div> <div>
<p className="text-[14px] font-bold text-base-content">{username}</p>
<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" : "" tenantname ? "mt-2" : ""
}`} }`}
> >
+59 -29
View File
@@ -1,6 +1,5 @@
import axios from "axios"; import axios from "axios";
import moment from "moment"; import moment from "moment";
import { isEnableSubscription } from "./const";
import React from "react"; import React from "react";
import { rgb } from "pdf-lib"; import { rgb } from "pdf-lib";
import Parse from "parse"; import Parse from "parse";
@@ -63,7 +62,7 @@ export async function fetchSubscription(
return { plan: "", billingDate: "" }; return { plan: "", billingDate: "" };
} }
} }
//function to get subcripition details from Extand user class //function to get subcripition details from subscription class
export async function checkIsSubscribed() { export async function checkIsSubscribed() {
try { try {
const res = await fetchSubscription(); const res = await fetchSubscription();
@@ -84,6 +83,34 @@ export async function checkIsSubscribed() {
} }
} }
//function to get subcripition details from subscription class
export async function checkIsSubscribedTeam() {
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
} else if (res.billingDate) {
const plan =
res.plan === "team-weekly" ||
res.plan === "team-yearly" ||
res.plan === "teams-monthly" ||
res.plan === "teams-yearly" ||
res.plan === "enterprise-monthly" ||
res.plan === "enterprise-yearly";
if (plan && new Date(res.billingDate) > new Date()) {
return true;
} else {
return false;
}
} else {
return false;
}
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
}
}
export const color = [ export const color = [
"#93a3db", "#93a3db",
"#e6c3db", "#e6c3db",
@@ -151,7 +178,7 @@ export const getDrive = async (documentId, skip = 0, limit = 100) => {
} }
}) })
.catch((err) => { .catch((err) => {
console.log("Err ", err); console.log("Err in getDrive cloud function", err);
return "Error: Something went wrong!"; return "Error: Something went wrong!";
}); });
@@ -186,7 +213,7 @@ export const contractUsers = async (email) => {
return data; return data;
}) })
.catch((err) => { .catch((err) => {
console.log("Err ", err); console.log("Err in getUserDetails cloud function", err);
return "Error: Something went wrong!"; return "Error: Something went wrong!";
}); });
@@ -326,7 +353,7 @@ export const getDate = () => {
}; };
export const addWidgetOptions = (type) => { export const addWidgetOptions = (type) => {
const defaultOpt = { const defaultOpt = {
name: "", name: type,
status: "required" status: "required"
}; };
switch (type) { switch (type) {
@@ -541,7 +568,7 @@ export const signPdfFun = async (
if ( if (
tenantDetails?.CompletionBody && tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject && tenantDetails?.CompletionSubject &&
(!isEnableSubscription || isSubscribed) isSubscribed
) { ) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
@@ -599,7 +626,7 @@ export const signPdfFun = async (
return res; return res;
}) })
.catch((err) => { .catch((err) => {
console.log("Err ", err); console.log("Err in signPdf cloud function ", err);
setIsAlert({ setIsAlert({
isShow: true, isShow: true,
alertMessage: "something went wrong" alertMessage: "something went wrong"
@@ -669,9 +696,7 @@ export const createDocument = async (template, placeholders, signerData) => {
try { try {
const res = await axios.post( const res = await axios.post(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem("baseUrl")}classes/contracts_Document`,
"_appName"
)}_Document`,
data, data,
{ {
headers: { headers: {
@@ -1618,9 +1643,9 @@ export const placeholderHeight = (pos) => {
export const contactBook = async (objectId) => { export const contactBook = async (objectId) => {
const result = await axios const result = await axios
.get( .get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Contactbook?where={"objectId":"${objectId}"}`, )}classes/contracts_Contactbook?where={"objectId":"${objectId}"}`,
{ {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -1636,7 +1661,7 @@ export const contactBook = async (objectId) => {
}) })
.catch((err) => { .catch((err) => {
console.log("Err ", err); console.log("Err in contracts_Contactbook class ", err);
return "Error: Something went wrong!"; return "Error: Something went wrong!";
}); });
return result; return result;
@@ -1668,7 +1693,7 @@ export const contractDocument = async (documentId) => {
} }
}) })
.catch((err) => { .catch((err) => {
console.log("Err ", err); console.log("Err in getDocument cloud function ", err);
return "Error: Something went wrong!"; return "Error: Something went wrong!";
}); });
@@ -1830,18 +1855,21 @@ export const getFileName = (fileUrl) => {
//fetch tenant app logo from `partners_Tenant` class by domain name //fetch tenant app logo from `partners_Tenant` class by domain name
export const getAppLogo = async () => { export const getAppLogo = async () => {
const domainName = window.location.host; if (window.location.host === "app.opensignlabs.com") {
return { logo: appInfo.applogo, user: "exist" };
} else {
const domain = window.location.host;
try { try {
const tenantCreditsQuery = new Parse.Query("partners_Tenant"); const tenant = await Parse.Cloud.run("getlogobydomain", {
tenantCreditsQuery.equalTo("Domain", domainName); domain: domain
const res = await tenantCreditsQuery.first(); });
if (tenant) {
if (res) { return { logo: tenant?.logo, user: tenant?.user };
const updateRes = JSON.parse(JSON.stringify(res)); }
return updateRes?.Logo; } catch (err) {
console.log("err in getlogo ", err);
return { logo: appInfo.applogo, user: "exist" };
} }
} catch (e) {
return null;
} }
}; };
@@ -1940,12 +1968,12 @@ export const handleDownloadPdf = async (
try { try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl }); // const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post( const axiosRes = await axios.post(
`${appInfo.baseUrl}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl }, { url: pdfUrl },
{ {
headers: { headers: {
"content-type": "Application/json", "content-type": "Application/json",
"X-Parse-Application-Id": appInfo.appId, "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
} }
} }
@@ -1970,13 +1998,15 @@ export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
setIsDownloading("pdf"); setIsDownloading("pdf");
try { try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl }); // const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
//`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign
//if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url
const axiosRes = await axios.post( const axiosRes = await axios.post(
`${appInfo.baseUrl}/functions/getsignedurl`, `${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl }, { url: pdfUrl },
{ {
headers: { headers: {
"content-type": "Application/json", "content-type": "Application/json",
"X-Parse-Application-Id": appInfo.appId, "X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken") "X-Parse-Session-Token": localStorage.getItem("accesstoken")
} }
} }
+22 -12
View File
@@ -1,8 +1,6 @@
import logo from "../assets/images/logo.png"; import logo from "../assets/images/logo.png";
export const appInfo = { export const appInfo = {
appTitle: "contracts",
applogo: logo, applogo: logo,
appname: "contracts",
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign", appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
baseUrl: process.env.REACT_APP_SERVERURL baseUrl: process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL ? process.env.REACT_APP_SERVERURL
@@ -18,22 +16,34 @@ export const appInfo = {
: "", : "",
metaDescription: metaDescription:
"The fastest way to sign PDFs & request signatures from others.", "The fastest way to sign PDFs & request signatures from others.",
objectId: "aIPmIvMzGM",
settings: [ settings: [
{
role: "contracts_User",
menuId: "H9vRfEYKhT",
pageType: "dashboard",
pageId: "35KBoSgoAK",
extended_class: "contracts_Users"
},
{ {
role: "contracts_Admin", role: "contracts_Admin",
menuId: "VPh91h0ZHk", menuId: "VPh91h0ZHk",
pageType: "dashboard", pageType: "dashboard",
pageId: "35KBoSgoAK", pageId: "35KBoSgoAK",
extended_class: "contracts_Users" 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"
}; };
+8 -1
View File
@@ -3,4 +3,11 @@ export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document"; export const documentCls = "contracts_Document";
export const themeColor = "#47a3ad"; export const themeColor = "#47a3ad";
export const iconColor = "#686968"; 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 = true;
// process.env.REACT_APP_ENABLE_SUBSCRIPTION &&
// process.env.REACT_APP_ENABLE_SUBSCRIPTION?.toLowerCase() === "true"
// ? true
// : false;
export const isStaging = "http://localhost:3001";
// window.location.origin === "https://staging-app.opensignlabs.com";
+51 -11
View File
@@ -1,6 +1,15 @@
export default function reportJson(id) { export default function reportJson(id) {
// console.log("json ", json); // console.log("json ", json);
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"]; 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 contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"]; const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"]; const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
@@ -43,7 +52,7 @@ export default function reportJson(id) {
hoverLabel: "Sign", hoverLabel: "Sign",
btnColor: "op-btn-primary", btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature", btnIcon: "fa-light fa-signature",
redirectUrl: "pdfRequestFiles", redirectUrl: "draftDocument",
action: "redirect" action: "redirect"
} }
], ],
@@ -69,7 +78,7 @@ export default function reportJson(id) {
hoverLabel: "View", hoverLabel: "View",
btnColor: "op-btn-secondary", btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye", btnIcon: "fa-light fa-eye",
redirectUrl: "pdfRequestFiles", redirectUrl: "draftDocument",
action: "redirect" action: "redirect"
}, },
{ {
@@ -140,7 +149,7 @@ export default function reportJson(id) {
case "UPr2Fm5WY3": case "UPr2Fm5WY3":
return { return {
reportName: "Declined Documents", reportName: "Declined Documents",
heading: head, heading: declineHead,
actions: [ actions: [
{ {
btnId: "1458", btnId: "1458",
@@ -207,7 +216,7 @@ export default function reportJson(id) {
hoverLabel: "View", hoverLabel: "View",
btnColor: "op-btn-secondary", btnColor: "op-btn-secondary",
btnIcon: "fa-light fa-eye", btnIcon: "fa-light fa-eye",
redirectUrl: "pdfRequestFiles", redirectUrl: "draftDocument",
action: "redirect" action: "redirect"
}, },
{ {
@@ -258,7 +267,7 @@ export default function reportJson(id) {
hoverLabel: "Sign", hoverLabel: "Sign",
btnColor: "op-btn-primary", btnColor: "op-btn-primary",
btnIcon: "fa-light fa-signature", btnIcon: "fa-light fa-signature",
redirectUrl: "pdfRequestFiles", redirectUrl: "draftDocument",
action: "redirect" action: "redirect"
} }
] ]
@@ -306,11 +315,8 @@ 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." "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 // template report
case "6TeaPr321t": case "6TeaPr321t": {
return { const templateAct = [
reportName: "Templates",
heading: templateReport,
actions: [
{ {
btnId: "2234", btnId: "2234",
btnLabel: "Use", btnLabel: "Use",
@@ -364,10 +370,44 @@ export default function reportJson(id) {
} }
] ]
} }
], ];
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: templateActions,
helpMsg: 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." "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: default:
return null; return null;
} }
+4 -4
View File
@@ -143,17 +143,17 @@ const sidebarList = [
icon: "fa-light fa-key", icon: "fa-light fa-key",
title: "API Token", title: "API Token",
target: "_self", target: "_self",
pageType: "generatetoken", pageType: "",
description: "", description: "",
objectId: "" objectId: "generatetoken"
}, },
{ {
icon: "fa-light fa-globe", icon: "fa-light fa-globe",
title: "Webhook", title: "Webhook",
target: "_self", target: "_self",
pageType: "webhook", pageType: "",
description: "", description: "",
objectId: "" objectId: "webhook"
} }
] ]
} }
+42 -11
View File
@@ -1,11 +1,14 @@
import { isStaging } from "../constant/const";
const plans = [ const plans = [
{ {
planName: "OPENSIGN™ FREE", planName: "OPENSIGN™ FREE",
img: "free.png",
currency: "", currency: "",
monthlyPrice: "Free", monthlyPrice: "Free",
yearlyPrice: "Free", yearlyPrice: "Free",
subtitle: "Free Unlimited E-signatures, Forever.", subtitle: "Free Unlimited E-signatures, Forever.",
btnText: "Subscribe", btn: { text: "Subscribe", color: "op-btn-primary" },
url: "", url: "",
target: "_blank", target: "_blank",
benefits: [ benefits: [
@@ -28,17 +31,16 @@ const plans = [
}, },
{ {
planName: "OPENSIGN™ PROFESSIONAL", planName: "OPENSIGN™ PROFESSIONAL",
img: "professional.png",
currency: "$", currency: "$",
monthlyPrice: "29.99", monthlyPrice: "29.99",
yearlyPrice: "329.99", yearlyPrice: `10 <sup style="font-size: 17px;">/month/user</sup>`,
subtitle: "Exclusive Access to advanced features.", subtitle: "Exclusive Access to advanced features.",
btnText: "Subscribe", btn: { text: "Subscribe", color: "op-btn-primary" },
url: url: isStaging
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly" ? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly", : "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl: yearlyUrl: isStaging
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly" ? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly", : "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self", target: "_self",
@@ -58,11 +60,12 @@ const plans = [
}, },
{ {
planName: "OPENSIGN™ ENTERPRISE", planName: "OPENSIGN™ ENTERPRISE",
img: "enterprise.png",
currency: "", currency: "",
monthlyPrice: "Request Price", monthlyPrice: `<p style="font-size: 22px;">Request Price</p>`,
yearlyPrice: "Request Price", yearlyPrice: `<p style="font-size: 22px;">Request Price</p>`,
subtitle: "Scalable Features with priority support.", subtitle: "Scalable Features with priority support.",
btnText: "Contact us", btn: { text: "Contact us", color: "op-btn-primary" },
url: "https://www.opensignlabs.com/contact-us", url: "https://www.opensignlabs.com/contact-us",
target: "_blank", target: "_blank",
benefits: [ benefits: [
@@ -75,5 +78,33 @@ const plans = [
] ]
} }
]; ];
export const stagingPlan = {
planName: "OPENSIGN™ TEAMS",
img: "teams.png",
currency: "$",
monthlyPrice: "40",
yearlyPrice: `20 <sup style="font-size: 17px;">/month/user</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/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self",
benefits: [
"Everything in OpenSign™ free, professional",
"Field validations",
"Regular expression validations",
"Organize docs in OpenSign™ Drive",
"Webhooks",
"Zapier integration",
"API Access",
"100 API signatures included",
"DocumentId removal from signed docs",
"Custom email templates",
"Auto reminders"
]
};
export default plans; export default plans;
+13 -8
View File
@@ -67,6 +67,8 @@ const HomeLayout = () => {
const indexOfFirstDot = domainName.indexOf("."); const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character // Remove the first dot and get the substring starting from the next character
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
const serverUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
setCookie("accesstoken", localStorage.getItem("accesstoken"), { setCookie("accesstoken", localStorage.getItem("accesstoken"), {
secure: true, secure: true,
domain: updateDomain domain: updateDomain
@@ -75,6 +77,14 @@ const HomeLayout = () => {
secure: true, secure: true,
domain: updateDomain domain: updateDomain
}); });
setCookie("server_url", serverUrl, {
secure: true,
domain: updateDomain
});
setCookie("parse_app_id", parseAppId, {
secure: true,
domain: updateDomain
});
}; };
async function checkIsSubscribed() { async function checkIsSubscribed() {
@@ -109,11 +119,7 @@ const HomeLayout = () => {
}, [width]); }, [width]);
useEffect(() => { useEffect(() => {
if ( if (arr && arr.length > 0) {
localStorage.getItem("domain") === "contracts" &&
arr &&
arr.length > 0
) {
handleDynamicSteps(); handleDynamicSteps();
} else { } else {
setIsTour(false); setIsTour(false);
@@ -171,7 +177,6 @@ const HomeLayout = () => {
setIsTour(false); setIsTour(false);
const serverUrl = localStorage.getItem("baseUrl"); const serverUrl = localStorage.getItem("baseUrl");
const appId = localStorage.getItem("parseAppId"); const appId = localStorage.getItem("parseAppId");
const extUserClass = localStorage.getItem("extended_class");
const json = JSON.parse(localStorage.getItem("Extand_Class")); const json = JSON.parse(localStorage.getItem("Extand_Class"));
const extUserId = json && json.length > 0 && json[0].objectId; const extUserId = json && json.length > 0 && json[0].objectId;
// console.log("extUserId ", extUserId) // console.log("extUserId ", extUserId)
@@ -193,7 +198,7 @@ const HomeLayout = () => {
// console.log("updatedTourStatus ", updatedTourStatus); // console.log("updatedTourStatus ", updatedTourStatus);
await axios.put( await axios.put(
serverUrl + "classes/" + extUserClass + "/" + extUserId, serverUrl + "classes/contracts_Users/" + extUserId,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
@@ -251,7 +256,7 @@ const HomeLayout = () => {
}; };
return ( return (
<div> <div>
<div className="sticky top-0 z-[101]"> <div className="sticky top-0 z-[501]">
{!isLoader && <Header showSidebar={showSidebar} />} {!isLoader && <Header showSidebar={showSidebar} />}
</div> </div>
{isUserValid ? ( {isUserValid ? (
+454
View File
@@ -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&apos;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;
+6 -2
View File
@@ -21,7 +21,12 @@ function ChangePassword() {
user.set("password", newpassword); user.set("password", newpassword);
user user
.save() .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."); alert("Password updated successfully.");
}) })
.catch((error) => { .catch((error) => {
@@ -32,7 +37,6 @@ function ChangePassword() {
} else { } else {
alert("Your current password is missing or incorrect."); alert("Your current password is missing or incorrect.");
} }
console.log("Logged in user", user);
}) })
.catch((error) => { .catch((error) => {
alert("Your current password is missing or incorrect."); alert("Your current password is missing or incorrect.");
+3 -6
View File
@@ -36,9 +36,6 @@ function ForgotPassword() {
const handleSubmit = async (event) => { const handleSubmit = async (event) => {
event.preventDefault(); event.preventDefault();
localStorage.setItem("appLogo", appInfo.applogo); 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)); localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
if (state.email) { if (state.email) {
const username = state.email; const username = state.email;
@@ -64,9 +61,9 @@ function ForgotPassword() {
}, []); }, []);
const saveLogo = async () => { const saveLogo = async () => {
if (isEnableSubscription) { if (isEnableSubscription) {
const logo = await getAppLogo(); const app = await getAppLogo();
if (logo) { if (app?.logo) {
setImage(logo); setImage(app?.logo);
} else { } else {
setImage(appInfo?.applogo || undefined); setImage(appInfo?.applogo || undefined);
} }
+5 -11
View File
@@ -18,7 +18,6 @@ import { isEnableSubscription } from "../constant/const";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip"; import { Tooltip } from "react-tooltip";
import Upgrade from "../primitives/Upgrade"; import Upgrade from "../primitives/Upgrade";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
// `Form` render all type of Form on this basis of their provided in path // `Form` render all type of Form on this basis of their provided in path
@@ -645,7 +644,7 @@ const Forms = (props) => {
</label> </label>
{fileupload.length > 0 ? ( {fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center"> <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 h-full text-x text-[13px]"> <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"> <div className="break-all">
file selected: {getFileName(fileupload)} file selected: {getFileName(fileupload)}
</div> </div>
@@ -830,17 +829,11 @@ const Forms = (props) => {
<div className="text-center">No</div> <div className="text-center">No</div>
</div> </div>
</div> </div>
{isEnableSubscription && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span <span
className={ className={
isSubscribe || !isEnableSubscription isSubscribe
? "font-semibold" ? "font-semibold"
: "font-semibold text-gray-300" : "font-semibold text-gray-300"
} }
@@ -850,7 +843,7 @@ const Forms = (props) => {
</span> </span>
<label <label
className={`${ className={`${
isSubscribe || !isEnableSubscription isSubscribe
? "cursor-pointer " ? "cursor-pointer "
: "pointer-events-none opacity-50" : "pointer-events-none opacity-50"
} relative block items-center mb-0`} } relative block items-center mb-0`}
@@ -863,6 +856,7 @@ const Forms = (props) => {
/> />
</label> </label>
</div> </div>
)}
{formData?.autoreminder === true && ( {formData?.autoreminder === true && (
<div className="text-xs mt-2"> <div className="text-xs mt-2">
<label className="block"> <label className="block">
+3 -10
View File
@@ -5,7 +5,6 @@ import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const"; import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils"; import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard"; import SubscribeCard from "../primitives/SubscribeCard";
@@ -45,7 +44,7 @@ function GenerateToken() {
}; };
const res = await axios.post(url, {}, { headers: headers }); const res = await axios.post(url, {}, { headers: headers });
if (res) { if (res) {
SetApiToken(res.data.result.result); SetApiToken(res.data?.result?.result);
} }
setIsLoader(false); setIsLoader(false);
} catch (err) { } catch (err) {
@@ -112,7 +111,6 @@ function GenerateToken() {
) : ( ) : (
<> <>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3"> <div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}> <h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign™ API{" "} OpenSign™ API{" "}
<Tooltip <Tooltip
@@ -122,19 +120,14 @@ function GenerateToken() {
isSubscribe={true} isSubscribe={true}
/> />
</h1> </h1>
<ul <ul className="w-full flex flex-col p-2 text-sm">
className={"w-full flex flex-col p-2 text-sm"}
// 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"> <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"> <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 className="ml-1">API Token:</span>{" "}
<span <span
id="token" id="token"
className={`${ className={`${
isSubscribe || !isEnableSubscription isSubscribe
? "" ? ""
: "bg-white/20 pointer-events-none select-none" : "bg-white/20 pointer-events-none select-none"
} md:text-end py-2 md:py-0`} } md:text-end py-2 md:py-0`}
+3 -5
View File
@@ -29,9 +29,9 @@ function GuestLogin() {
//function generate serverUrl and parseAppId from url and save it in local storage //function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = async () => { const handleServerUrl = async () => {
if (isEnableSubscription) { if (isEnableSubscription) {
const applogo = await getAppLogo(); const app = await getAppLogo();
if (applogo) { if (app?.logo) {
setAppLogo(applogo); setAppLogo(app?.logo);
} else { } else {
setAppLogo(logo); setAppLogo(logo);
} }
@@ -42,10 +42,8 @@ function GuestLogin() {
localStorage.clear(); localStorage.clear();
const parseId = appInfo.appId; const parseId = appInfo.appId;
const newServer = `${appInfo.baseUrl}/`; const newServer = `${appInfo.baseUrl}/`;
const appName = appInfo.appname;
localStorage.setItem("baseUrl", newServer); localStorage.setItem("baseUrl", newServer);
localStorage.setItem("parseAppId", parseId); localStorage.setItem("parseAppId", parseId);
localStorage.setItem("_appName", appName);
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data. //this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
if (!id) { if (!id) {
//`atob` function is used to decode base64 //`atob` function is used to decode base64
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -4,12 +4,10 @@ import "../styles/managesign.css";
import "../styles/signature.css"; import "../styles/signature.css";
import { toDataUrl } from "../constant/Utils"; import { toDataUrl } from "../constant/Utils";
import Parse from "parse"; import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
const ManageSign = () => { const ManageSign = () => {
const appName = appInfo.appname;
const [penColor, setPenColor] = useState("blue"); const [penColor, setPenColor] = useState("blue");
const [initialPen, setInitialPen] = useState("blue"); const [initialPen, setInitialPen] = useState("blue");
const [image, setImage] = useState(); const [image, setImage] = useState();
@@ -39,7 +37,7 @@ const ManageSign = () => {
objectId: User.id objectId: User.id
}; };
try { try {
const signCls = `${appName}_Signature`; const signCls = "contracts_Signature";
const signQuery = new Parse.Query(signCls); const signQuery = new Parse.Query(signCls);
signQuery.equalTo("UserId", userId); signQuery.equalTo("UserId", userId);
const signRes = await signQuery.first(); const signRes = await signQuery.first();
@@ -174,7 +172,7 @@ const ManageSign = () => {
}; };
const saveEntry = async (obj) => { const saveEntry = async (obj) => {
const signCls = `${appName}_Signature`; const signCls = "contracts_Signature";
const User = Parse.User.current().id; const User = Parse.User.current().id;
const userId = { __type: "Pointer", className: "_User", objectId: User }; const userId = { __type: "Pointer", className: "_User", objectId: User };
if (id) { if (id) {
@@ -265,7 +263,7 @@ const ManageSign = () => {
<div className="relative"> <div className="relative">
<div> <div>
{image ? ( {image ? (
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box"> <div className="signatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden">
<img <img
alt="signature" alt="signature"
src={image} src={image}
+2 -3
View File
@@ -272,7 +272,7 @@ function Opensigndrive() {
setIsFolderLoader(true); setIsFolderLoader(true);
const getParentObjId = folderName[folderName.length - 1]; const getParentObjId = folderName[folderName.length - 1];
const parentId = getParentObjId && getParentObjId.objectId; const parentId = getParentObjId && getParentObjId.objectId;
const foldercls = `${localStorage.getItem("_appName")}_Document`; const foldercls = "contracts_Document";
const folderPtr = { const folderPtr = {
__type: "Pointer", __type: "Pointer",
className: foldercls, className: foldercls,
@@ -465,7 +465,6 @@ function Opensigndrive() {
if (isDontShow) { if (isDontShow) {
const serverUrl = localStorage.getItem("baseUrl"); const serverUrl = localStorage.getItem("baseUrl");
const appId = localStorage.getItem("parseAppId"); const appId = localStorage.getItem("parseAppId");
const extUserClass = localStorage.getItem("extended_class");
const json = JSON.parse(localStorage.getItem("Extand_Class")); const json = JSON.parse(localStorage.getItem("Extand_Class"));
const extUserId = json && json.length > 0 && json[0].objectId; const extUserId = json && json.length > 0 && json[0].objectId;
let updatedTourStatus = []; let updatedTourStatus = [];
@@ -483,7 +482,7 @@ function Opensigndrive() {
updatedTourStatus = [{ driveTour: true }]; updatedTourStatus = [{ driveTour: true }];
} }
await axios.put( await axios.put(
serverUrl + "classes/" + extUserClass + "/" + extUserId, serverUrl + "classes/contracts_Users/" + extUserId,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
+520 -145
View File
@@ -1,12 +1,11 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { isEnableSubscription, themeColor } from "../constant/const"; import { isEnableSubscription, isStaging, themeColor } from "../constant/const";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import "../styles/signature.css"; import "../styles/signature.css";
import Parse from "parse"; import Parse from "parse";
import axios from "axios"; import axios from "axios";
import { DndProvider } from "react-dnd"; import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend"; import { HTML5Backend } from "react-dnd-html5-backend";
import { useLocation, useNavigate, useParams } from "react-router-dom";
import SignPad from "../components/pdf/SignPad"; import SignPad from "../components/pdf/SignPad";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage"; import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import Tour from "reactour"; import Tour from "reactour";
@@ -42,22 +41,13 @@ import Title from "../components/Title";
import DefaultSignature from "../components/pdf/DefaultSignature"; import DefaultSignature from "../components/pdf/DefaultSignature";
import ModalUi from "../primitives/ModalUi"; import ModalUi from "../primitives/ModalUi";
import TourContentWithBtn from "../primitives/TourContentWithBtn"; import TourContentWithBtn from "../primitives/TourContentWithBtn";
import { appInfo } from "../constant/appinfo";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import SignerListComponent from "../components/pdf/SignerListComponent"; import SignerListComponent from "../components/pdf/SignerListComponent";
import VerifyEmail from "../components/pdf/VerifyEmail"; import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom"; import PdfZoom from "../components/pdf/PdfZoom";
function useQuery() { function PdfRequestFiles(props) {
return new URLSearchParams(useLocation().search);
}
function PdfRequestFiles() {
const { docId } = useParams();
const navigate = useNavigate();
const location = useLocation();
const query = useQuery();
const sendmail = query.get("sendmail");
const [pdfDetails, setPdfDetails] = useState([]); const [pdfDetails, setPdfDetails] = useState([]);
const [signedSigners, setSignedSigners] = useState([]); const [signedSigners, setSignedSigners] = useState([]);
const [unsignedSigners, setUnSignedSigners] = useState([]); const [unsignedSigners, setUnSignedSigners] = useState([]);
@@ -129,34 +119,49 @@ function PdfRequestFiles() {
const [zoomPercent, setZoomPercent] = useState(0); const [zoomPercent, setZoomPercent] = useState(0);
const [totalZoomPercent, setTotalZoomPercent] = useState(); const [totalZoomPercent, setTotalZoomPercent] = useState();
const [scale, setScale] = useState(1); const [scale, setScale] = useState(1);
const [uniqueId, setUniqueId] = useState("");
const [isPublicTemplate, setIsPublicTemplate] = useState(false);
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
const [isOtp, setIsOtp] = useState(false);
const [loading, setLoading] = useState(false);
const [res, setRes] = useState({});
const [documentId, setDocumentId] = useState("");
const [isPublicContact, setIsPublicContact] = useState(false);
const isHeader = useSelector((state) => state.showHeader); const isHeader = useSelector((state) => state.showHeader);
const divRef = useRef(null); const divRef = useRef(null);
const isMobile = window.innerWidth < 767; const isMobile = window.innerWidth < 767;
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const objectId = let isGuestSignFlow = false;
rowLevel && rowLevel.id let sendmail;
? rowLevel.id let getDocId = "";
: rowLevel?.objectId && rowLevel.objectId; const route = !props.templateId && window.location.pathname;
const documentId = docId ? docId : objectId && objectId; const getQuery =
!props.templateId &&
window.location?.search &&
window.location?.search?.split("?");
const senderUser = if (getQuery) {
localStorage.getItem( sendmail = getQuery[1].split("=")[1];
`Parse/${localStorage.getItem("parseAppId")}/currentUser` }
) && const checkSplit = route && route?.split("/");
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
if (checkSplit && checkSplit.length > 4) {
isGuestSignFlow = true;
getDocId = checkSplit[3];
} else {
getDocId = checkSplit[2];
}
let getDocumentId = getDocId || documentId;
useEffect(() => { useEffect(() => {
if (documentId) { if (getDocumentId) {
getDocumentDetails(); setDocumentId(getDocumentId);
getDocumentDetails(getDocumentId);
} else if (props.templateId) {
getTemplateDetails();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, [props.templateId, getDocumentId]);
useEffect(() => { useEffect(() => {
const updateSize = () => { const updateSize = () => {
if (divRef.current) { if (divRef.current) {
@@ -171,7 +176,6 @@ function PdfRequestFiles() {
// Use setTimeout to wait for the transition to complete // Use setTimeout to wait for the transition to complete
const timer = setTimeout(updateSize, 100); // match the transition duration const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer); return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]); }, [divRef.current, isHeader]);
@@ -180,7 +184,7 @@ function PdfRequestFiles() {
e.preventDefault(); e.preventDefault();
setOtpLoader(true); setOtpLoader(true);
const localuser = localStorage.getItem( const localuser = localStorage.getItem(
`Parse/${appInfo.appId}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const currentUser = JSON.parse(localuser); const currentUser = JSON.parse(localuser);
await handleSendOTP(currentUser?.email); await handleSendOTP(currentUser?.email);
@@ -192,7 +196,7 @@ function PdfRequestFiles() {
e.preventDefault(); e.preventDefault();
setOtpLoader(true); setOtpLoader(true);
const localuser = localStorage.getItem( const localuser = localStorage.getItem(
`Parse/${appInfo.appId}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const currentUser = JSON.parse(localuser); const currentUser = JSON.parse(localuser);
try { try {
@@ -219,13 +223,13 @@ function PdfRequestFiles() {
const handleVerifyBtn = async () => { const handleVerifyBtn = async () => {
setIsVerifyModal(true); setIsVerifyModal(true);
const localuser = localStorage.getItem( const localuser = localStorage.getItem(
`Parse/${appInfo.appId}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const currentUser = JSON.parse(localuser); const currentUser = JSON.parse(localuser);
await handleSendOTP(currentUser?.email); await handleSendOTP(currentUser?.email);
}; };
async function checkIsSubscribed(extUserId, contactId) { async function checkIsSubscribed(extUserId, contactId) {
const isGuestSign = location.pathname.includes("/load/") || false; const isGuestSign = isGuestSignFlow || false;
const res = await fetchSubscription(extUserId, contactId, isGuestSign); const res = await fetchSubscription(extUserId, contactId, isGuestSign);
const plan = res.plan; const plan = res.plan;
const billingDate = res?.billingDate; const billingDate = res?.billingDate;
@@ -237,10 +241,10 @@ function PdfRequestFiles() {
setIsSubscribed(true); setIsSubscribed(true);
return true; return true;
} else { } else {
if (location.pathname.includes("/load/")) { if (isGuestSign) {
setIsSubscriptionExpired(true); setIsSubscriptionExpired(true);
} else { } else {
navigate(`/subscription`); window.location.href = "/subscription";
} }
} }
} else if (isGuestSign) { } else if (isGuestSign) {
@@ -254,15 +258,92 @@ function PdfRequestFiles() {
if (isGuestSign) { if (isGuestSign) {
setIsSubscriptionExpired(true); setIsSubscriptionExpired(true);
} else { } else {
navigate(`/subscription`); window.location.href = "/subscription";
} }
} }
} }
//function for get document details for perticular signer with signer'object id //function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (isNextUser) => { const getTemplateDetails = async () => {
try {
const params = { templateId: props.templateId, ispublic: true };
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
const documentData =
templateDeatils.data && templateDeatils.data.result
? [templateDeatils.data.result]
: [];
if (documentData && documentData.length > 0) {
setIsPublicTemplate(true);
const getPublicRole = documentData[0]?.PublicRole[0];
const getUniqueIdDetails = documentData[0]?.Placeholders.find(
(x) => x.Role === getPublicRole
);
if (getUniqueIdDetails) {
setUniqueId(getUniqueIdDetails.Id);
}
setSignerPos(documentData[0]?.Placeholders);
let placeholdersOrSigners = [];
// const placeholder = documentData[0]?.Placeholders;
for (const placeholder of documentData[0].Placeholders) {
//`emailExist` variable to handle condition for quick send flow and show unsigned signers list
const signerIdExist = placeholder?.signerObjId;
if (signerIdExist) {
const getSignerData = documentData[0].Signers.find(
(data) => data.objectId === placeholder?.signerObjId
);
placeholdersOrSigners.push(getSignerData);
} else {
placeholdersOrSigners.push(placeholder);
}
}
setUnSignedSigners(placeholdersOrSigners);
setPdfDetails(documentData);
setIsLoading({
isLoad: false
});
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
console.log("err in get template details ");
setHandleError("Error: Something went wrong!");
setIsLoading({
isLoad: false
});
} else {
setHandleError("No Data Found!");
setIsLoading({
isLoad: false
});
}
} catch (err) {
console.log("err in get template details ", err);
if (err?.response?.data?.code === 101) {
setHandleError("Error: Template not found!");
} else {
setHandleError("Error: Something went wrong!");
}
}
};
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (docId, isNextUser) => {
const senderUser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
let currUserId; let currUserId;
//getting document details //getting document details
const documentData = await contractDocument(documentId); const documentData = await contractDocument(documentId || docId);
if (documentData && documentData.length > 0) { if (documentData && documentData.length > 0) {
setExtUserId(documentData[0]?.ExtUserPtr?.objectId); setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const isCompleted = const isCompleted =
@@ -275,7 +356,7 @@ function PdfRequestFiles() {
const getCurrentSigner = const getCurrentSigner =
getSigners && getSigners &&
getSigners.filter( getSigners.filter(
(data) => data.UserId.objectId === jsonSender.objectId (data) => data.UserId.objectId === jsonSender?.objectId
); );
currUserId = getCurrentSigner[0] ? getCurrentSigner[0].objectId : ""; currUserId = getCurrentSigner[0] ? getCurrentSigner[0].objectId : "";
@@ -329,40 +410,14 @@ function PdfRequestFiles() {
"You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers." "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers."
}); });
} else { } else {
if (currUserId) {
const checkCurrentUser = documentData[0].Placeholders.find( const checkCurrentUser = documentData[0].Placeholders.find(
(data) => data.signerObjId === currUserId (data) => data?.signerObjId === currUserId
); );
if (checkCurrentUser) { if (checkCurrentUser) {
setCurrentSigner(true); setCurrentSigner(true);
} }
} }
const isGuestSign = location.pathname.includes("/load/");
if (
!isGuestSign &&
!isCompleted &&
!declined &&
currDate < expireUpdateDate
) {
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
let isEmailVerified;
isEmailVerified = currentUser?.emailVerified;
if (isEmailVerified) {
setIsEmailVerified(isEmailVerified);
} else {
try {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(currentUser.objectId, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
isEmailVerified = user?.get("emailVerified");
setIsEmailVerified(isEmailVerified);
}
} catch (e) {
setHandleError("Error: Something went wrong!");
}
}
} }
const audittrailData = const audittrailData =
documentData[0].AuditTrail && documentData[0].AuditTrail &&
@@ -436,14 +491,14 @@ function PdfRequestFiles() {
const placeholdersOrSigners = []; const placeholdersOrSigners = [];
for (const placeholder of documentData[0].Placeholders) { for (const placeholder of documentData[0].Placeholders) {
//`emailExist` variable to handle condition for quick send flow and show unsigned signers list //`emailExist` variable to handle condition for quick send flow and show unsigned signers list
const emailExist = placeholder?.email; const signerIdExist = placeholder?.signerObjId;
if (emailExist) { if (signerIdExist) {
placeholdersOrSigners.push(placeholder);
} else {
const getSignerData = documentData[0].Signers.find( const getSignerData = documentData[0].Signers.find(
(data) => data.objectId === placeholder?.signerObjId (data) => data.objectId === placeholder?.signerObjId
); );
placeholdersOrSigners.push(getSignerData); placeholdersOrSigners.push(getSignerData);
} else {
placeholdersOrSigners.push(placeholder);
} }
} }
//condition to check already signed document by someone //condition to check already signed document by someone
@@ -498,7 +553,7 @@ function PdfRequestFiles() {
//else condition to check current user exist in contracts_Users class and check tour message status //else condition to check current user exist in contracts_Users class and check tour message status
//if not then check user exist in contracts_Contactbook class and check tour message status //if not then check user exist in contracts_Contactbook class and check tour message status
const localuser = localStorage.getItem( const localuser = localStorage.getItem(
`Parse/${appInfo.appId}/currentUser` `Parse/${localStorage.getItem("parseAppId")}/currentUser`
); );
const currentUser = JSON.parse(localuser); const currentUser = JSON.parse(localuser);
const currentUserEmail = currentUser.email; const currentUserEmail = currentUser.email;
@@ -511,12 +566,14 @@ function PdfRequestFiles() {
setSignerUserId(currUserId); setSignerUserId(currUserId);
const tourData = res[0].TourStatus && res[0].TourStatus; const tourData = res[0].TourStatus && res[0].TourStatus;
if (tourData && tourData.length > 0) { if (tourData && tourData.length > 0) {
const checkTourRequest = tourData.filter(
(data) => data?.requestSign
);
setTourStatus(tourData); setTourStatus(tourData);
setRequestSignTour(tourData[0]?.requestSign || false); setRequestSignTour(checkTourRequest[0]?.requestSign || false);
} }
} else if (res?.length === 0) { } else if (res?.length === 0) {
const res = await contactBook(currUserId); const res = await contactBook(currUserId);
if (res === "Error: Something went wrong!") { if (res === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!"); setHandleError("Error: Something went wrong!");
} else if (res[0] && res.length) { } else if (res[0] && res.length) {
@@ -525,8 +582,11 @@ function PdfRequestFiles() {
setSignerUserId(objectId); setSignerUserId(objectId);
const tourData = res[0].TourStatus && res[0].TourStatus; const tourData = res[0].TourStatus && res[0].TourStatus;
if (tourData && tourData.length > 0) { if (tourData && tourData.length > 0) {
const checkTourRequest = tourData.filter(
(data) => data?.requestSign
);
setTourStatus(tourData); setTourStatus(tourData);
setRequestSignTour(tourData[0]?.requestSign || false); setRequestSignTour(checkTourRequest[0]?.requestSign || false);
} }
} else if (res.length === 0) { } else if (res.length === 0) {
setHandleError("Error: User does not exist!"); setHandleError("Error: User does not exist!");
@@ -538,24 +598,22 @@ function PdfRequestFiles() {
documentData === "Error: Something went wrong!" || documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error) (documentData.result && documentData.result.error)
) { ) {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!"); setHandleError("Error: Something went wrong!");
setIsLoading(loadObj); setIsLoading({
isLoad: false
});
console.log("err in getDocument cloud function ");
} else { } else {
setHandleError("No Data Found!"); setHandleError("No Data Found!");
const loadObj = { setIsUiLoading({
isLoad: false isLoad: false
}; });
setIsLoading(loadObj);
setIsUiLoading(false);
} }
await axios await axios
.get( .get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${ )}classes/contracts_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${
jsonSender?.objectId jsonSender?.objectId
}"}}`, }"}}`,
{ {
@@ -581,16 +639,34 @@ function PdfRequestFiles() {
setIsLoading(loadObj); setIsLoading(loadObj);
}) })
.catch((err) => { .catch((err) => {
const loadObj = { console.log("Err in contracts_Signature class", err);
isLoad: false
};
console.log("Err ", err);
setHandleError("Error: Something went wrong!"); setHandleError("Error: Something went wrong!");
setIsLoading(loadObj); setIsLoading({
isLoad: false
});
}); });
}; };
//function for embed signature or image url in pdf //function for embed signature or image url in pdf
async function embedWidgetsData() { async function embedWidgetsData() {
//for emailVerified data checking first in localstorage
const localuser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
let currentUser = JSON.parse(localuser);
//if emailVerified data is not present in local user details then fetch again in _User class
if (!currentUser?.emailVerified) {
const userQuery = new Parse.Query(Parse.User);
const getUser = await userQuery.get(currentUser?.objectId, {
sessionToken: currentUser?.sessionToken
});
if (getUser) {
currentUser = JSON.parse(JSON.stringify(getUser));
}
}
let isEmailVerified = currentUser?.emailVerified;
//check if isEmailVerified then go on next step
if (isEmailVerified) {
setIsEmailVerified(isEmailVerified);
try { try {
const checkUser = signerPos.filter( const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId (data) => data.signerObjId === signerObjectId
@@ -619,7 +695,8 @@ function PdfRequestFiles() {
//get all required type checkbox //get all required type checkbox
requiredCheckbox = checkUser[0].placeHolder[i].pos.filter( requiredCheckbox = checkUser[0].placeHolder[i].pos.filter(
(position) => (position) =>
!position.options?.isReadOnly && position.type === "checkbox" !position.options?.isReadOnly &&
position.type === "checkbox"
); );
//if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check //if required type checkbox data exit then check user checked all checkbox or some checkbox remain to check
//also validate to minimum and maximum required checkbox //also validate to minimum and maximum required checkbox
@@ -653,7 +730,10 @@ function PdfRequestFiles() {
setminRequiredCount(parseMin); setminRequiredCount(parseMin);
} }
//else condition to validate minimum required checkbox //else condition to validate minimum required checkbox
else if (parseMin > 0 && (parseMin > response || !response)) { else if (
parseMin > 0 &&
(parseMin > response || !response)
) {
if (!showAlert) { if (!showAlert) {
showAlert = true; showAlert = true;
widgetKey = requiredCheckbox[i].key; widgetKey = requiredCheckbox[i].key;
@@ -820,7 +900,7 @@ function PdfRequestFiles() {
setUnSignedSigners([]); setUnSignedSigners([]);
getDocumentDetails(true); getDocumentDetails(true);
const index = pdfDetails?.[0].Signers.findIndex( const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === jsonSender.email (x) => x.Email === currentUser?.email
); );
const newIndex = index + 1; const newIndex = index + 1;
const usermail = { const usermail = {
@@ -872,7 +952,12 @@ function PdfRequestFiles() {
`${pdfDetails?.[0].objectId}/${user.Email}` `${pdfDetails?.[0].objectId}/${user.Email}`
); );
} }
let signPdf = `${hostUrl}/login/${encodeBase64}`; const hostPublicUrl = isStaging
? "https://staging-app.opensignlabs.com"
: "https://app.opensignlabs.com";
let signPdf = props?.templateId
? `${hostPublicUrl}/login/${encodeBase64}`
: `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = const openSignUrl =
"https://www.opensignlabs.com/contact-us"; "https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company const orgName = pdfDetails[0]?.ExtUserPtr.Company
@@ -880,11 +965,7 @@ function PdfRequestFiles() {
: ""; : "";
const themeBGcolor = themeColor; const themeBGcolor = themeColor;
let replaceVar; let replaceVar;
if ( if (requestBody && requestSubject && isSubscribed) {
requestBody &&
requestSubject &&
(!isEnableSubscription || isSubscribed)
) {
const replacedRequestBody = requestBody.replace( const replacedRequestBody = requestBody.replace(
/"/g, /"/g,
"'" "'"
@@ -996,6 +1077,22 @@ function PdfRequestFiles() {
alertMessage: "something went wrong, please try again later." alertMessage: "something went wrong, please try again later."
}); });
} }
} else {
//else verify users email
try {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(currentUser?.objectId, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
isEmailVerified = user?.get("emailVerified");
setIsEmailVerified(isEmailVerified);
}
} catch (e) {
console.log("error in save user's emailVerified in user class");
setHandleError("Error: Something went wrong!");
}
}
} }
//function for update TourStatus //function for update TourStatus
@@ -1075,7 +1172,6 @@ function PdfRequestFiles() {
return newState; // Update the state with the modified copy return newState; // Update the state with the modified copy
}); });
}; };
//function for save button to save signature or image url //function for save button to save signature or image url
const saveSign = (type, isDefaultSign, width, height) => { const saveSign = (type, isDefaultSign, width, height) => {
const isTypeText = width && height ? true : false; const isTypeText = width && height ? true : false;
@@ -1140,11 +1236,13 @@ function PdfRequestFiles() {
}); });
}; };
//function for set decline true on press decline button //function for set decline true on press decline button
const declineDoc = async () => { const declineDoc = async (reason) => {
const senderUser = localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
setIsDecline({ isDeclined: false }); setIsDecline({ isDeclined: false });
const data = { const data = { IsDeclined: true, DeclineReason: reason };
IsDeclined: true
};
setIsUiLoading(true); setIsUiLoading(true);
await axios await axios
@@ -1255,9 +1353,9 @@ function PdfRequestFiles() {
} }
try { try {
await axios.put( await axios.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}${contractName}/${signerUserId}`, )}classes/contracts${contractName}/${signerUserId}`,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
@@ -1359,9 +1457,143 @@ function PdfRequestFiles() {
/> />
); );
}; };
const handleUserDetails = () => {
setIsPublicContact(true);
};
//`handlePublicUser` function to use create user from public role and create document from public template
const handlePublicUser = async (e) => {
e.preventDefault();
setLoading(true);
try {
const params = {
...contact,
templateid: pdfDetails[0]?.objectId,
role: pdfDetails[0]?.PublicRole[0]
};
const userRes = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}/functions/publicuserlinkcontacttodoc`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
if (userRes?.data?.result) {
setRes(userRes.data.result);
await SendOtp();
} else {
console.log("error in public-sign to create user details");
alert("something went wrong");
}
} catch (e) {
console.log("e", e);
// setIsLoader(false);
}
};
const handleInputChange = (e) => {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
const SendOtp = async () => {
try {
const params = { email: contact.email, docId: res.docId };
const Otp = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/SendOTPMailV1`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
);
if (Otp) {
setIsOtp(true);
setLoading(false);
}
} catch (error) {
console.log("error in verify otp in public-sign", error);
alert("something went wrong!");
}
};
//verify OTP send on via email
const VerifyOTP = async (e) => {
e.preventDefault();
setLoading(true);
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (otp) {
// setLoading(true);
try {
let url = `${serverUrl}/functions/AuthLoginAsMail/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId
};
let body = {
email: contact.email,
otp: otp
};
let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") {
alert("Invalid Otp");
setLoading(false);
} else if (user.data.result === "user not found!") {
alert("User not found!");
setLoading(false);
} else {
let _user = user.data.result;
const parseId = localStorage.getItem("parseAppId");
const contractUserDetails = await contractUsers(_user.email);
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem(
`Parse/${parseId}/currentUser`,
JSON.stringify(_user)
);
if (contractUserDetails && contractUserDetails.length > 0) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
);
}
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken);
setLoading(false);
// navigate(`/load/recipientSignPdf/${res?.docId}/${res?.contactId}`);
// document.getElementById("my_modal").close();
setIsPublicContact(false);
setIsPublicTemplate(false);
setIsLoading({
isLoad: false
});
setDocumentId(res?.docId);
getDocumentDetails(res?.docId);
}
} catch (error) {
console.log("err ", error);
}
} else {
alert("Please Enter OTP!");
}
};
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} /> <Title title={props.templateId ? "Public Sign" : "Request Sign"} />
{isSubscriptionExpired ? ( {isSubscriptionExpired ? (
<ModalUi <ModalUi
title={"Subscription Expired"} title={"Subscription Expired"}
@@ -1408,7 +1640,9 @@ function PdfRequestFiles() {
}} }}
className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300" className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"
> >
{!requestSignTour && requestSignTourFunction()} {!requestSignTour &&
signerObjectId &&
requestSignTourFunction()}
<ModalUi <ModalUi
isOpen={isAlert.isShow} isOpen={isAlert.isShow}
title={"Alert message"} title={"Alert message"}
@@ -1451,7 +1685,7 @@ function PdfRequestFiles() {
{/* this modal is used to show decline alert */} {/* this modal is used to show decline alert */}
<PdfDeclineModal <PdfDeclineModal
show={isDecline.isDeclined} show={isDecline.isDeclined}
headMsg="Document Declined" headMsg="Document decline"
bodyMssg={ bodyMssg={
isDecline.currnt === "Sure" isDecline.currnt === "Sure"
? "Are you sure want to decline this document ?" ? "Are you sure want to decline this document ?"
@@ -1467,7 +1701,7 @@ function PdfRequestFiles() {
{/* this modal is used for show expired alert */} {/* this modal is used for show expired alert */}
<PdfDeclineModal <PdfDeclineModal
show={isExpired} show={isExpired}
headMsg="Document Expired!" headMsg="Document expired"
bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`} bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`}
/> />
{!isEmailVerified && ( {!isEmailVerified && (
@@ -1484,14 +1718,152 @@ function PdfRequestFiles() {
)} )}
<ModalUi <ModalUi
isOpen={defaultSignAlert.isShow} isOpen={isPublicContact}
title={"Auto sign"} title={"Contact Details"}
handleClose={() => { handleClose={() => {
setDefaultSignAlert({ setIsPublicContact(false);
isShow: false, }}
alertMessage: "" >
<div className="h-full p-[20px]">
{isOtp ? (
<form onSubmit={VerifyOTP}>
<div className="flex flex-col gap-2">
<span>You will get a OTP via Email</span>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<input
type="number"
name="otp"
className="grow"
placeholder="Enter Verification Code"
required
value={otp}
onChange={(e) => setOtp(e.target.value)}
disabled={loading}
/>
</label>
</div>
<div className="op-modal-action">
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
// document.getElementById("my_modal").close();
setIsPublicContact(false);
setLoading(false);
setIsOtp(false);
setOtp();
setContact({
name: "",
email: "",
phone: ""
}); });
}} }}
>
Cancel
</button>
<button
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Verify"}
</button>
</div>
</div>
</form>
) : (
<form onSubmit={handlePublicUser}>
<div className="flex flex-col gap-2">
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
<input
type="text"
className="grow"
name="name"
value={contact.name}
onChange={handleInputChange}
placeholder="name"
required
disabled={loading}
/>
</label>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M2.5 3A1.5 1.5 0 0 0 1 4.5v.793c.026.009.051.02.076.032L7.674 8.51c.206.1.446.1.652 0l6.598-3.185A.755.755 0 0 1 15 5.293V4.5A1.5 1.5 0 0 0 13.5 3h-11Z" />
<path d="M15 6.954 8.978 9.86a2.25 2.25 0 0 1-1.956 0L1 6.954V11.5A1.5 1.5 0 0 0 2.5 13h11a1.5 1.5 0 0 0 1.5-1.5V6.954Z" />
</svg>
<input
type="text"
className="grow"
name="email"
value={contact.email}
onChange={handleInputChange}
placeholder="Email"
required
disabled={loading}
/>
</label>
<label className="op-input op-input-bordered flex items-center gap-2 ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
className="w-4 h-4 opacity-70"
>
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" />
</svg>
<input
value={contact.phone}
onChange={handleInputChange}
type="text"
name="phone"
className="grow"
placeholder="phone"
disabled={loading}
/>
</label>
</div>
<div className="op-modal-action">
<div className="flex gap-2">
<button
className="op-btn op-btn-ghost"
onClick={() => {
// document.getElementById("my_modal").close();
setIsPublicContact(false);
}}
>
Close
</button>
<button
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Submit"}
</button>
</div>
</div>
</form>
)}
</div>
</ModalUi>
<ModalUi
isOpen={defaultSignAlert.isShow}
title={"Auto sign"}
handleClose={() =>
setDefaultSignAlert({ isShow: false, alertMessage: "" })
}
> >
<div className="h-full p-[20px]"> <div className="h-full p-[20px]">
<p>{defaultSignAlert.alertMessage}</p> <p>{defaultSignAlert.alertMessage}</p>
@@ -1506,12 +1878,12 @@ function PdfRequestFiles() {
Yes Yes
</button> </button>
<button <button
onClick={() => { onClick={() =>
setDefaultSignAlert({ setDefaultSignAlert({
isShow: false, isShow: false,
alertMessage: "" alertMessage: ""
}); })
}} }
type="button" type="button"
className="op-btn op-btn-secondary" className="op-btn op-btn-secondary"
> >
@@ -1520,12 +1892,9 @@ function PdfRequestFiles() {
</> </>
) : ( ) : (
<button <button
onClick={() => { onClick={() =>
setIsAlert({ setIsAlert({ isShow: false, alertMessage: "" })
isShow: false, }
alertMessage: ""
});
}}
type="button" type="button"
className="op-btn op-btn-primary" className="op-btn op-btn-primary"
> >
@@ -1546,6 +1915,7 @@ function PdfRequestFiles() {
setAllPages={setAllPages} setAllPages={setAllPages}
setPageNumber={setPageNumber} setPageNumber={setPageNumber}
pageNumber={pageNumber} pageNumber={pageNumber}
containerWH={containerWH}
/> />
{/* pdf render view */} {/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4"> <div className=" w-full md:w-[57%] flex mr-4">
@@ -1582,6 +1952,19 @@ function PdfRequestFiles() {
)} )}
{!isCompleted?.message && ( {!isCompleted?.message && (
<div className="flex mt-4 gap-1 px-[15px]"> <div className="flex mt-4 gap-1 px-[15px]">
<button
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
>
<i
className="fa-light fa-print"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
<button <button
type="button" type="button"
onClick={() => onClick={() =>
@@ -1600,19 +1983,6 @@ function PdfRequestFiles() {
Certificate Certificate
</span> </span>
</button> </button>
<button
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral"
>
<i
className="fa-light fa-print"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
</button>
<button <button
type="button" type="button"
className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary" className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary"
@@ -1681,7 +2051,7 @@ function PdfRequestFiles() {
/> />
{/* pdf header which contain funish back button */} {/* pdf header which contain funish back button */}
<Header <Header
isPdfRequestFiles={true} isPdfRequestFiles={isPublicTemplate ? false : true}
pageNumber={pageNumber} pageNumber={pageNumber}
allPages={allPages} allPages={allPages}
changePage={changePage} changePage={changePage}
@@ -1689,7 +2059,9 @@ function PdfRequestFiles() {
signerPos={signerPos} signerPos={signerPos}
isSigned={isSigned} isSigned={isSigned}
isCompleted={isCompleted.isCertificate} isCompleted={isCompleted.isCertificate}
embedWidgetsData={embedWidgetsData} embedWidgetsData={
isPublicTemplate ? handleUserDetails : embedWidgetsData
}
isShowHeader={true} isShowHeader={true}
setIsDecline={setIsDecline} setIsDecline={setIsDecline}
decline={true} decline={true}
@@ -1704,6 +2076,7 @@ function PdfRequestFiles() {
containerWH={containerWH} containerWH={containerWH}
setZoomPercent={setZoomPercent} setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent} zoomPercent={zoomPercent}
isPublicTemplate={isPublicTemplate}
/> />
<div ref={divRef} data-tut="pdfArea" className="h-[95%]"> <div ref={divRef} data-tut="pdfArea" className="h-[95%]">
@@ -1740,6 +2113,9 @@ function PdfRequestFiles() {
setTotalZoomPercent={setTotalZoomPercent} setTotalZoomPercent={setTotalZoomPercent}
setScale={setScale} setScale={setScale}
scale={scale} scale={scale}
uniqueId={uniqueId}
ispublicTemplate={isPublicTemplate}
handleUserDetails={handleUserDetails}
/> />
)} )}
</div> </div>
@@ -1839,5 +2215,4 @@ function PdfRequestFiles() {
</DndProvider> </DndProvider>
); );
} }
export default PdfRequestFiles; export default PdfRequestFiles;
+42 -171
View File
@@ -70,6 +70,19 @@ const PgSignUp = () => {
}); });
if (res) { if (res) {
localStorage.removeItem("userDetails"); 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( navigate(
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId "/" + userSettings[0].pageType + "/" + userSettings[0].pageId
); );
@@ -107,8 +120,6 @@ const PgSignUp = () => {
} }
}; };
const saveUser = async (obj) => { const saveUser = async (obj) => {
// const domain = localStorage.getItem("domain");
// console.log("domain", domain)
try { try {
const zohoRes = await axios.post( const zohoRes = await axios.post(
parseBaseUrl + "functions/zohodetails", parseBaseUrl + "functions/zohodetails",
@@ -209,8 +220,6 @@ const PgSignUp = () => {
if (res.data) { if (res.data) {
let _user = res.data; let _user = res.data;
localStorage.setItem("UserInformation", JSON.stringify(_user)); localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("userEmail", _user.email);
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken); localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true); localStorage.setItem("scriptId", true);
if (_user.ProfilePic) { if (_user.ProfilePic) {
@@ -220,190 +229,52 @@ const PgSignUp = () => {
} }
// Check extended class user role and tenentId // Check extended class user role and tenentId
try { try {
let userRoles = []; const userSettings = appInfo.settings;
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(); const currentUser = Parse.User.current();
await Parse.Cloud.run("getUserDetails", { const extUser = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email") 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) { if (extUser) {
dispatch( const IsDisabled = extUser?.get("IsDisabled") || false;
showTenant(tenentInfo[0].tenentName || "") if (!IsDisabled) {
); const userRole = extUser?.get("UserRole");
localStorage.setItem( const menu =
"TenantName", userRole && userSettings.find((menu) => menu.role === userRole);
tenentInfo[0].tenentName || "" if (menu) {
); const _currentRole = userRole;
} const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
localStorage.setItem("showpopup", true); const extInfo_stringify = JSON.stringify([extUser]);
localStorage.setItem( localStorage.setItem("Extand_Class", extInfo_stringify);
"PageLanding", const extInfo = JSON.parse(JSON.stringify(extUser));
element.pageId localStorage.setItem("userEmail", extInfo?.Email);
); localStorage.setItem("username", extInfo?.Name);
localStorage.setItem( if (extInfo?.TenantId) {
"defaultmenuid", const tenant = {
element.menuId Id: extInfo?.TenantId?.objectId || "",
); Name: extInfo?.TenantId?.TenantName || ""
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( localStorage.setItem("TenantId", tenant?.Id);
"TenantId", dispatch(showTenant(tenant?.Name));
x.TenantId.objectId localStorage.setItem("TenantName", tenant?.Name);
);
tenentInfo.push(obj);
} }
}); localStorage.setItem("PageLanding", menu.pageId);
if (tenentInfo.length) { localStorage.setItem("defaultmenuid", menu.menuId);
dispatch( localStorage.setItem("pageType", menu.pageType);
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); setIsLoader(false);
alert("Registered user successfully"); alert("Registered user successfully");
const redirectUrl = const redirectUrl =
location?.state?.from || location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
`/${element.pageType}/${element.pageId}`;
// Redirect to the appropriate URL after successful login // Redirect to the appropriate URL after successful login
navigate(redirectUrl); 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 { } else {
setIsLoader(false); setIsLoader(false);
} }
} else {
setIsLoader(false);
}
})
.catch((err) => {
console.log("err", err);
setIsLoader(false);
});
} }
} catch (error) { } catch (error) {
setIsLoader(false); setIsLoader(false);
console.log(error); console.log("Err in fetch extuser", error);
} }
} }
}; };
+63 -130
View File
@@ -50,7 +50,6 @@ import Upgrade from "../primitives/Upgrade";
import Alert from "../primitives/Alert"; import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import TextFontSetting from "../components/pdf/TextFontSetting";
import PdfZoom from "../components/pdf/PdfZoom"; import PdfZoom from "../components/pdf/PdfZoom";
import LottieWithLoader from "../primitives/DotLottieReact"; import LottieWithLoader from "../primitives/DotLottieReact";
@@ -170,16 +169,7 @@ function PlaceHolderSign() {
isDragStamp: !!monitor.isDragging() isDragStamp: !!monitor.isDragging()
}) })
}); });
const documentId = docId;
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const objectId =
rowLevel && rowLevel.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
const documentId = docId ? docId : objectId && objectId;
useEffect(() => { useEffect(() => {
if (documentId) { if (documentId) {
getDocumentDetails(); getDocumentDetails();
@@ -454,7 +444,6 @@ function PlaceHolderSign() {
const addPositionOfSignature = (item, monitor) => { const addPositionOfSignature = (item, monitor) => {
getSignerPos(item, monitor); getSignerPos(item, monitor);
}; };
// console.log("signerpos", signerPos);
const getSignerPos = (item, monitor) => { const getSignerPos = (item, monitor) => {
// setSignerObjId(""); // setSignerObjId("");
// setContractName(""); // setContractName("");
@@ -679,6 +668,8 @@ function PlaceHolderSign() {
//function for set and update x and y postion after drag and drop signature tab //function for set and update x and y postion after drag and drop signature tab
const handleStop = (event, dragElement, signerId, key) => { const handleStop = (event, dragElement, signerId, key) => {
setFontColor();
setFontSize();
if (!isResize && isDragging) { if (!isResize && isDragging) {
const dataNewPlace = addZIndex(signerPos, key, setZIndex); const dataNewPlace = addZIndex(signerPos, key, setZIndex);
let updateSignPos = [...signerPos]; let updateSignPos = [...signerPos];
@@ -751,7 +742,6 @@ function PlaceHolderSign() {
setIsDragging(false); setIsDragging(false);
}, 200); }, 200);
}; };
//function for delete signature block //function for delete signature block
const handleDeleteSign = (key, Id) => { const handleDeleteSign = (key, Id) => {
const updateData = []; const updateData = [];
@@ -1007,9 +997,9 @@ function PlaceHolderSign() {
} }
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Document/${documentId}`, )}classes/contracts_Document/${documentId}`,
data, data,
{ {
headers: { headers: {
@@ -1145,12 +1135,7 @@ function PlaceHolderSign() {
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
const documentName = `${pdfDetails?.[0].Name}`; const documentName = `${pdfDetails?.[0].Name}`;
let replaceVar; let replaceVar;
if ( if (requestBody && requestSubject && isCustomize && isSubscribe) {
requestBody &&
requestSubject &&
isCustomize &&
(isSubscribe || !isEnableSubscription)
) {
const replacedRequestBody = requestBody.replace(/"/g, "'"); const replacedRequestBody = requestBody.replace(/"/g, "'");
htmlReqBody = htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
@@ -1218,12 +1203,7 @@ function PlaceHolderSign() {
setMailStatus("success"); setMailStatus("success");
try { try {
let data; let data;
if ( if (requestBody && requestSubject && isCustomize && isSubscribe) {
requestBody &&
requestSubject &&
isCustomize &&
(isSubscribe || !isEnableSubscription)
) {
data = { data = {
RequestBody: htmlReqBody, RequestBody: htmlReqBody,
RequestSubject: requestSubject, RequestSubject: requestSubject,
@@ -1237,9 +1217,9 @@ function PlaceHolderSign() {
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Document/${documentId}`, )}classes/contracts_Document/${documentId}`,
data, data,
{ {
headers: { headers: {
@@ -1488,13 +1468,19 @@ function PlaceHolderSign() {
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: validation:
(isSubscribe || !isEnableSubscription) && inputype isSubscribe && inputype
? { ? {
type: inputype, type: inputype,
pattern: pattern:
inputype === "regex" ? defaultdata.textvalidate : "" inputype === "regex" ? defaultdata.textvalidate : ""
} }
: {} : {},
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
} }
}; };
} else { } else {
@@ -1504,7 +1490,13 @@ function PlaceHolderSign() {
...position.options, ...position.options,
name: defaultdata.name, name: defaultdata.name,
status: defaultdata.status, status: defaultdata.status,
defaultValue: defaultdata.defaultValue defaultValue: defaultdata.defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
} }
}; };
} }
@@ -1528,6 +1520,8 @@ function PlaceHolderSign() {
} }
} }
setCurrWidgetsDetails({}); setCurrWidgetsDetails({});
setFontSize();
setFontColor();
handleNameModal(); handleNameModal();
}; };
@@ -1537,12 +1531,19 @@ function PlaceHolderSign() {
setShowDropdown(false); setShowDropdown(false);
setIsRadio(false); setIsRadio(false);
setIsCheckbox(false); setIsCheckbox(false);
setIsPageCopy(false);
//condition for text widget type after set all values for text widget
//change setUniqueId which is set in tempsignerId
//because textwidget do not have signer user so for selected signers we have to do
if (currWidgetsDetails.type === textWidget) {
setUniqueId(tempSignerId);
setTempSignerId("");
}
}; };
//function for update TourStatus //function for update TourStatus
const closeTour = async () => { const closeTour = async () => {
setPlaceholderTour(false); setPlaceholderTour(false);
if (isDontShow) { if (isDontShow) {
const extUserClass = localStorage.getItem("extended_class");
let updatedTourStatus = []; let updatedTourStatus = [];
if (tourStatus.length > 0) { if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus]; updatedTourStatus = [...tourStatus];
@@ -1561,7 +1562,7 @@ function PlaceHolderSign() {
.put( .put(
`${localStorage.getItem( `${localStorage.getItem(
"baseUrl" "baseUrl"
)}classes/${extUserClass}/${signerUserId}`, )}classes/contracts_Users/${signerUserId}`,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
@@ -1620,13 +1621,11 @@ function PlaceHolderSign() {
setCurrentId(getCurrentUserDeatils[0].Email); setCurrentId(getCurrentUserDeatils[0].Email);
} }
} }
setSignersData(updateSigner); setSignersData(updateSigner);
const index = signersdata.findIndex((x) => x.Id === uniqueId); const index = signersdata.findIndex((x) => x.Id === uniqueId);
setIsSelectId(index); setIsSelectId(index);
} }
}; };
//function to add new signer in document signers list //function to add new signer in document signers list
const handleAddNewRecipients = (data) => { const handleAddNewRecipients = (data) => {
const newId = randomId(); const newId = randomId();
@@ -1689,60 +1688,6 @@ function PlaceHolderSign() {
setSignerPos(updatePlaceholderUser); setSignerPos(updatePlaceholderUser);
setIsMailSend(false); setIsMailSend(false);
}; };
const handleSaveFontSize = () => {
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
if (filterSignerPos.length > 0) {
const getPlaceHolder = filterSignerPos[0].placeHolder;
const getPageNumer = getPlaceHolder.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,
fontColor: fontColor
}
};
}
return position;
});
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === pageNumber) {
return { ...obj, pos: addSignPos };
}
return obj;
});
const newUpdateSigner = signerPos.map((obj) => {
if (obj.Id === uniqueId) {
return { ...obj, placeHolder: newUpdateSignPos };
}
return obj;
});
setSignerPos(newUpdateSigner);
}
}
setFontSize();
setFontColor();
if (currWidgetsDetails.type === textWidget) {
setUniqueId(tempSignerId);
setTempSignerId("");
}
handleTextSettingModal(false);
};
const handleTextSettingModal = (value) => {
setIsTextSetting(value);
};
return ( return (
<> <>
<Title title={state?.title ? state.title : "New Document"} /> <Title title={state?.title ? state.title : "New Document"} />
@@ -1813,7 +1758,6 @@ function PlaceHolderSign() {
: isSendAlert.mssg === "confirm" && "Send Mail" : isSendAlert.mssg === "confirm" && "Send Mail"
} }
handleClose={() => setIsSendAlert({})} handleClose={() => setIsSendAlert({})}
showHeaderMessage={isSendAlert.mssg === "confirm"}
> >
<div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content"> <div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content">
{isSendAlert.mssg === "sure" ? ( {isSendAlert.mssg === "sure" ? (
@@ -1827,24 +1771,20 @@ function PlaceHolderSign() {
</p> </p>
) : ( ) : (
isSendAlert.mssg === "confirm" && ( isSendAlert.mssg === "confirm" && (
<>
<> <>
{!isCustomize && ( {!isCustomize && (
<span> <span>
Are you sure you want to send out this Are you sure you want to send out this document
document for signatures? for signatures?
</span> </span>
)} )}
{isCustomize && {isCustomize && isSubscribe && (
(!isEnableSubscription || isSubscribe) && (
<> <>
<EmailBody <EmailBody
editorRef={editorRef} editorRef={editorRef}
requestBody={requestBody} requestBody={requestBody}
requestSubject={requestSubject} requestSubject={requestSubject}
handleOnchangeRequest={ handleOnchangeRequest={handleOnchangeRequest}
handleOnchangeRequest
}
setRequestSubject={setRequestSubject} setRequestSubject={setRequestSubject}
/> />
<div <div
@@ -1858,11 +1798,7 @@ function PlaceHolderSign() {
</div> </div>
</> </>
)} )}
<div <div className="flex flex-row md:items-center gap-2 md:gap-6 mt-2">
className={
"flex flex-row md:items-center gap-2 md:gap-6 mt-2 "
}
>
<div className="flex flex-row gap-2"> <div className="flex flex-row gap-2">
<button <button
onClick={() => sendEmailToSigners()} onClick={() => sendEmailToSigners()}
@@ -1872,28 +1808,21 @@ function PlaceHolderSign() {
</button> </button>
{isCustomize && ( {isCustomize && (
<button <button
onClick={() => { onClick={() => setIsCustomize(false)}
setIsCustomize(false);
}}
className="op-btn op-btn-ghost font-[500] text-sm" className="op-btn op-btn-ghost font-[500] text-sm"
> >
Close Close
</button> </button>
)} )}
</div> </div>
{!isCustomize && isSubscribe && (
{!isCustomize &&
(isSubscribe || !isEnableSubscription) && (
<span <span
className="op-link op-link-accent text-sm" className="op-link op-link-accent text-sm"
onClick={() => { onClick={() => setIsCustomize(!isCustomize)}
setIsCustomize(!isCustomize);
}}
> >
Cutomize Email Cutomize Email
</span> </span>
)} )}
{!isSubscribe && isEnableSubscription && ( {!isSubscribe && isEnableSubscription && (
<div className="mt-2"> <div className="mt-2">
<Upgrade <Upgrade
@@ -1904,10 +1833,8 @@ function PlaceHolderSign() {
)} )}
</div> </div>
</> </>
</>
) )
)} )}
{isSendAlert.mssg === "confirm" && ( {isSendAlert.mssg === "confirm" && (
<> <>
<div className="flex justify-center items-center mt-3"> <div className="flex justify-center items-center mt-3">
@@ -1948,6 +1875,13 @@ function PlaceHolderSign() {
{!mailStatus && ( {!mailStatus && (
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div> <div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
)} )}
<div
className={
mailStatus === "success"
? "flex justify-center mt-1"
: ""
}
>
{isCurrUser && ( {isCurrUser && (
<button <button
onClick={() => { onClick={() => {
@@ -1972,6 +1906,7 @@ function PlaceHolderSign() {
{isCurrUser ? "No" : "Close"} {isCurrUser ? "No" : "Close"}
</button> </button>
</div> </div>
</div>
</ModalUi> </ModalUi>
<ModalUi <ModalUi
isOpen={isShowEmail} isOpen={isShowEmail}
@@ -2095,10 +2030,10 @@ function PlaceHolderSign() {
handleNameModal={setIsNameModal} handleNameModal={setIsNameModal}
setTempSignerId={setTempSignerId} setTempSignerId={setTempSignerId}
uniqueId={uniqueId} uniqueId={uniqueId}
handleTextSettingModal={handleTextSettingModal}
pdfOriginalWH={pdfOriginalWH} pdfOriginalWH={pdfOriginalWH}
setScale={setScale} setScale={setScale}
scale={scale} scale={scale}
setIsSelectId={setIsSelectId}
/> />
)} )}
</div> </div>
@@ -2142,6 +2077,7 @@ function PlaceHolderSign() {
setBlockColor={setBlockColor} setBlockColor={setBlockColor}
setIsAddSigner={setIsAddSigner} setIsAddSigner={setIsAddSigner}
handleDeleteUser={handleDeleteUser} handleDeleteUser={handleDeleteUser}
uniqueId={uniqueId}
/> />
</div> </div>
) : ( ) : (
@@ -2151,6 +2087,7 @@ function PlaceHolderSign() {
aria-disabled aria-disabled
> >
<SignerListPlace <SignerListPlace
setSignerPos={setSignerPos}
signerPos={signerPos} signerPos={signerPos}
signersdata={signersdata} signersdata={signersdata}
isSelectListId={isSelectListId} isSelectListId={isSelectListId}
@@ -2167,6 +2104,7 @@ function PlaceHolderSign() {
setIsAddSigner={setIsAddSigner} setIsAddSigner={setIsAddSigner}
handleDeleteUser={handleDeleteUser} handleDeleteUser={handleDeleteUser}
roleName={roleName} roleName={roleName}
uniqueId={uniqueId}
// handleAddSigner={handleAddSigner} // handleAddSigner={handleAddSigner}
/> />
<div data-tut="addWidgets"> <div data-tut="addWidgets">
@@ -2211,17 +2149,6 @@ function PlaceHolderSign() {
</button> </button>
</div> </div>
</ModalUi> </ModalUi>
<TextFontSetting
isTextSetting={isTextSetting}
setIsTextSetting={setIsTextSetting}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
handleSaveFontSize={handleSaveFontSize}
currWidgetsDetails={currWidgetsDetails}
/>
<LinkUserModal <LinkUserModal
handleAddUser={handleAddUser} handleAddUser={handleAddUser}
isAddUser={isAddUser} isAddUser={isAddUser}
@@ -2242,6 +2169,12 @@ function PlaceHolderSign() {
handleClose={handleNameModal} handleClose={handleNameModal}
handleData={handleWidgetdefaultdata} handleData={handleWidgetdefaultdata}
isSubscribe={isSubscribe} isSubscribe={isSubscribe}
isTextSetting={isTextSetting}
setIsTextSetting={setIsTextSetting}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/> />
</DndProvider> </DndProvider>
</> </>
+61 -37
View File
@@ -1,11 +1,12 @@
import React, { useState, useEffect } from "react"; 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 checkmark from "../assets/images/checkmark.png";
import plansArr from "../json/plansArr"; import plansArr, { stagingPlan } from "../json/plansArr";
import Title from "../components/Title"; import Title from "../components/Title";
import Parse from "parse"; import Parse from "parse";
import { openInNewTab } from "../constant/Utils"; import { openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import { isStaging } from "../constant/const";
const listItemStyle = { const listItemStyle = {
paddingLeft: "20px", // Add padding to create space for the image paddingLeft: "20px", // Add padding to create space for the image
backgroundImage: `url(${checkmark})`, // Set your image as the list style image backgroundImage: `url(${checkmark})`, // Set your image as the list style image
@@ -16,7 +17,8 @@ const listItemStyle = {
const PlanSubscriptions = () => { const PlanSubscriptions = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(false); const isTeamPlan = plansArr.some((x) => x.planName === "OPENSIGN™ TEAMS");
const [yearlyVisible, setYearlyVisible] = useState(true);
const [isLoader, setIsLoader] = useState(true); const [isLoader, setIsLoader] = useState(true);
const extUser = const extUser =
localStorage.getItem("Extand_Class") && localStorage.getItem("Extand_Class") &&
@@ -29,10 +31,15 @@ const PlanSubscriptions = () => {
}; };
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user; const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
// console.log("userDetails ", userDetails); // console.log("userDetails ", userDetails);
const name = const fullname =
userDetails && userDetails.name userDetails && userDetails.name ? userDetails.name.split(" ") : "";
? "first_name=" + encodeURIComponent(userDetails.name) 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 = const email =
userDetails && userDetails.email userDetails && userDetails.email
? "&email=" + encodeURIComponent(userDetails.email) ? "&email=" + encodeURIComponent(userDetails.email)
@@ -53,15 +60,21 @@ const PlanSubscriptions = () => {
company + company +
phone; phone;
useEffect(() => { useEffect(() => {
// if (localStorage.getItem("accesstoken")) {
setIsLoader(false); setIsLoader(false);
// } else { if (!isTeamPlan && isStaging) {
// navigate("/", { replace: true }); plansArr.splice(2, 0, stagingPlan);
// } }
// eslint-disable-next-line // eslint-disable-next-line
}, []); }, []);
const handleFreePlan = async () => { 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); setIsLoader(true);
try { try {
const params = { userId: Parse.User.current().id }; const params = { userId: Parse.User.current().id };
@@ -81,6 +94,7 @@ const PlanSubscriptions = () => {
console.log("err in free subscribe", err.message); console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!"); alert("Somenthing went wrong, please try again later!");
} }
}
}; };
return ( return (
<> <>
@@ -122,19 +136,43 @@ const PlanSubscriptions = () => {
<h3 className="text-[#002862] uppercase"> <h3 className="text-[#002862] uppercase">
{item.planName} {item.planName}
</h3> </h3>
<div className="w-[150px] h-[150px]"> <div className="w-[120px] h-[120px] overflow-hidden">
<img <img
className="icon-basic mx-auto" className="w-full h-full object-contain"
src="https://js.zohostatic.com/books/zfwidgets/assets/images/plan.png" src={require(`../assets/images/${item?.img}`)}
alt="freeimg" alt="img"
/> />
</div> </div>
<div> <div>
<span className="text-3xl"> <span className="text-3xl">
{item.currency && <small>{item.currency}</small>} {item.currency && <small>{item.currency}</small>}
{yearlyVisible {yearlyVisible ? (
? item?.yearlyPrice <>
: item.monthlyPrice} {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> </span>
<p className="font-semibold pt-2 text-sm"> <p className="font-semibold pt-2 text-sm">
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"} {yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
@@ -160,28 +198,14 @@ const PlanSubscriptions = () => {
</div> </div>
</div> </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 <button
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:text-white cursor-pointer" className={`${
onClick={() => handleFreePlan()} 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.btnText} {item?.btn?.text}
</button> </button>
)}
</div> </div>
<hr className="w-full bg-gray-300 h-[0.5px]" /> <hr className="w-full bg-gray-300 h-[0.5px]" />
<ul className="mx-1 p-3 text-left break-words text-sm list-none"> <ul className="mx-1 p-3 text-left break-words text-sm list-none">
+10
View File
@@ -22,6 +22,7 @@ const Report = () => {
const [form, setForm] = useState(""); const [form, setForm] = useState("");
const [tourData, setTourData] = useState([]); const [tourData, setTourData] = useState([]);
const [isDontShow, setIsDontShow] = useState(false); const [isDontShow, setIsDontShow] = useState(false);
const [isPublic, setIsPublic] = useState({});
const abortController = new AbortController(); const abortController = new AbortController();
const docPerPage = 10; const docPerPage = 10;
@@ -156,6 +157,13 @@ const Report = () => {
? [...prevRecord, ...res.data.result] ? [...prevRecord, ...res.data.result]
: 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); setIsLoader(false);
@@ -193,6 +201,8 @@ const Report = () => {
report_help={reporthelp} report_help={reporthelp}
tourData={tourData} tourData={tourData}
isDontShow={isDontShow} isDontShow={isDontShow}
setIsPublic={setIsPublic}
isPublic={isPublic}
/> />
) : ( ) : (
<PageNotFound prefix={"Report"} /> <PageNotFound prefix={"Report"} />
+42 -80
View File
@@ -1,7 +1,6 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import Parse from "parse"; import Parse from "parse";
import axios from "axios";
import { appInfo } from "../constant/appinfo"; import { appInfo } from "../constant/appinfo";
import { isEnableSubscription } from "../constant/const"; import { isEnableSubscription } from "../constant/const";
import { fetchSubscription } from "../constant/Utils"; import { fetchSubscription } from "../constant/Utils";
@@ -136,8 +135,6 @@ const SSOVerify = () => {
}; };
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user // `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
const thirdpartyLoginfn = async (sessionToken) => { const thirdpartyLoginfn = async (sessionToken) => {
const baseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
try { try {
const validUser = await Parse.User.become(sessionToken); const validUser = await Parse.User.become(sessionToken);
if (validUser) { if (validUser) {
@@ -151,76 +148,44 @@ const SSOVerify = () => {
} }
// Check extended class user role and tenentId // Check extended class user role and tenentId
try { try {
let userRoles = [];
const userSettings = appInfo.settings; const userSettings = appInfo.settings;
//Get Current user roles const currentUser = Parse.User.current();
const url = `${baseUrl}functions/UserGroups`; await Parse.Cloud.run("getUserDetails", {
const headers = { email: currentUser.get("email")
"Content-Type": "application/json", })
"X-Parse-Application-Id": parseAppId, .then(async (extUser) => {
sessionToken: _user.sessionToken if (extUser) {
}; const IsDisabled = extUser?.get("IsDisabled") || false;
const body = { appname: appInfo.appname }; if (!IsDisabled) {
const UserGroupsRes = await axios.post(url, JSON.stringify(body), { const userRole = extUser?.get("UserRole");
headers: headers const menu =
}); userRole &&
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || []; userSettings.find((menu) => menu.role === userRole);
if (userRoles) { if (menu) {
let _currentRole = ""; const _currentRole = userRole;
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 = const redirectUrl =
location?.state?.from || location?.state?.from ||
`/${roleSetting.pageType}/${roleSetting.pageId}`; `/${menu.pageType}/${menu.pageId}`;
const _role = _currentRole.replace(`${appInfo.appname}_`, ""); const _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role); localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class const extUser_stringify = JSON.stringify([extUser]);
localStorage.setItem( localStorage.setItem("Extand_Class", extUser_stringify);
"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)); const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", _extUser.Email); localStorage.setItem("userEmail", _extUser.Email);
localStorage.setItem("username", _extUser.Name); localStorage.setItem("username", _extUser.Name);
localStorage.setItem("scriptId", true); localStorage.setItem("scriptId", true);
let tenentInfo = [];
const results = [extUser];
if (results) {
let extendedInfo_stringify = JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
);
if (_extUser.TenantId) { if (_extUser.TenantId) {
const obj = { const tenant = {
tenentId: _extUser.TenantId.objectId, Id: _extUser?.TenantId?.objectId || "",
tenentName: _extUser.TenantId.TenantName || "" Name: _extUser?.TenantId?.TenantName || ""
}; };
localStorage.setItem("TenantId", obj.tenentId); localStorage.setItem("TenantId", tenant?.Id);
tenentInfo.push(obj); dispatch(showTenant(tenant?.Name));
dispatch(showTenant(obj.tenentName || "")); localStorage.setItem("TenantName", tenant?.Name);
localStorage.setItem("TenantName", obj.tenentName || "");
} }
localStorage.setItem("PageLanding", roleSetting.pageId); localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", roleSetting.menuId); localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", roleSetting.pageType); localStorage.setItem("pageType", menu.pageType);
if (isEnableSubscription) { if (isEnableSubscription) {
const res = await fetchSubscription(); const res = await fetchSubscription();
const plan = res.plan; const plan = res.plan;
@@ -232,35 +197,32 @@ const SSOVerify = () => {
localStorage.removeItem("userDetails"); localStorage.removeItem("userDetails");
navigate(redirectUrl); navigate(redirectUrl);
} else { } else {
navigate(`/subscription`, { navigate(`/subscription`, { replace: true });
replace: true
});
} }
} else { } else {
navigate(`/subscription`, { navigate(`/subscription`, { replace: true });
replace: true
});
} }
} else { } else {
navigate(redirectUrl); 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) { }
})
.catch((error) => {
console.error("Err in fetch extuser", error);
alert("user not exist."); alert("user not exist.");
setMessage("Error: User not exist."); setMessage("Error: User not exist.");
console.log("err in get extUser", err); });
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} catch (err) { } catch (err) {
console.log("err in usergroups", err); console.log("err in getuserdetails", err);
} }
} }
} catch (err) { } catch (err) {
+52 -238
View File
@@ -44,14 +44,8 @@ const Signup = () => {
let baseUrl = localStorage.getItem("baseUrl"); let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId"); let appid = localStorage.getItem("parseAppId");
let applogo = localStorage.getItem("appLogo"); 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 defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding"); 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"); let userSettings = localStorage.getItem("userSettings");
localStorage.clear(); localStorage.clear();
@@ -59,14 +53,8 @@ const Signup = () => {
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
localStorage.setItem("appLogo", applogo); localStorage.setItem("appLogo", applogo);
localStorage.setItem("domain", domain);
localStorage.setItem("appversion", appversion);
localStorage.setItem("appTitle", appTitle);
localStorage.setItem("defaultmenuid", defaultmenuid); localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding); localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("_appName", _appName);
localStorage.setItem("_app_objectId", _app_objectId);
localStorage.setItem("appName", appName);
localStorage.setItem("userSettings", userSettings); localStorage.setItem("userSettings", userSettings);
localStorage.setItem("baseUrl", baseUrl); localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid); localStorage.setItem("parseAppId", appid);
@@ -194,8 +182,6 @@ const Signup = () => {
if (res.data) { if (res.data) {
let _user = res.data; let _user = res.data;
localStorage.setItem("UserInformation", JSON.stringify(_user)); localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("userEmail", _user.email);
localStorage.setItem("username", _user.name);
localStorage.setItem("accesstoken", _user.sessionToken); localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true); localStorage.setItem("scriptId", true);
if (_user.ProfilePic) { if (_user.ProfilePic) {
@@ -205,252 +191,84 @@ const Signup = () => {
} }
// Check extended class user role and tenentId // Check extended class user role and tenentId
try { try {
let userRoles = []; const userSettings = appInfo.settings;
if (appInfo.settings) { const currentUser = Parse.User.current();
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
);
await Parse.Cloud.run("getUserDetails", { await Parse.Cloud.run("getUserDetails", {
email: email email: currentUser.get("email")
}).then( })
(result) => { .then(async (extUser) => {
let tenentInfo = []; if (extUser) {
const results = [result]; const IsDisabled = extUser?.get("IsDisabled") || false;
if (results) { if (!IsDisabled) {
let extendedInfo_stringify = const userRole = extUser?.get("UserRole");
JSON.stringify(results); const menu =
localStorage.setItem( userRole &&
"Extand_Class", userSettings.find((menu) => menu.role === userRole);
extendedInfo_stringify if (menu) {
); const _currentRole = userRole;
let extendedInfo = JSON.parse( const _role = _currentRole.replace("contracts_", "");
extendedInfo_stringify localStorage.setItem("_user_role", _role);
); const extInfo_stringify = JSON.stringify([extUser]);
if (extendedInfo.length > 1) { localStorage.setItem("Extand_Class", extInfo_stringify);
extendedInfo.forEach((x) => { const extInfo = JSON.parse(JSON.stringify(extUser));
if (x.TenantId) { localStorage.setItem("userEmail", extInfo?.Email);
let obj = { localStorage.setItem("username", extInfo?.Name);
tenentId: x.TenantId.objectId, if (extInfo?.TenantId) {
tenentName: x.TenantId.TenantName || "" const tenant = {
Id: extInfo?.TenantId?.objectId || "",
Name: extInfo?.TenantId?.TenantName || ""
}; };
tenentInfo.push(obj); localStorage.setItem("TenantId", tenant?.Id);
dispatch(showTenant(tenant?.Name));
localStorage.setItem("TenantName", tenant?.Name);
} }
}); localStorage.setItem("PageLanding", menu.pageId);
if (tenentInfo.length) { localStorage.setItem("defaultmenuid", menu.menuId);
dispatch( localStorage.setItem("pageType", menu.pageType);
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 }); setState({ loading: false });
if (isEnableSubscription) { if (isEnableSubscription) {
if (isFreeplan) { if (isFreeplan) {
navigate( navigate(`/${menu.pageType}/${menu.pageId}`);
`/${element.pageType}/${element.pageId}`
);
} else { } else {
navigate(`/subscription`, { replace: true }); navigate(`/subscription`, { replace: true });
} }
} else { } else {
alert("Registered user successfully"); alert("Registered user successfully");
navigate( navigate(`/${menu.pageType}/${menu.pageId}`);
`/${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);
}
);
}
});
} else { } else {
setState({ setState({
loading: false, loading: false,
alertType: "danger", 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); }, 2000);
} }
} else { } else {
setState({ setState({
loading: false, loading: false,
alertType: "danger", 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); }, 2000);
} }
}
}) })
.catch((err) => { .catch((error) => {
console.log("err", err); console.log("error in fetch extuser", error);
setState({ setState({
loading: false, loading: false,
alertType: "danger", alertType: "danger",
alertMsg: alertMsg: "You don't have access, please contact the admin."
"Does not have permissions to access this application!"
});
setTimeout(function () {
setState({
loading: false,
alertType: "danger",
alertMsg: ""
}); });
setTimeout(() => {
setState({ loading: false, alertMsg: "" });
}, 2000); }, 2000);
}); });
}
} catch (error) { } catch (error) {
// alert(`${error.message}`); // alert(`${error.message}`);
setState({ setState({
@@ -458,12 +276,8 @@ const Signup = () => {
alertType: "danger", alertType: "danger",
alertMsg: `${error.message}` alertMsg: `${error.message}`
}); });
setTimeout(function () { setTimeout(() => {
setState({ setState({ loading: false, alertMsg: "" });
loading: false,
alertType: "danger",
alertMsg: ""
});
}, 2000); }, 2000);
console.log(error); console.log(error);
} }
@@ -477,9 +291,9 @@ const Signup = () => {
const saveLogo = async () => { const saveLogo = async () => {
if (isEnableSubscription) { if (isEnableSubscription) {
const logo = await getAppLogo(); const app = await getAppLogo();
if (logo) { if (app?.logo) {
setImage(logo); setImage(app?.logo);
} else { } else {
setImage(appInfo?.applogo || undefined); setImage(appInfo?.applogo || undefined);
} }
@@ -601,9 +415,9 @@ const Signup = () => {
onClick={togglePasswordVisibility} onClick={togglePasswordVisibility}
> >
{showPassword ? ( {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> </span>
</div> </div>
+15 -13
View File
@@ -2,7 +2,6 @@ import React, { useState, useRef, useEffect } from "react";
import { PDFDocument } from "pdf-lib"; import { PDFDocument } from "pdf-lib";
import "../styles/signature.css"; import "../styles/signature.css";
import Parse from "parse"; import Parse from "parse";
import { isEnableSubscription } from "../constant/const";
import Confetti from "react-confetti"; import Confetti from "react-confetti";
import axios from "axios"; import axios from "axios";
import LoaderWithMsg from "../primitives/LoaderWithMsg"; import LoaderWithMsg from "../primitives/LoaderWithMsg";
@@ -71,8 +70,8 @@ function SignYourSelf() {
const signRef = useRef(null); const signRef = useRef(null);
const dragRef = useRef(null); const dragRef = useRef(null);
const [dragKey, setDragKey] = useState(); const [dragKey, setDragKey] = useState();
const [fontSize, setFontSize] = useState(11); const [fontSize, setFontSize] = useState();
const [fontColor, setFontColor] = useState("black"); const [fontColor, setFontColor] = useState();
const [signKey, setSignKey] = useState(); const [signKey, setSignKey] = useState();
const [imgWH, setImgWH] = useState({}); const [imgWH, setImgWH] = useState({});
const [pdfNewWidth, setPdfNewWidth] = useState(); const [pdfNewWidth, setPdfNewWidth] = useState();
@@ -264,9 +263,9 @@ function SignYourSelf() {
} }
await axios await axios
.get( .get(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${ )}classes/contracts_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${
jsonSender.objectId jsonSender.objectId
}"}}`, }"}}`,
{ {
@@ -739,7 +738,7 @@ function SignYourSelf() {
if ( if (
tenantDetails?.CompletionBody && tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject && tenantDetails?.CompletionSubject &&
(!isEnableSubscription || getIsSubscribe) getIsSubscribe
) { ) {
isCustomCompletionMail = true; isCustomCompletionMail = true;
} }
@@ -809,6 +808,8 @@ function SignYourSelf() {
}; };
//function for set and update x and y postion after drag and drop signature tab //function for set and update x and y postion after drag and drop signature tab
const handleStop = (event, dragElement) => { const handleStop = (event, dragElement) => {
setFontSize();
setFontColor();
if (isDragging && dragElement) { if (isDragging && dragElement) {
event.preventDefault(); event.preventDefault();
const getPdfPageWidth = pdfOriginalWH.find( const getPdfPageWidth = pdfOriginalWH.find(
@@ -1028,9 +1029,9 @@ function SignYourSelf() {
} }
await axios await axios
.put( .put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem( `${localStorage.getItem(
"_appName" "baseUrl"
)}${contractName}/${signerUserId}`, )}classes/contracts${contractName}/${signerUserId}`,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
@@ -1134,8 +1135,10 @@ function SignYourSelf() {
...position, ...position,
options: { options: {
...position.options, ...position.options,
fontSize: fontSize || currWidgetsDetails?.options?.fontSize, fontSize:
fontColor: fontColor || currWidgetsDetails?.options?.fontColor fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor || currWidgetsDetails?.options?.fontColor || "black"
} }
}; };
} }
@@ -1153,7 +1156,6 @@ function SignYourSelf() {
handleTextSettingModal(false); handleTextSettingModal(false);
} }
}; };
return ( return (
<DndProvider backend={HTML5Backend}> <DndProvider backend={HTML5Backend}>
<Title title={"Self Sign"} /> <Title title={"Self Sign"} />
+465
View File
@@ -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,
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;
+34 -72
View File
@@ -38,7 +38,6 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption"; import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import Parse from "parse"; import Parse from "parse";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import TextFontSetting from "../components/pdf/TextFontSetting";
import PdfZoom from "../components/pdf/PdfZoom"; import PdfZoom from "../components/pdf/PdfZoom";
const TemplatePlaceholder = () => { const TemplatePlaceholder = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@@ -389,7 +388,6 @@ const TemplatePlaceholder = () => {
(dragTypeValue === "stamp" || dragTypeValue === "image") && true, (dragTypeValue === "stamp" || dragTypeValue === "image") && true,
key: key, key: key,
scale: containerScale, scale: containerScale,
// isMobile: isMobile,
zIndex: posZIndex, zIndex: posZIndex,
type: dragTypeValue, type: dragTypeValue,
options: addWidgetOptions(dragTypeValue), options: addWidgetOptions(dragTypeValue),
@@ -501,6 +499,8 @@ const TemplatePlaceholder = () => {
setShowDropdown(true); setShowDropdown(true);
} else if (dragTypeValue === "checkbox") { } else if (dragTypeValue === "checkbox") {
setIsCheckbox(true); setIsCheckbox(true);
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
} else if ( } else if (
[textInputWidget, "name", "company", "job title", "email"].includes( [textInputWidget, "name", "company", "job title", "email"].includes(
dragTypeValue dragTypeValue
@@ -508,8 +508,6 @@ const TemplatePlaceholder = () => {
) { ) {
setFontSize(12); setFontSize(12);
setFontColor("black"); setFontColor("black");
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
} }
setCurrWidgetsDetails({}); setCurrWidgetsDetails({});
setWidgetType(dragTypeValue); setWidgetType(dragTypeValue);
@@ -555,6 +553,8 @@ const TemplatePlaceholder = () => {
//function for set and update x and y postion after drag and drop signature tab //function for set and update x and y postion after drag and drop signature tab
const handleStop = (event, dragElement, signerId, key) => { const handleStop = (event, dragElement, signerId, key) => {
setFontColor();
setFontSize();
if (!isResize && isDragging) { if (!isResize && isDragging) {
const dataNewPlace = addZIndex(signerPos, key, setZIndex); const dataNewPlace = addZIndex(signerPos, key, setZIndex);
let updateSignPos = [...signerPos]; let updateSignPos = [...signerPos];
@@ -615,7 +615,6 @@ const TemplatePlaceholder = () => {
setIsDragging(false); setIsDragging(false);
}, 200); }, 200);
}; };
//function for delete signature block //function for delete signature block
const handleDeleteSign = (key, Id) => { const handleDeleteSign = (key, Id) => {
const updateData = []; const updateData = [];
@@ -851,7 +850,6 @@ const TemplatePlaceholder = () => {
const closeTour = async () => { const closeTour = async () => {
setTemplateTour(false); setTemplateTour(false);
if (isDontShow) { if (isDontShow) {
const extUserClass = localStorage.getItem("extended_class");
let updatedTourStatus = []; let updatedTourStatus = [];
if (tourStatus.length > 0) { if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus]; updatedTourStatus = [...tourStatus];
@@ -870,7 +868,7 @@ const TemplatePlaceholder = () => {
.put( .put(
`${localStorage.getItem( `${localStorage.getItem(
"baseUrl" "baseUrl"
)}classes/${extUserClass}/${signerUserId}`, )}classes/contracts_Users/${signerUserId}`,
{ {
TourStatus: updatedTourStatus TourStatus: updatedTourStatus
}, },
@@ -1199,13 +1197,19 @@ const TemplatePlaceholder = () => {
hint: defaultdata?.hint || "", hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "", defaultValue: defaultdata?.defaultValue || "",
validation: validation:
(isSubscribe || !isEnableSubscription) && inputype isSubscribe && inputype
? { ? {
type: inputype, type: inputype,
pattern: pattern:
inputype === "regex" ? defaultdata.textvalidate : "" inputype === "regex" ? defaultdata.textvalidate : ""
} }
: {} : {},
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
} }
}; };
} else { } else {
@@ -1215,7 +1219,13 @@ const TemplatePlaceholder = () => {
...position.options, ...position.options,
name: defaultdata.name, name: defaultdata.name,
status: defaultdata.status, status: defaultdata.status,
defaultValue: defaultdata.defaultValue defaultValue: defaultdata.defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
} }
}; };
} }
@@ -1238,66 +1248,19 @@ const TemplatePlaceholder = () => {
setSignerPos(newUpdateSigner); setSignerPos(newUpdateSigner);
} }
} }
setCurrWidgetsDetails({});
handleNameModal();
};
const handleNameModal = () => {
setIsNameModal(false);
setCurrWidgetsDetails({});
setShowDropdown(false);
setIsRadio(false);
setIsCheckbox(false);
};
const handleSaveFontSize = () => {
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
if (filterSignerPos.length > 0) {
const getPlaceHolder = filterSignerPos[0].placeHolder;
const getPageNumer = getPlaceHolder.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,
fontColor: fontColor
}
};
}
return position;
});
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === pageNumber) {
return { ...obj, pos: addSignPos };
}
return obj;
});
const newUpdateSigner = signerPos.map((obj) => {
if (obj.Id === uniqueId) {
return { ...obj, placeHolder: newUpdateSignPos };
}
return obj;
});
setSignerPos(newUpdateSigner);
}
}
setFontSize(); setFontSize();
setFontColor(); setFontColor();
setCurrWidgetsDetails({});
handleNameModal();
};
const handleNameModal = () => {
setIsNameModal(false);
setCurrWidgetsDetails({});
setShowDropdown(false);
setIsRadio(false);
setIsCheckbox(false);
};
handleTextSettingModal(false);
};
const handleTextSettingModal = (value) => {
setIsTextSetting(value);
};
return ( return (
<> <>
<Title title={"Template"} /> <Title title={"Template"} />
@@ -1399,6 +1362,7 @@ const TemplatePlaceholder = () => {
<button <button
onClick={() => { onClick={() => {
setIsCreateDocModal(false); setIsCreateDocModal(false);
navigate("/report/6TeaPr321t");
}} }}
type="button" type="button"
className="op-btn op-btn-secondary ml-2" className="op-btn op-btn-secondary ml-2"
@@ -1532,10 +1496,10 @@ const TemplatePlaceholder = () => {
selectWidgetId={selectWidgetId} selectWidgetId={selectWidgetId}
setIsCheckbox={setIsCheckbox} setIsCheckbox={setIsCheckbox}
handleNameModal={setIsNameModal} handleNameModal={setIsNameModal}
handleTextSettingModal={handleTextSettingModal}
pdfOriginalWH={pdfOriginalWH} pdfOriginalWH={pdfOriginalWH}
setScale={setScale} setScale={setScale}
scale={scale} scale={scale}
setIsSelectId={setIsSelectId}
/> />
)} )}
</div> </div>
@@ -1591,6 +1555,7 @@ const TemplatePlaceholder = () => {
<SignerListPlace <SignerListPlace
isMailSend={isMailSend} isMailSend={isMailSend}
signerPos={signerPos} signerPos={signerPos}
setSignerPos={setSignerPos}
signersdata={signersdata} signersdata={signersdata}
isSelectListId={isSelectListId} isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId} setSignerObjId={setSignerObjId}
@@ -1607,6 +1572,7 @@ const TemplatePlaceholder = () => {
setSignersData={setSignersData} setSignersData={setSignersData}
blockColor={blockColor} blockColor={blockColor}
setBlockColor={setBlockColor} setBlockColor={setBlockColor}
uniqueId={uniqueId}
/> />
<div data-tut="addWidgets"> <div data-tut="addWidgets">
<WidgetComponent <WidgetComponent
@@ -1669,16 +1635,12 @@ const TemplatePlaceholder = () => {
handleClose={handleNameModal} handleClose={handleNameModal}
handleData={handleWidgetdefaultdata} handleData={handleWidgetdefaultdata}
isSubscribe={isSubscribe} isSubscribe={isSubscribe}
/>
<TextFontSetting
isTextSetting={isTextSetting} isTextSetting={isTextSetting}
setIsTextSetting={setIsTextSetting} setIsTextSetting={setIsTextSetting}
fontSize={fontSize} fontSize={fontSize}
setFontSize={setFontSize} setFontSize={setFontSize}
fontColor={fontColor} fontColor={fontColor}
setFontColor={setFontColor} setFontColor={setFontColor}
handleSaveFontSize={handleSaveFontSize}
currWidgetsDetails={currWidgetsDetails}
/> />
</DndProvider> </DndProvider>
</> </>
@@ -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&apos;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;
+426
View File
@@ -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;
+142 -70
View File
@@ -1,8 +1,4 @@
import React, { import React, { useState, useEffect, useRef } from "react";
useState,
useEffect
// useRef
} from "react";
import { Navigate, useNavigate } from "react-router-dom"; import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse"; import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize"; import { SaveFileSize } from "../constant/saveFileSize";
@@ -10,7 +6,6 @@ import dp from "../assets/images/dp.png";
import Title from "../components/Title"; import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName"; import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios"; import axios from "axios";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription } from "../constant/const"; import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils"; import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
@@ -32,10 +27,11 @@ function UserProfile() {
const [percentage, setpercentage] = useState(0); const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false); const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false); const [isSubscribe, setIsSubscribe] = useState(false);
// const [publicUserName, setPublicUserName] = useState( const [isUpgrade, setIsUpgrade] = useState(false);
// extendUser && extendUser?.[0]?.UserName const [publicUserName, setPublicUserName] = useState(
// ); extendUser && extendUser?.[0]?.UserName
// const previousPublicUserName = useRef(publicUserName); );
const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState( const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company extendUser && extendUser?.[0]?.Company
); );
@@ -46,7 +42,10 @@ function UserProfile() {
const [otp, setOtp] = useState(""); const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false); const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false); const [isEmailVerified, setIsEmailVerified] = useState(false);
// const [userNameError, setUserNameError] = useState(""); const [userNameError, setUserNameError] = useState("");
const [tagLine, setTagLine] = useState(
extendUser && extendUser?.[0]?.Tagline
);
useEffect(() => { useEffect(() => {
getUserDetail(); getUserDetail();
}, []); }, []);
@@ -85,31 +84,31 @@ function UserProfile() {
} }
}; };
//function to check public username already exist //function to check public username already exist
// const handleCheckPublicUserName = async () => { const handleCheckPublicUserName = async () => {
// try { try {
// const res = await Parse.Cloud.run("getpublicusername", { const res = await Parse.Cloud.run("getpublicusername", {
// username: publicUserName username: publicUserName
// }); });
// if (res) { if (res) {
// setIsLoader(false); setIsLoader(false);
// setUserNameError("user name already exist"); setUserNameError("user name already exist");
// setTimeout(() => { setTimeout(() => {
// setUserNameError(""); setUserNameError("");
// }, 3000); }, 3000);
// return res; return res;
// } }
// } catch (e) { } catch (e) {
// console.log("error in getpublicusername cloud function"); console.log("error in getpublicusername cloud function");
// } }
// }; };
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
let phn = Phone, let phn = Phone,
res = ""; res = "";
//condition to call cloud function when user change publicUserName //condition to call cloud function when user change publicUserName
// if (previousPublicUserName.current !== publicUserName) { if (publicUserName && previousPublicUserName.current !== publicUserName) {
// res = await handleCheckPublicUserName(); res = await handleCheckPublicUserName();
// } }
if (!res) { if (!res) {
setIsLoader(true); setIsLoader(true);
try { try {
@@ -155,7 +154,6 @@ function UserProfile() {
// `updateExtUser` is used to update user details in extended class // `updateExtUser` is used to update user details in extended class
const updateExtUser = async (obj) => { const updateExtUser = async (obj) => {
const extClass = localStorage.getItem("extended_class");
const extData = JSON.parse(localStorage.getItem("Extand_Class")); const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId; const ExtUserId = extData[0].objectId;
const body = { const body = {
@@ -163,12 +161,13 @@ function UserProfile() {
Name: obj.Name, Name: obj.Name,
HeaderDocId: isDisableDocId, HeaderDocId: isDisableDocId,
JobTitle: jobTitle, JobTitle: jobTitle,
Company: company Company: company,
// UserName: publicUserName || "" UserName: publicUserName || "",
Tagline: tagLine || ""
}; };
await axios.put( await axios.put(
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId, parseBaseUrl + "classes/contracts_Users/" + ExtUserId,
body, body,
{ {
headers: { headers: {
@@ -185,6 +184,7 @@ function UserProfile() {
const json = JSON.parse(JSON.stringify([res])); const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json); const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes); localStorage.setItem("Extand_Class", extRes);
previousPublicUserName.current = publicUserName;
// console.log("updateRes ", updateRes); // console.log("updateRes ", updateRes);
}; };
// file upload function // file upload function
@@ -278,23 +278,20 @@ function UserProfile() {
alert("OTP sent on you email"); alert("OTP sent on you email");
}; };
//function to handle onchange username and restrict 6-characters username for free users //function to handle onchange username and restrict 6-characters username for free users
// const handleOnchangeUserName = (e) => { const handleOnchangeUserName = (e) => {
// const value = e.target.value; const newValue = e.target.value.replace(/\s+/g, "");
// if (value.length > 6 && !isSubscribe) { setPublicUserName(newValue);
// setUserNameError("Please upgrade to allow more than 6 characters."); };
// setTimeout(() => {
// setUserNameError(""); const handleOnchangeTagLine = (e) => {
// }, 2000); setTagLine(e.target.value);
// } else { };
// setPublicUserName(e.target.value);
// }
// };
const handleCancel = () => { const handleCancel = () => {
setEditMode(false); setEditMode(false);
SetName(localStorage.getItem("username")); SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone); SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg")); setImage(localStorage.getItem("profileImg"));
// setPublicUserName(extendUser && extendUser?.[0]?.UserName); setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company); setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle); setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId); setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
@@ -308,13 +305,13 @@ function UserProfile() {
</div> </div>
) : ( ) : (
<div className="flex justify-center items-center w-full relative"> <div className="flex justify-center items-center w-full relative">
{/* {userNameError && ( {userNameError && (
<div <div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`} className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
> >
{userNameError} {userNameError}
</div> </div>
)} */} )}
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box 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="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full"> <div className="w-[200px] h-[200px] overflow-hidden rounded-full">
@@ -442,20 +439,28 @@ function UserProfile() {
)} )}
</span> </span>
</li> </li>
{/* {isEnableSubscription && ( {isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all"> <>
<span className="font-semibold"> <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 :{" "} Public profile :{" "}
<Tooltip <Tooltip
message={`this is your public URL. Copy or share it maxWidth="max-w-[250px]"
with the signer, and you will be able to see 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.`}
all your publicly set templates.`}
/> />
</span> </span>
<div className="flex text-xs items-center"> <div className="flex md:flex-row flex-col md:items-center">
<span>opensign-me.vercel.app/</span> <span className="mb-1 md:mb-1">opensign.me/</span>
{editmode ? ( {editmode ? (
<input <input
maxLength={40}
style={{
border:
!isSubscribe &&
publicUserName?.length > 0 &&
publicUserName?.length < 9 &&
"solid red"
}}
onChange={handleOnchangeUserName} onChange={handleOnchangeUserName}
value={publicUserName} value={publicUserName}
disabled={!editmode} disabled={!editmode}
@@ -472,12 +477,41 @@ function UserProfile() {
)} )}
</div> </div>
</li> </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"> <li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2"> <div className="flex justify-between items-center py-2">
<span <span
className={ className={
isSubscribe || !isEnableSubscription isSubscribe
? "font-semibold" ? "font-semibold"
: "font-semibold text-gray-300" : "font-semibold text-gray-300"
} }
@@ -492,7 +526,7 @@ function UserProfile() {
</span> </span>
<label <label
className={`${ className={`${
isSubscribe || !isEnableSubscription isSubscribe
? `${editmode ? "cursor-pointer" : ""}` ? `${editmode ? "cursor-pointer" : ""}`
: "pointer-events-none opacity-50" : "pointer-events-none opacity-50"
} relative block items-center mb-0`} } relative block items-center mb-0`}
@@ -506,21 +540,27 @@ function UserProfile() {
/> />
</label> </label>
</div> </div>
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Disable documentId is free in beta, this feature will incur a fee later."
}
/>
)}
</li> </li>
)}
</ul> </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 <button
type="button" type="button"
onClick={(e) => onClick={(e) => {
editmode ? handleSubmit(e) : setEditMode(true) if (
!isSubscribe &&
publicUserName?.length > 0 &&
publicUserName?.length < 9
) {
setIsUpgrade(true);
} else {
editmode ? handleSubmit(e) : setEditMode(true);
} }
}}
className="op-btn op-btn-primary" className="op-btn op-btn-primary"
> >
{editmode ? "Save" : "Edit"} {editmode ? "Save" : "Edit"}
@@ -577,6 +617,38 @@ function UserProfile() {
)} )}
</ModalUi> </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> </div>
)} )}
</React.Fragment> </React.Fragment>
-2
View File
@@ -6,7 +6,6 @@ import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const"; import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, openInNewTab } from "../constant/Utils"; import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
import Parse from "parse"; import Parse from "parse";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip"; import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader"; import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard"; import SubscribeCard from "../primitives/SubscribeCard";
@@ -107,7 +106,6 @@ function Webhook() {
) : ( ) : (
<> <>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3"> <div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}> <h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign™ Webhook{" "} OpenSign™ Webhook{" "}
<Tooltip <Tooltip
@@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import Parse from "parse"; import Parse from "parse";
import axios from "axios";
import Loader from "./Loader"; import Loader from "./Loader";
const AddContact = (props) => { const AddContact = (props) => {
const [name, setName] = useState(""); const [name, setName] = useState("");
@@ -9,8 +8,6 @@ const AddContact = (props) => {
const [addYourself, setAddYourself] = useState(false); const [addYourself, setAddYourself] = useState(false);
const [isLoader, setIsLoader] = useState(false); const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false); const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
useEffect(() => { useEffect(() => {
checkUserExist(); checkUserExist();
}, []); }, []);
@@ -77,18 +74,6 @@ const AddContact = (props) => {
const user = await _user.save(); const user = await _user.save();
if (user) { 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(); const currentUser = Parse.User.current();
contactQuery.set( contactQuery.set(
"CreatedBy", "CreatedBy",
@@ -124,18 +109,6 @@ const AddContact = (props) => {
if (err.code === 202) { if (err.code === 202) {
const params = { email: email }; const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params); 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(); const currentUser = Parse.User.current();
contactQuery.set( contactQuery.set(
"CreatedBy", "CreatedBy",
@@ -30,8 +30,7 @@ const LottieWithLoader = () => {
return ( return (
<div> <div>
{!isLoaded && !hasError && ( {!isLoaded && !hasError && (
<div className="loader"> <div className="w-[120px] h-[120px] mx-auto">
{" "}
<Loader /> <Loader />
</div> </div>
)} )}
@@ -39,9 +38,8 @@ const LottieWithLoader = () => {
{isLoaded && animationSrc && ( {isLoaded && animationSrc && (
<DotLottieReact <DotLottieReact
src={animationSrc} src={animationSrc}
loop
autoplay autoplay
style={{ display: "block" }} className="w-[120px] h-[120px] md:w-[200px] md:h-[200px] mx-auto"
/> />
)} )}
</div> </div>
File diff suppressed because it is too large Load Diff
+1 -11
View File
@@ -1,6 +1,4 @@
import React from "react"; import React from "react";
import { isEnableSubscription } from "../constant/const";
import PremiumAlertHeader from "./PremiumAlertHeader";
const ModalUi = ({ const ModalUi = ({
children, children,
@@ -9,8 +7,7 @@ const ModalUi = ({
handleClose, handleClose,
showHeader = true, showHeader = true,
showClose = true, showClose = true,
reduceWidth, reduceWidth
showHeaderMessage
}) => { }) => {
const width = reduceWidth; const width = reduceWidth;
return ( return (
@@ -37,13 +34,6 @@ const ModalUi = ({
)} )}
</> </>
)} )}
{!isEnableSubscription && showHeaderMessage && (
<PremiumAlertHeader
message={
"Cutomize Email is free in beta, this feature will incur a fee later."
}
/>
)}
<div>{children}</div> <div>{children}</div>
</div> </div>
</dialog> </dialog>
@@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
import "../styles/signature.css"; import "../styles/signature.css";
function CustomModal({ function CustomModal({
@@ -9,11 +9,12 @@ function CustomModal({
declineDoc, declineDoc,
setIsDecline setIsDecline
}) { }) {
const [reason, setReason] = useState("");
return ( return (
show && ( show && (
<dialog className="op-modal op-modal-open absolute z-[998]"> <dialog className="op-modal op-modal-open absolute z-[448]">
<div className="w-[80%] md:w-[40%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm"> <div className="w-[95%] md:w-[60%] lg:w-[40%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="text-[#de4337] font-bold text-lg pt-[15px] px-[20px]"> <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
{headMsg && headMsg} {headMsg && headMsg}
</h3> </h3>
<div className="p-[10px] px-[20px]"> <div className="p-[10px] px-[20px]">
@@ -21,19 +22,34 @@ function CustomModal({
</div> </div>
{footerMessage && ( {footerMessage && (
<> <>
<div className="h-[1px] bg-[#9f9f9f] w-full"></div> <div className="mx-3">
<textarea
rows={3}
placeholder="Reason (optional)"
className="px-4 op-textarea op-textarea-bordered focus:outline-none hover:border-base-content w-full text-xs"
value={reason}
onChange={(e) => setReason(e.target.value)}
></textarea>
</div>
{/* <div className="h-[1px] bg-[#9f9f9f] w-full"></div> */}
<div className="m-[15px]"> <div className="m-[15px]">
<button <button
className="op-btn op-btn-primary mr-2" className="op-btn op-btn-primary mr-2 px-6"
type="button" type="button"
onClick={() => declineDoc()} onClick={() => {
setReason("");
declineDoc(reason);
}}
> >
Yes Yes
</button> </button>
<button <button
type="button" type="button"
className="op-btn op-btn-secondary" className="op-btn op-btn-secondary"
onClick={() => setIsDecline({ isDeclined: false })} onClick={() => {
setReason("");
setIsDecline({ isDeclined: false });
}}
> >
Close Close
</button> </button>
@@ -1,11 +0,0 @@
import React from "react";
const PremiumAlertHeader = ({ message }) => (
<marquee className="bg-yellow-300 text-black px-2">
{message
? message
: "Currently free in Beta, this feature will incur a fee later."}
</marquee>
);
export default PremiumAlertHeader;
@@ -1,13 +1,16 @@
import React from "react"; import React from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
const SubscribeCard = () => { const SubscribeCard = ({ plan, price }) => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg"> <div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body"> <div className="op-card-body">
<h2 className="op-card-title">Upgrade to PRO Plan</h2> <h2 className="op-card-title">Upgrade to {plan ? plan : "PRO"} Plan</h2>
<p>$29.99/month: Sign 100 docs using API. Only $0.15/doc after that.</p> <p>
${price ? price : "29.99"}/month: Sign 100 docs using API. Only
$0.15/doc after that.
</p>
<div className="op-card-actions justify-end"> <div className="op-card-actions justify-end">
<button <button
onClick={() => navigate("/subscription")} onClick={() => navigate("/subscription")}
+2 -2
View File
@@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { Tooltip as ReactTooltip } from "react-tooltip"; import { Tooltip as ReactTooltip } from "react-tooltip";
import { openInNewTab } from "../constant/Utils"; import { openInNewTab } from "../constant/Utils";
const Tooltip = ({ id, message, url, iconColor }) => const Tooltip = ({ id, message, url, iconColor, maxWidth }) =>
url ? ( url ? (
<button onClick={() => openInNewTab(url)} className={"text-center"}> <button onClick={() => openInNewTab(url)} className={"text-center"}>
<sup> <sup>
@@ -33,7 +33,7 @@ const Tooltip = ({ id, message, url, iconColor }) =>
</a> </a>
<ReactTooltip <ReactTooltip
id={id ? id : "my-tooltip"} id={id ? id : "my-tooltip"}
className="max-w-[200px] z-[200]" className={`${maxWidth ? maxWidth : "max-w-[200px]"} z-[200]`}
/> />
</> </>
); );
@@ -6,15 +6,11 @@ const infoSlice = createSlice({
initialState: {}, initialState: {},
reducers: { reducers: {
fetchAppInfo: () => { fetchAppInfo: () => {
localStorage.setItem("_appName", appInfo.appname);
localStorage.setItem("_app_objectId", appInfo.objectId);
localStorage.setItem("baseUrl", `${appInfo.baseUrl}/`); localStorage.setItem("baseUrl", `${appInfo.baseUrl}/`);
localStorage.setItem("parseAppId", appInfo.appId); localStorage.setItem("parseAppId", appInfo.appId);
localStorage.setItem("appLogo", appInfo.applogo); localStorage.setItem("appLogo", appInfo.applogo);
localStorage.setItem("appVersion", appInfo.version);
localStorage.removeItem("userSettings"); localStorage.removeItem("userSettings");
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings)); localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
localStorage.setItem("appTitle", appInfo.appTitle);
localStorage.setItem("fev_Icon", appInfo.fev_Icon); localStorage.setItem("fev_Icon", appInfo.fev_Icon);
return appInfo; return appInfo;
} }
@@ -0,0 +1,53 @@
import React, { useState } from "react";
import Parse from "parse";
import ReactDOM from "react-dom/client";
import PdfRequestFiles from "../pages/PdfRequestFiles";
import { pdfjs } from "react-pdf";
import { Provider } from "react-redux";
import { store } from "../redux/store";
import "../index.css";
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.mjs`;
// Wrapper component to manage props
const PublicScriptFileWrapper = ({ initialProps }) => {
const [props, setProps] = useState(initialProps);
// Make the setProps function globally accessible
window.updatePublicTemplateProps = setProps;
return <PdfRequestFiles {...props} />;
};
const appId = "legadranaxn";
// "opensign";
const serverUrl = "https://app.opensignlabs.com/api/app";
// "https://staging-app.opensignlabs.com/api/app";
localStorage.setItem("baseUrl", `${serverUrl}/`);
localStorage.setItem("parseAppId", appId);
Parse.initialize(appId);
Parse.serverURL = serverUrl;
// Create a new div element and append it to the body
const scriptComponent = document.createElement("div");
scriptComponent.id = "script-component";
document.body.appendChild(scriptComponent);
const link = document.createElement("link");
link.href = "https://cdn.opensignlabs.com/fonts.css";
link.rel = "stylesheet";
document.head.appendChild(link);
//add the Tailwind CSS file
const tailwindCssLink = document.createElement("link");
tailwindCssLink.href =
"https://app.opensignlabs.com/static/js/public-template.bundle.css"; // css bundle file
// "https://staging-app.opensignlabs.com/static/js/public-template.bundle.css"; // css bundle file
tailwindCssLink.rel = "stylesheet";
document.head.appendChild(tailwindCssLink);
// Create a root and render the wrapper component with initial props
const root = ReactDOM.createRoot(document.getElementById("script-component"));
root.render(
<div>
<Provider store={store}>
<PublicScriptFileWrapper initialProps={{ text: "templateId" }} />
</Provider>
</div>
);
+3 -3
View File
@@ -1,6 +1,6 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"], content: ["./src/**/*.{js,jsx,ts,tsx}", "./src/script/PublicTemplate.js"],
theme: { theme: {
extend: {} extend: {}
}, },
@@ -60,8 +60,8 @@ module.exports = {
"success-content": "#f5f5f4", "success-content": "#f5f5f4",
warning: "#ffbe00", warning: "#ffbe00",
"warning-content": "#ccd9e8", "warning-content": "#ccd9e8",
error: "#ff0000", error: "#ffa1a7",
"error-content": "#f3f4f6", "error-content": "#16090a",
"--rounded-btn": "1.9rem", "--rounded-btn": "1.9rem",
"--tab-border": "2px", "--tab-border": "2px",
"--tab-radius": "0.7rem" "--tab-radius": "0.7rem"
+71
View File
@@ -0,0 +1,71 @@
const path = require("path");
const Dotenv = require("dotenv-webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //used to clean directory
const TerserPlugin = require("terser-webpack-plugin"); //used to minify and optimize JavaScript files
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); //used to extract css file into separate files
const isProduction = process.env.NODE_ENV;
module.exports = {
mode: isProduction ? "production" : "development",
entry: "./src/script/PublicTemplate.js",
output: {
path: path.resolve(__dirname, "public", "static", "js"),
filename: "public-template.bundle.js",
libraryTarget: "umd",
umdNamedDefine: true,
library: "PublicTemplate",
globalObject: "this"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-react"]
}
}
},
{
test: /\.css$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : "style-loader",
"css-loader",
"postcss-loader"
]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: "file-loader",
options: {
name: "assets/images/[name].[hash].[ext]"
}
}
]
}
]
},
resolve: {
extensions: [".js", ".jsx"]
},
plugins: [
new Dotenv(),
new CleanWebpackPlugin(),
...(isProduction
? [
new MiniCssExtractPlugin({
filename: "public-template.bundle.css"
})
]
: [])
],
optimization: {
minimize: isProduction ? true : false,
minimizer: [new TerserPlugin()]
},
devtool: isProduction ? "source-map" : "inline-source-map"
};
+1 -1
View File
@@ -1,5 +1,5 @@
# Use an official Node runtime as the base image # Use an official Node runtime as the base image
FROM node:18 FROM node:20
# Set the working directory inside the container # Set the working directory inside the container
WORKDIR /usr/src/app WORKDIR /usr/src/app
+2 -2
View File
@@ -143,13 +143,13 @@ export const updateMailCount = async extUserId => {
await contractUser.save(null, { useMasterKey: true }); await contractUser.save(null, { useMasterKey: true });
} else { } else {
// Create new entry if not found // Create new entry if not found
const ContractsUsers = Parse.Object.extend('contracts_users'); const ContractsUsers = Parse.Object.extend('contracts_Users');
const newContractUser = new ContractsUsers(); const newContractUser = new ContractsUsers();
newContractUser.set('EmailCount', 1); newContractUser.set('EmailCount', 1);
await newContractUser.save(null, { useMasterKey: true }); await newContractUser.save(null, { useMasterKey: true });
} }
} catch (error) { } catch (error) {
console.log('Error updating EmailCount in contracts_users: ' + error.message); console.log('Error updating EmailCount in contracts_Users: ' + error.message);
} }
}; };
@@ -1,7 +1,4 @@
import axios from 'axios';
export default async function createContact(request, response) { export default async function createContact(request, response) {
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const name = request.body.name; const name = request.body.name;
const phone = request.body?.phone; const phone = request.body?.phone;
const email = request.body.email; const email = request.body.email;
@@ -74,18 +71,6 @@ export default async function createContact(request, response) {
const user = await _user.save(); const user = await _user.save();
if (user) { if (user) {
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
// sessionToken: localStorage.getItem('accesstoken'),
};
const body = {
appName: 'contracts',
roleName: 'contracts_Guest',
userId: user.id,
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = userPtr; const currentUser = userPtr;
contactQuery.set('CreatedBy', currentUser); contactQuery.set('CreatedBy', currentUser);
contactQuery.set('UserId', user); contactQuery.set('UserId', user);
@@ -120,18 +105,6 @@ export default async function createContact(request, response) {
if (err.code === 202) { if (err.code === 202) {
const params = { email: email }; const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params); const userRes = await Parse.Cloud.run('getUserId', params);
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
// sessionToken: localStorage.getItem('accesstoken'),
};
const body = {
appName: 'contracts',
roleName: 'contracts_Guest',
userId: userRes.objectId,
};
await axios.post(roleurl, body, { headers: headers });
contactQuery.set('CreatedBy', userPtr); contactQuery.set('CreatedBy', userPtr);
contactQuery.set('UserId', { contactQuery.set('UserId', {
__type: 'Pointer', __type: 'Pointer',
+34 -7
View File
@@ -4,11 +4,9 @@ import GoogleSign from './parsefunction/GoogleSign.js';
import ZohoDetails from './parsefunction/ZohoDetails.js'; import ZohoDetails from './parsefunction/ZohoDetails.js';
import usersignup from './parsefunction/usersignup.js'; import usersignup from './parsefunction/usersignup.js';
import FacebookSign from './parsefunction/FacebookSign.js'; import FacebookSign from './parsefunction/FacebookSign.js';
import { addUserToGroups } from './parsefunction/AddUserToRole.js';
import { getUserGroups } from './parsefunction/UserGroups.js';
import DocumentAftersave from './parsefunction/DocumentAftersave.js'; import DocumentAftersave from './parsefunction/DocumentAftersave.js';
import ContactbookAftersave from './parsefunction/ContactBookAftersave.js'; import ContactbookAftersave from './parsefunction/ContactBookAftersave.js';
import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js'; // import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js';
import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js'; import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js';
import AuthLoginAsMail from './parsefunction/AuthLoginAsMail.js'; import AuthLoginAsMail from './parsefunction/AuthLoginAsMail.js';
import getUserId from './parsefunction/getUserId.js'; import getUserId from './parsefunction/getUserId.js';
@@ -44,13 +42,30 @@ import GetPublicUserName from './parsefunction/GetPublicUserName.js';
import GetPublicTemplate from './parsefunction/GetPublicTemplate.js'; import GetPublicTemplate from './parsefunction/GetPublicTemplate.js';
import ssoSignin from './parsefunction/ssoSignin.js'; import ssoSignin from './parsefunction/ssoSignin.js';
import isextenduser from './parsefunction/isextenduser.js'; import isextenduser from './parsefunction/isextenduser.js';
import getUserByOrg from './parsefunction/getUserByOrg.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import TeamsAftersave from './parsefunction/TeamsAftersave.js';
import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.js';
import PublicUserLinkContactToDoc from './parsefunction/PublicUserLinkContactToDoc.js';
import GetLogoByDomain from './parsefunction/GetLogobyDomain.js';
import AddAdmin from './parsefunction/AddAdmin.js';
import CheckAdminExist from './parsefunction/CheckAdminExist.js';
import UpdateExistUserAsAdmin from './parsefunction/UpdateExistUserAsAdmin.js';
import Newsletter from './parsefunction/Newsletter.js';
import getOrganizations from './parsefunction/getOrganizations.js';
import addOrganization from './parsefunction/addOrganization.js';
import updateOrganization from './parsefunction/updateOrganization.js';
import getTeams from './parsefunction/getTeams.js';
import addTeam from './parsefunction/addTeam.js';
import updateTeam from './parsefunction/updateTeam.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave); Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave); // Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave); Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
Parse.Cloud.afterSave('contracts_Teams', TeamsAftersave);
Parse.Cloud.afterSave('contracts_Subscriptions', SubscriptionAftersave);
// This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification. // This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification.
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave); Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave); Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
@@ -63,8 +78,6 @@ Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind); Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
// This define function creates a custom Cloud Function that can be called from the client-side, enabling custom business logic on the server. // This define function creates a custom Cloud Function that can be called from the client-side, enabling custom business logic on the server.
Parse.Cloud.define('AddUserToRole', addUserToGroups);
Parse.Cloud.define('UserGroups', getUserGroups);
Parse.Cloud.define('signPdf', PDF); Parse.Cloud.define('signPdf', PDF);
Parse.Cloud.define('sendmailv3', sendmailv3); Parse.Cloud.define('sendmailv3', sendmailv3);
Parse.Cloud.define('googlesign', GoogleSign); Parse.Cloud.define('googlesign', GoogleSign);
@@ -98,3 +111,17 @@ Parse.Cloud.define('getpublicusername', GetPublicUserName);
Parse.Cloud.define('getpublictemplate', GetPublicTemplate); Parse.Cloud.define('getpublictemplate', GetPublicTemplate);
Parse.Cloud.define('ssosign', ssoSignin); Parse.Cloud.define('ssosign', ssoSignin);
Parse.Cloud.define('isextenduser', isextenduser); Parse.Cloud.define('isextenduser', isextenduser);
Parse.Cloud.define('getuserbyorg', getUserByOrg);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('publicuserlinkcontacttodoc', PublicUserLinkContactToDoc);
Parse.Cloud.define('getlogobydomain', GetLogoByDomain);
Parse.Cloud.define('addadmin', AddAdmin);
Parse.Cloud.define('checkadminexist', CheckAdminExist);
Parse.Cloud.define('updateuserasadmin', UpdateExistUserAsAdmin);
Parse.Cloud.define('newsletter', Newsletter);
Parse.Cloud.define('getorganizations', getOrganizations);
Parse.Cloud.define('addorganization', addOrganization);
Parse.Cloud.define('updateorganization', updateOrganization);
Parse.Cloud.define('getteams', getTeams);
Parse.Cloud.define('addteam', addTeam);
Parse.Cloud.define('updateteam', updateTeam);
@@ -0,0 +1,221 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
async function addTeamAndOrg(extUser) {
try {
const orgCls = new Parse.Object('contracts_Organizations');
orgCls.set('Name', extUser.Company);
orgCls.set('IsActive', true);
orgCls.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser?.objectId,
});
orgCls.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser?.UserId?.objectId,
});
orgCls.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser?.TenantId?.objectId,
});
const orgRes = await orgCls.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
teamCls.set('IsActive', true);
const teamRes = await teamCls.save(null, { useMasterKey: true });
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUser.objectId;
updateUser.set('UserRole', 'contracts_Admin');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
} catch (err) {
console.log('err in add team, role, org', err);
}
}
async function saveUser(userDetails) {
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', userDetails.email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const url = `${serverUrl}/loginAs`;
const axiosRes = await axios({
method: 'POST',
url: url,
headers: {
'Content-Type': 'application/json;charset=utf-8',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
params: {
userId: userRes.id,
},
});
const login = await axiosRes.data;
// console.log("login ", login);
return { id: login.objectId, sessionToken: login.sessionToken };
} else {
const user = new Parse.User();
user.set('username', userDetails.email);
user.set('password', userDetails.password);
user.set('email', userDetails.email);
if (userDetails?.phone) {
user.set('phone', userDetails.phone);
}
user.set('name', userDetails.name);
const res = await user.signUp();
// console.log("res ", res);
return { id: res.id, sessionToken: res.getSessionToken() };
}
}
export default async function AddAdmin(request) {
const userDetails = request.params.userDetails;
// const subscription = request.params.subscription;
const user = await saveUser(userDetails);
try {
const extQuery = new Parse.Query('contracts_Users');
extQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
const extUser = await extQuery.first({ useMasterKey: true });
if (extUser) {
return { message: 'User already exist' };
} else {
// console.log("role ", role);
const partnerQuery = new Parse.Object('partners_Tenant');
partnerQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
if (userDetails?.phone) {
partnerQuery.set('ContactNumber', userDetails.phone);
}
partnerQuery.set('TenantName', userDetails.company);
partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true);
partnerQuery.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
if (userDetails && userDetails.pincode) {
partnerQuery.set('PinCode', userDetails.pincode);
}
if (userDetails && userDetails.country) {
partnerQuery.set('Country', userDetails.country);
}
if (userDetails && userDetails.state) {
partnerQuery.set('State', userDetails.state);
}
if (userDetails && userDetails.city) {
partnerQuery.set('City', userDetails.city);
}
if (userDetails && userDetails.address) {
partnerQuery.set('Address', userDetails.address);
}
const tenantRes = await partnerQuery.save(null, { useMasterKey: true });
// console.log("tenantRes ", tenantRes);
const newObj = new Parse.Object('contracts_Users');
newObj.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.id,
});
newObj.set('UserRole', userDetails.role);
newObj.set('Email', userDetails.email);
newObj.set('Name', userDetails.name);
if (userDetails?.phone) {
newObj.set('Phone', userDetails?.phone);
}
newObj.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantRes.id,
});
if (userDetails && userDetails.company) {
newObj.set('Company', userDetails.company);
}
if (userDetails && userDetails.jobTitle) {
newObj.set('JobTitle', userDetails.jobTitle);
}
const extRes = await newObj.save(null, { useMasterKey: true });
// if (subscription) {
const extUser = {
objectId: extRes.id,
Name: userDetails.name,
Email: userDetails.email,
Phone: userDetails?.phone ? userDetails.phone : '',
TenantId: { objectId: tenantRes.id },
UserId: { objectId: user.id },
UserRole: userDetails.role,
Company: userDetails.company,
JobTitle: userDetails.jobTitle,
};
await addTeamAndOrg(extUser);
// await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
// }
return { message: 'User sign up', sessionToken: user.sessionToken };
}
} catch (err) {
console.log('Err ', err);
}
}
// async function saveSubscription(extUserId, UserId, tenantId, subscription) {
// const SubscriptionId = subscription?.data?.subscription?.subscription_id || '';
// const Next_billing_date = subscription?.data?.subscription?.next_billing_at || '';
// const planCode = subscription?.data?.subscription?.plan?.plan_code || '';
// try {
// const createSubscription = new Parse.Object('contracts_Subscriptions');
// createSubscription.set('SubscriptionId', SubscriptionId);
// createSubscription.set('SubscriptionDetails', subscription);
// createSubscription.set('ExtUserPtr', {
// __type: 'Pointer',
// className: 'contracts_Users',
// objectId: extUserId,
// });
// createSubscription.set('CreatedBy', {
// __type: 'Pointer',
// className: '_User',
// objectId: UserId,
// });
// createSubscription.set('TenantId', {
// __type: 'Pointer',
// className: 'partners_Tenant',
// objectId: tenantId,
// });
// createSubscription.set('Next_billing_date', new Date(Next_billing_date));
// createSubscription.set('PlanCode', planCode);
// await createSubscription.save(null, { useMasterKey: true });
// } catch (err) {
// console.log('err in save subscription pgsignup', err);
// }
// }
@@ -1,262 +0,0 @@
/* --Description :cloud function to add or attach user in given role */
//-- Export Modules
import 'dotenv/config.js';
import axios from 'axios';
export async function addUserToGroups(request) {
try {
var roleName = request.params.roleName;
if (roleName == undefined) {
return Promise.reject('Error:roleName not found!');
}
var appName = request.params.appName;
if (appName == undefined) {
return Promise.reject('Error:appName not found!');
}
var chkappName = appName + '_';
//console.log("roleName " + roleName);
var userId = request.params.userId;
//console.log("userId " + userId);
console.log('addUserToGroups');
var response = {};
var rolelist = {};
var user = {
users: {
__op: 'AddRelation',
objects: [{ __type: 'Pointer', className: '_User', objectId: userId }],
},
};
var Role = roleName;
var chkappnam = Role.split('_')[0];
chkappnam = chkappnam + '_';
// if (!chkappnam == chkappName) {
// return Promise.reject("Error:Please check role it should belong to current appllication");
// }
function getAccessType(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/classes/w_appinfo?where={"appname":"' + appName + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
},
};
axios(options)
.then(x => {
const body = x.data;
var accessType;
if (body['results'].length !== 0) {
accessType = body['results'][0]['accessType'];
} else {
reject('Error:app not found!');
}
var error = accessType == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(accessType);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
var role = appName + 'appeditor';
var appaccessType = await getAccessType(request);
//console.log("appaccessType");
//console.log(appaccessType);
if (appaccessType == 'public') {
adduserToRole();
} else {
//--function to get the userid from session token
function getuserid(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/users/me',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
};
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
var userData = await getuserid(request);
if (userData.objectId == undefined) {
return Promise.reject('Error:user not found!');
}
var chkuserid = userData.objectId;
//console.log("chkuserid "+chkuserid);
var url =
process.env.SERVER_URL +
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
chkuserid +
'"},"name": {"$regex": "' +
chkappName +
'"}}';
//-- check user role
function getRoleList(chkuserid) {
return new Promise(function (resolve, reject) {
const options = {
url: url,
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
},
};
axios(options)
.then(x => {
const body = x.data;
if (body['results'].length == 0) {
reject('Error:user not found');
}
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
rolelist = await getRoleList(chkuserid);
//console.log("rolelist");
// console.log(rolelist);
var roleres = [];
var result;
for (var i = 0; i < rolelist['results'].length; i++) {
var rolenum = rolelist['results'][i]['name'];
var appnam = rolenum.split('_')[0];
appnam = appnam + '_';
if (appnam == chkappName) {
result = true;
}
}
if (result == true) {
adduserToRole();
} else {
return Promise.reject('Error:user of this app can only add user to Role');
}
}
//--after validation call adduserToRole function
async function adduserToRole() {
var roleNam = roleName;
var roleid = await getroleobjId(roleNam);
console.log('roleid');
console.log(roleid);
var response = await adduserid(roleid);
/*process.stdin.resume();
// listen to the event
process.on('SIGTERM', () => {
process.emit('cleanup');
})*/
return response;
}
//--function to get the role objId
function getroleobjId(roleNam) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles?where={"name":"' + roleNam + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
},
};
axios(options)
.then(x => {
const body = x.data;
var roleid;
if (body['results'].length !== 0) {
roleid = body['results'][0]['objectId'];
} else {
reject('Error:Role not found!');
}
var error = roleid == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(roleid);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
//--function to add the userid to role
function adduserid(roleid) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles/' + roleid,
method: 'PUT',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
'Content-Type': 'application/json',
},
data: user,
};
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
console.log('user added to role');
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
} catch (err) {
console.log('err in AddUserToRole');
console.log(err);
return Promise.reject('Error:exception in query,Result not Found');
}
}
@@ -21,7 +21,23 @@ async function AuthLoginAsMail(request) {
if (resOtp === otp) { if (resOtp === otp) {
var result = await getToken(request); var result = await getToken(request);
if (result && !result?.emailVerified) {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(result?.objectId, {
sessionToken: result.sessionToken,
});
// Update the emailVerified field to true
user.set('emailVerified', true);
// Save the user object
const res = await user.save(null, { useMasterKey: true });
if (res) {
return result; return result;
} else {
reject('user not found!');
}
} else {
return result;
}
async function getToken(request) { async function getToken(request) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
@@ -56,6 +72,7 @@ async function AuthLoginAsMail(request) {
.catch(err => { .catch(err => {
reject('user not found!'); reject('user not found!');
}); });
// user couldn't find lets sign up! // user couldn't find lets sign up!
}) })
.catch(() => { .catch(() => {
@@ -0,0 +1,20 @@
// `CheckAdminExist` is used to check is admin with org exist or not in db
export default async function CheckAdminExist() {
try {
const extClsQuery = new Parse.Query('contracts_Users');
extClsQuery.equalTo('UserRole', 'contracts_Admin');
extClsQuery.notEqualTo('IsDisabled', true);
const extAdminRes = await extClsQuery.find({ useMasterKey: true });
// must be only one admin
if (extAdminRes && extAdminRes.length === 1 && extAdminRes?.[0]?.get('OrganizationId')) {
return 'exist';
} else {
return 'not_exist';
}
} catch (err) {
console.log('err in isAdminExist', err);
const code = err?.code || 400;
const msg = err?.message || 'something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -7,7 +7,7 @@ async function DocumentAftersave(request) {
const ip = request?.headers?.['x-real-ip'] || ''; const ip = request?.headers?.['x-real-ip'] || '';
if (createdAt && Folder === undefined) { if (createdAt && Folder === undefined) {
// console.log("IN If condition") // console.log("IN If condition")
const TimeToCompleteDays = request.object.get('TimeToCompleteDays'); const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15;
const ExpiryDate = new Date(createdAt); const ExpiryDate = new Date(createdAt);
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays); ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
const documentQuery = new Parse.Query('contracts_Document'); const documentQuery = new Parse.Query('contracts_Document');
@@ -17,13 +17,13 @@ async function DocumentBeforesave(request) {
await contractUser.save(null, { useMasterKey: true }); await contractUser.save(null, { useMasterKey: true });
} else { } else {
// Create new entry if not found // Create new entry if not found
const ContractsUsers = Parse.Object.extend('contracts_users'); const ContractsUsers = Parse.Object.extend('contracts_Users');
const newContractUser = new ContractsUsers(); const newContractUser = new ContractsUsers();
newContractUser.set('DocumentCount', 1); newContractUser.set('DocumentCount', 1);
await newContractUser.save(null, { useMasterKey: true }); await newContractUser.save(null, { useMasterKey: true });
} }
} catch (error) { } catch (error) {
console.log('Error updating document count in contracts_users: ' + error.message); console.log('Error updating document count in contracts_Users: ' + error.message);
} }
if (document?.get('Signers') && document.get('Signers').length > 0) { if (document?.get('Signers') && document.get('Signers').length > 0) {
document.set('DocSentAt', new Date()); document.set('DocSentAt', new Date());
@@ -0,0 +1,25 @@
// `GetLogoByDomain` is used to get logo by domain as well as check any tenant exist or not in db
export default async function GetLogoByDomain(request) {
const domain = request.params.domain;
try {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('Domain', domain);
const res = await tenantCreditsQuery.first();
if (res) {
const updateRes = JSON.parse(JSON.stringify(res));
return { logo: updateRes?.Logo, user: 'exist' };
} else {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
const tenantRes = await tenantCreditsQuery.first();
if (tenantRes) {
return { logo: '', user: 'exist' };
} else {
return { logo: '', user: 'not_exist' };
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -4,8 +4,12 @@ async function GetPublicTemplate(request) {
if (username) { if (username) {
const extUserQuery = new Parse.Query('contracts_Users'); const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserName', username); extUserQuery.equalTo('UserName', username);
const res = await extUserQuery.first({ useMasterKey: true }); const userRes = await extUserQuery.first({ useMasterKey: true });
const userId = res.get('UserId').id; const userId = userRes.get('UserId').id;
//get _user details
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(userId, { useMasterKey: true });
if (userId) { if (userId) {
const templatQuery = new Parse.Query('contracts_Template'); const templatQuery = new Parse.Query('contracts_Template');
templatQuery.equalTo('CreatedBy', { templatQuery.equalTo('CreatedBy', {
@@ -13,12 +17,21 @@ async function GetPublicTemplate(request) {
className: '_User', className: '_User',
objectId: userId, objectId: userId,
}); });
templatQuery.descending('updatedAt');
templatQuery.equalTo('IsPublic', true); templatQuery.equalTo('IsPublic', true);
const getTemplate = await templatQuery.find({ useMasterKey: true }); const getTemplate = await templatQuery.find({ useMasterKey: true });
return getTemplate;
const extend_Res = await Parse.Cloud.run('getUserDetails', {
email: user.get('email'),
});
if (extend_Res) {
return { template: getTemplate, user: user, extend_User: extend_Res };
} else { } else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
} }
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User does not exist');
}
} else { } else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
} }
@@ -3,8 +3,10 @@ import axios from 'axios';
export default async function GetTemplate(request) { export default async function GetTemplate(request) {
const serverUrl = process.env.SERVER_URL; const serverUrl = process.env.SERVER_URL;
const templateId = request.params.templateId; const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
try { try {
if (!ispublic) {
const userRes = await axios.get(serverUrl + '/users/me', { const userRes = await axios.get(serverUrl + '/users/me', {
headers: { headers: {
'X-Parse-Application-Id': process.env.APP_ID, 'X-Parse-Application-Id': process.env.APP_ID,
@@ -12,29 +14,67 @@ export default async function GetTemplate(request) {
}, },
}); });
const userId = userRes.data && userRes.data.objectId; const userId = userRes.data && userRes.data.objectId;
// console.log("templateId ", templateId)
// console.log("userId ",userId)
if (templateId && userId) { if (templateId && userId) {
try {
let template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userRes.data.email);
extUserQuery.include('TeamIds');
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
if (_extUser?.TeamIds && _extUser.TeamIds?.length > 0) {
let teamsArr = [];
_extUser?.TeamIds?.forEach(x => (teamsArr = [...teamsArr, ...x.Ancestors]));
// Create the first query
const sharedWithQuery = new Parse.Query('contracts_Template');
sharedWithQuery.containedIn('SharedWith', teamsArr);
// Create the second query
const createdByQuery = new Parse.Query('contracts_Template');
createdByQuery.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
template = Parse.Query.or(sharedWithQuery, createdByQuery);
template.equalTo('objectId', templateId);
template.include('ExtUserPtr');
template.include('Signers');
template.include('CreatedBy');
}
}
const res = await template.first({ useMasterKey: true });
if (res) {
// console.log("res ",res)
return res;
} else {
return { error: "You don't have access of this document!" };
}
} catch (err) {
console.log('err', err);
return err;
}
}
} else if (templateId && ispublic) {
try { try {
const template = new Parse.Query('contracts_Template'); const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId); template.equalTo('objectId', templateId);
template.include('ExtUserPtr'); template.include('ExtUserPtr');
template.include('Signers'); template.include('Signers');
template.include('CreateBy'); template.include('CreatedBy');
const res = await template.first({ useMasterKey: true }); const res = await template.first({ useMasterKey: true });
// console.log("res ", res) // console.log("res ", res)
if (res) { if (res) {
// console.log("res ",res)
const acl = res.getACL();
// console.log("acl", acl.getReadAccess(userId))
if (acl && acl.getReadAccess(userId)) {
return res; return res;
} else { } else {
return { error: "You don't have access of this document!" }; return { error: "You don't have access of this document!" };
} }
} else {
return { error: "You don't have access of this document!" };
}
} catch (err) { } catch (err) {
console.log('err', err); console.log('err', err);
return err; return err;

Some files were not shown because too many files have changed in this diff Show More