Compare commits

...
Author SHA1 Message Date
prafull-opensignlabsandGitHub acd1c589d7 update pdf worker url 2024-06-16 12:59:34 +05:30
prafull-opensignlabsandGitHub de62052453 refactor: load pdf worker from local using fetch method 2024-06-16 01:01:05 +05:30
prafull-opensignlabsandGitHub de120dae39 fix: update pdf worker import path 2024-06-16 00:03:48 +05:30
AmolandGitHub 08d1e9a360 Merge pull request #848 from OpenSignLabs/resolve_stage 2024-06-15 23:16:00 +05:30
prafull-opensignlabs 74a9d08550 style: update design of invalid session popup 2024-06-15 23:14:59 +05:30
AmolandGitHub 6fc86e026b Merge pull request #847 from OpenSignLabs/revert-846-revert-845-main
Revert "Safari issue patch "
2024-06-15 22:46:38 +05:30
AmolandGitHub 72da56a916 Revert "Revert "Safari issue patch "" 2024-06-15 22:45:55 +05:30
AmolandGitHub 4ba7ffbb1a Merge pull request #846 from OpenSignLabs/revert-845-main
Revert "Safari issue patch "
2024-06-15 22:44:59 +05:30
AmolandGitHub 0d9a0b8bb1 Revert "Safari issue patch " 2024-06-15 22:44:05 +05:30
AmolandGitHub 084aa69702 Merge pull request #845 from OpenSignLabs/main
Safari issue patch
2024-06-15 22:39:36 +05:30
AmolandGitHub 597635800f Merge branch 'staging' into main 2024-06-15 22:38:52 +05:30
AmolandGitHub 8da2c7fad4 Merge pull request #844 from OpenSignLabs/fix_withresolver 2024-06-15 22:33:49 +05:30
prafull-opensignlabs 2b0276ead9 fix: Promise.withResolvers is not function 2024-06-15 22:28:21 +05:30
AmolandGitHub 45a96ece54 Merge pull request #843 from OpenSignLabs/resolve_stage 2024-06-14 21:11:50 +05:30
prafull-opensignlabs 729d19dfd3 style: update css and add email sent gif 2024-06-14 20:47:18 +05:30
AmolandGitHub 29aa123669 Merge pull request #842 from OpenSignLabs/resolve_stage
fix: showing blank screen when try to send document
2024-06-14 19:14:18 +05:30
prafull-opensignlabs f69caf016a fix: showing blank screen when try to send document 2024-06-14 19:13:07 +05:30
AmolandGitHub 48e0e0ee15 fix: change docker branch to main 2024-06-14 18:16:15 +05:30
AmolandGitHub a71278f95c Merge pull request #840 from OpenSignLabs/resolve_stage 2024-06-14 18:07:24 +05:30
prafull-opensignlabs f7bb11ede1 style: update loader and ui design of components 2024-06-14 18:05:10 +05:30
prafull-opensignlabs 6a365f7734 style: update widget detail popups 2024-06-13 20:21:25 +05:30
prafull-opensignlabs e4d946e519 style: update UI of signyourself, edit document, request signature 2024-06-13 19:29:23 +05:30
prafull-opensignlabs 149a16f150 style: update design of template ui 2024-06-13 13:27:06 +05:30
prafull-opensignlabs d8ff3d9faa refactor: resolve eslint warning 2024-06-12 18:41:03 +05:30
prafull-opensignlabs fe34001e42 style: change default theme and ui changes in components 2024-06-12 17:28:03 +05:30
prafull-opensignlabs fcdd7e2758 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into resolve_stage 2024-06-12 17:05:21 +05:30
AndrewandGitHub 025c30b685 Merge pull request #836 from OpenSignLabs/fix_smtp
Fix: smtp is not working
2024-06-12 16:58:42 +05:30
prafull-opensignlabs e742e85f0e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into fix_smtp 2024-06-12 16:58:21 +05:30
prafull-opensignlabs 476a99feae fix: smtpsecure is not defined 2024-06-12 16:56:28 +05:30
prafull-opensignlabs 29464585b8 fix: SMTP setup is not working 2024-06-12 16:54:09 +05:30
prafull-opensignlabs cdd078699b Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into resolve_stage 2024-06-12 11:15:00 +05:30
AmolandGitHub 9ef1e22fda Merge pull request #835 from OpenSignLabs/fix_linkcontact 2024-06-12 11:11:40 +05:30
prafull-opensignlabsandGitHub 655401e04b "Fix: Third contact is not receiving mail when second contact is non-existent." 2024-06-12 11:10:25 +05:30
prafull-opensignlabs b550c07ca8 style: change ui design of popups 2024-06-11 18:33:47 +05:30
prafull-opensignlabsandGitHub fd72094b8a Merge pull request #833 from OpenSignLabs/staging
fix: Ensure real-time updates to records in reports after actions are performed
2024-06-11 11:40:42 +05:30
AmolandGitHub 4d7348abc2 Merge pull request #832 from OpenSignLabs/fix_actions 2024-06-11 11:34:58 +05:30
prafull-opensignlabsandGitHub ec68479e98 fix: Ensure real-time updates to records in reports after actions are performed 2024-06-11 11:32:37 +05:30
prafull-opensignlabs ac05942e58 Merge branch 'feat_dasyui' of https://github.com/OpenSignLabs/OpenSign into resolve_stage 2024-06-11 11:11:25 +05:30
prafull-opensignlabsandGitHub b387b6b402 Merge pull request #829 from Zathiel/staging
build(deps): minor update
2024-06-11 10:01:54 +05:30
ZathielandGitHub e13baeb2fc Merge pull request #20 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/babel/runtime-corejs2-7.24.7
build(deps-dev): bump @babel/runtime-corejs2 from 7.24.6 to 7.24.7 in /apps/OpenSign
2024-06-11 00:32:07 +02:00
ZathielandGitHub 52eb8e4a68 Merge pull request #15 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/babel/eslint-parser-7.24.7
build(deps-dev): bump @babel/eslint-parser from 7.24.6 to 7.24.7 in /apps/OpenSignServer
2024-06-11 00:31:19 +02:00
ZathielandGitHub 71189783a8 Merge pull request #18 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/nodemon-3.1.3
build(deps-dev): bump nodemon from 3.1.1 to 3.1.3 in /apps/OpenSignServer
2024-06-11 00:30:31 +02:00
ZathielandGitHub aec9396524 Merge pull request #21 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/tailwindcss-3.4.4
build(deps-dev): bump tailwindcss from 3.4.3 to 3.4.4 in /apps/OpenSign
2024-06-11 00:29:36 +02:00
ZathielandGitHub d74390b974 Merge pull request #23 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/braces-3.0.3
build(deps): bump braces from 3.0.2 to 3.0.3 in /apps/OpenSignServer
2024-06-11 00:29:03 +02:00
ZathielandGitHub 998fe013c8 Merge pull request #24 from Zathiel/dependabot/npm_and_yarn/braces-3.0.3
build(deps-dev): bump braces from 3.0.2 to 3.0.3
2024-06-11 00:28:48 +02:00
dependabot[bot]andGitHub 06d4c9aeaf build(deps-dev): bump nodemon in /apps/OpenSignServer
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.1 to 3.1.3.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.1...v3.1.3)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 22:27:44 +00:00
dependabot[bot]andGitHub bc77a9b0a1 build(deps-dev): bump braces from 3.0.2 to 3.0.3
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 22:27:19 +00:00
dependabot[bot]andGitHub 07c0390401 build(deps): bump braces from 3.0.2 to 3.0.3 in /apps/OpenSignServer
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 22:27:00 +00:00
ZathielandGitHub c50d753d74 Merge pull request #22 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/grpc/grpc-js-1.10.9
build(deps): bump @grpc/grpc-js from 1.10.8 to 1.10.9 in /apps/OpenSignServer
2024-06-11 00:26:56 +02:00
dependabot[bot]andGitHub 65eef5739d build(deps): bump @grpc/grpc-js in /apps/OpenSignServer
Bumps [@grpc/grpc-js](https://github.com/grpc/grpc-node) from 1.10.8 to 1.10.9.
- [Release notes](https://github.com/grpc/grpc-node/releases)
- [Commits](https://github.com/grpc/grpc-node/compare/@grpc/grpc-js@1.10.8...@grpc/grpc-js@1.10.9)

---
updated-dependencies:
- dependency-name: "@grpc/grpc-js"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 22:22:20 +00:00
dependabot[bot]andGitHub 01eed3664b build(deps-dev): bump tailwindcss from 3.4.3 to 3.4.4 in /apps/OpenSign
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.3 to 3.4.4.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.4/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.3...v3.4.4)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 16:00:42 +00:00
dependabot[bot]andGitHub 685e09d406 build(deps-dev): bump @babel/runtime-corejs2 in /apps/OpenSign
Bumps [@babel/runtime-corejs2](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime-corejs2) from 7.24.6 to 7.24.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/packages/babel-runtime-corejs2)

---
updated-dependencies:
- dependency-name: "@babel/runtime-corejs2"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 16:00:25 +00:00
dependabot[bot]andGitHub 7bacca1f78 build(deps-dev): bump @babel/eslint-parser in /apps/OpenSignServer
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.24.6 to 7.24.7.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.7/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-10 15:51:30 +00:00
prafull-opensignlabsandGitHub 3764ab5c30 Merge pull request #827 from OpenSignLabs/resolve_main
fix: conflict resolve
2024-06-10 20:40:07 +05:30
RaktimaNXG 2f7a1d67b6 fix: conflict resolve 2024-06-10 20:38:42 +05:30
prafull-opensignlabs 8726efb0aa style: updated buttons from report and signers input box 2024-06-10 20:37:21 +05:30
prafull-opensignlabsandGitHub e073e83ad6 Merge pull request #824 from OpenSignLabs/raktima-opensignlabs-patch-9
fix: a mismatch permits a signer placeholder to sign any placeholder not allocated to that signer issue in the quick send flow.
2024-06-10 18:42:08 +05:30
RaktimaNXG 69140d2139 fix: a mismatch permits a signer placeholder to sign any placeholder not allocated to that signer issue in the quick send flow. 2024-06-10 18:14:28 +05:30
prafull-opensignlabs aeed48c0ef style: change ui design of login, signup, forgetpassword page 2024-06-10 15:51:52 +05:30
prafull-opensignlabs 1f9504b9cc style: change default theme emerald to cupcake 2024-06-10 15:01:16 +05:30
prafull-opensignlabsandGitHub 5518d6932e Merge pull request #823 from OpenSignLabs/raktima-opensignlabs-patch-9
fix: second non existing signer is not receiving the signature request email in the quick-send process.
2024-06-10 12:19:49 +05:30
RaktimaNXG f14e301b6c fix: change condition of email send 2024-06-10 12:15:48 +05:30
RaktimaNXG 901ddf35bb remove console 2024-06-10 11:43:56 +05:30
RaktimaNXG 9aa3166528 fix: second signer is not receiving the signature request email in the quick-send process. 2024-06-10 11:24:35 +05:30
prafull-opensignlabsandGitHub a90e679228 Merge pull request #813 from OpenSignLabs/raktima-opensignlabs-patch-9
fix: display the list of unsigned signers in the 'Yet to Sign' panel using the QuickSend flow.
2024-06-07 20:16:34 +05:30
RaktimaNXG 90b5e3c860 fix: exist contact not recieving maiin in quick send flow 2024-06-07 19:55:40 +05:30
RaktimaNXG 74f6612271 refactor code 2024-06-07 19:33:28 +05:30
prafull-opensignlabs 2862a97c4e feat: implement daisy-ui in alert, report, drive 2024-06-07 19:32:00 +05:30
RaktimaNXG 5c3f967128 fix: add validation of signature widgetat in quick send flow, update SentToOthers field when document is in inProgress , resolve resend nonexist email issue 2024-06-07 19:07:56 +05:30
prafull-opensignlabs 6fde71e947 feat: implement daisy-ui in all pages 2024-06-07 14:24:15 +05:30
RaktimaNXG 2900464272 fix: display the list of unsigned signers in the 'Yet to Sign' panel using the QuickSend flow. 2024-06-07 14:20:36 +05:30
prafull-opensignlabs 10bac3c11f refactor: change prefix of daisy-ui theme classes 2024-06-07 11:19:08 +05:30
AmolandGitHub 302f38d472 Merge pull request #808 from OpenSignLabs/autoreminder 2024-06-06 19:36:19 +05:30
prafull-opensignlabs 8ecdbc43a3 feat: implement daisy ui in forms 2024-06-06 18:36:31 +05:30
prafull-opensignlabs 6721b334cf refactor: remove unncessary code from dashboard & css changes in sidebar 2024-06-06 14:38:08 +05:30
prafull-opensignlabs 454f557d14 fix: position of cards and reports in dashboard 2024-06-06 11:49:06 +05:30
prafull-opensignlabs 635693b394 fix: position of cards and reports 2024-06-06 11:48:06 +05:30
prafull-opensignlabsandGitHub 0be1816046 Merge pull request #801 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/nodemon-3.1.2
build(deps-dev): bump nodemon from 3.1.1 to 3.1.2 in /apps/OpenSignServer
2024-06-06 11:09:30 +05:30
prafull-opensignlabsandGitHub feb162e03e Merge pull request #809 from Zathiel/staging
build(deps) : react-rnd && mongoose
2024-06-06 11:06:42 +05:30
prafull-opensignlabs c8801846fa feat: implement daisy ui in dashboard 2024-06-05 20:23:50 +05:30
ZathielandGitHub a793fc3722 Merge pull request #1 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/react-rnd-10.4.11
build(deps): bump react-rnd from 10.4.10 to 10.4.11 in /apps/OpenSign
2024-06-05 14:26:30 +02:00
ZathielandGitHub ffdabe4a00 Merge pull request #13 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/mongoose-8.4.1
build(deps): bump mongoose from 8.4.0 to 8.4.1 in /apps/OpenSignServer
2024-06-05 14:25:43 +02:00
prafull-opensignlabs 5536c99052 feat: implement daisy-ui in sidebar 2024-06-05 17:35:31 +05:30
prafull-opensignlabsandGitHub ba1c3145f3 fix: mail and document count in autoreminder API is not correct 2024-06-05 13:49:20 +05:30
prafull-opensignlabs 0fbe6aeb6c feat: implement dasyui in header, footer & report 2024-06-04 19:38:41 +05:30
AmolandGitHub 4898c4a851 Merge pull request #807 from OpenSignLabs/feat_sso 2024-06-04 10:06:34 +05:30
prafull-opensignlabs 85aa6b2947 fix: update sso url 2024-06-04 10:03:30 +05:30
AmolandGitHub 3b02d2bcdb fix: update SSO url 2024-06-04 01:01:52 +05:30
dependabot[bot]andGitHub ed01f4a7a0 build(deps): bump mongoose from 8.4.0 to 8.4.1 in /apps/OpenSignServer
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.4.0 to 8.4.1.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/8.4.0...8.4.1)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 15:38:51 +00:00
AmolandGitHub 1f395cf25f Merge pull request #806 from OpenSignLabs/feat_sso 2024-06-03 18:52:09 +05:30
prafull-opensignlabs cf98e73d13 fix: freeze sign in with sso button after click 2024-06-03 18:50:38 +05:30
AmolandGitHub 10112ac20d Merge pull request #805 from OpenSignLabs/feat_sso 2024-06-03 18:28:12 +05:30
prafull-opensignlabs 9a0183274f fix: Set send_email to false to stop receiving mails through API 2024-06-03 18:27:17 +05:30
AmolandGitHub 6b2e16c21d Merge pull request #804 from OpenSignLabs/feat_sso 2024-06-03 15:49:45 +05:30
prafull-opensignlabs 3c51baf1c9 build(deps): updated nth-check dependency for security 2024-06-03 15:46:24 +05:30
dependabot[bot]andGitHub 3f4be1c4f3 build(deps): bump react-rnd from 10.4.10 to 10.4.11 in /apps/OpenSign
Bumps [react-rnd](https://github.com/bokuweb/react-rnd) from 10.4.10 to 10.4.11.
- [Release notes](https://github.com/bokuweb/react-rnd/releases)
- [Commits](https://github.com/bokuweb/react-rnd/commits)

---
updated-dependencies:
- dependency-name: react-rnd
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 09:15:34 +00:00
AmolandGitHub 62b36f58f7 Merge pull request #803 from OpenSignLabs/feat_sso 2024-06-03 14:00:27 +05:30
prafull-opensignlabs ba3591c7bc fix: enable mail send feature for non-quick send documents when sendinorder is true 2024-06-03 13:51:15 +05:30
dependabot[bot]andGitHub 1ec11b8733 build(deps-dev): bump nodemon in /apps/OpenSignServer
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.1...v3.1.2)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 07:16:39 +00:00
prafull-opensignlabsandGitHub ab89d3b71f Merge pull request #799 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/prettier-3.3.0
build(deps-dev): bump prettier from 3.2.5 to 3.3.0 in /apps/OpenSignServer
2024-06-03 12:45:41 +05:30
prafull-opensignlabsandGitHub 1811173648 Merge pull request #796 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/reactour-1.19.4
build(deps): bump reactour from 1.19.3 to 1.19.4 in /apps/OpenSign
2024-06-03 12:44:34 +05:30
AmolandGitHub b218e96107 Merge pull request #802 from OpenSignLabs/fix_email 2024-06-03 12:39:24 +05:30
prafull-opensignlabs 64a0be8776 fix: cannot read property of undefined 'email' 2024-06-03 12:38:17 +05:30
dependabot[bot]andGitHub 414e2d819c build(deps-dev): bump prettier in /apps/OpenSignServer
Bumps [prettier](https://github.com/prettier/prettier) from 3.2.5 to 3.3.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.2.5...3.3.0)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 01:11:29 +00:00
dependabot[bot]andGitHub 0769101f28 build(deps): bump reactour from 1.19.3 to 1.19.4 in /apps/OpenSign
Bumps [reactour](https://github.com/elrumordelaluz/reactour) from 1.19.3 to 1.19.4.
- [Release notes](https://github.com/elrumordelaluz/reactour/releases)
- [Commits](https://github.com/elrumordelaluz/reactour/commits)

---
updated-dependencies:
- dependency-name: reactour
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-03 00:25:47 +00:00
AmolandGitHub 5dc86119d3 Merge pull request #794 from OpenSignLabs/feat_sso 2024-05-31 19:28:52 +05:30
prafull-opensignlabs 4999dc2b18 Fix: password field is not visible for non-sso user 2024-05-31 19:27:18 +05:30
AmolandGitHub 962d36c088 Merge pull request #793 from OpenSignLabs/feat_sso 2024-05-31 19:02:40 +05:30
prafull-opensignlabs d071986aac feat: hide password input for sso and restict sso 2024-05-31 19:00:29 +05:30
prafull-opensignlabs afb4de9d0c Feat: create SSO user account on platform 2024-05-31 18:22:16 +05:30
prafull-opensignlabs bc5c8cac33 feat: existing user can sign in with sso 2024-05-31 11:41:53 +05:30
prafull-opensignlabsandGitHub be93e6a214 Merge pull request #777 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/babel/runtime-corejs2-7.24.6
build(deps-dev): bump @babel/runtime-corejs2 from 7.24.5 to 7.24.6 in /apps/OpenSign
2024-05-31 10:40:37 +05:30
prafull-opensignlabsandGitHub 37748057e3 Merge pull request #791 from OpenSignLabs/public_url
fix: incorrect condition implementation for setting template as public based on isEnableSubscription
2024-05-30 18:10:34 +05:30
RaktimaNXG eb3bb77692 fix: incorrect condition implementation for setting template as public based on isEnableSubscription 2024-05-30 18:08:54 +05:30
prafull-opensignlabsandGitHub df0ce3f086 Merge pull request #788 from OpenSignLabs/public_url
feat: create public profile URLs to add username and develop functionality to make public template from report
2024-05-30 17:53:07 +05:30
AmolandGitHub 0a99b5181e Merge pull request #790 from OpenSignLabs/feat_bulksend 2024-05-30 17:37:45 +05:30
prafull-opensignlabs 8a5d3a0368 Fix: not able to create contract from add user icon in PDF UI 2024-05-30 17:35:40 +05:30
RaktimaNXG c00406e401 fix: template list status issue on pagination 2024-05-30 17:24:33 +05:30
RaktimaNXG aeead23c86 fix: change public profile url 2024-05-30 15:57:16 +05:30
RaktimaNXG 83fe88911f fix: public toggle issue,cloud function error handling 2024-05-30 15:55:16 +05:30
prafull-opensignlabsandGitHub 527bca32d2 Merge pull request #789 from Zathiel/staging
FIX : update USE_LOCAL variable
2024-05-30 15:14:09 +05:30
ZathielandGitHub 9bf4940c2e Merge branch 'OpenSignLabs:staging' into staging 2024-05-30 10:46:55 +02:00
ZathielandGitHub a653df6935 FIX : USE_LOCAL variable 2024-05-30 01:45:05 +02:00
RaktimaNXG dfc6da388c fix: add comment 2024-05-29 19:44:17 +05:30
RaktimaNXG 3e2c822bbc fix: handle condition to save username 2024-05-29 19:39:27 +05:30
RaktimaandGitHub 1a20841a94 Merge branch 'staging' into public_url 2024-05-29 19:31:17 +05:30
RaktimaNXG c6be81923b fix: conflict resolved in userProfile 2024-05-29 19:24:04 +05:30
RaktimaNXG 6b47f6a27b fix: add comment 2024-05-29 18:56:17 +05:30
RaktimaNXG 33b0111ed0 fix: remove console 2024-05-29 18:43:24 +05:30
RaktimaNXG 48fd9e52b3 fix: update isEnableSuscription variable nname 2024-05-29 18:20:16 +05:30
RaktimaNXG d85074b127 fix: username issue to get details from GetPublicUserNmae cloud function 2024-05-29 18:17:07 +05:30
AmolandGitHub 5ada9a411b Merge pull request #787 from OpenSignLabs/feat_bulksend 2024-05-29 17:56:43 +05:30
prafull-opensignlabs 6f50de1ef1 fix: make phone field optional for contact 2024-05-29 17:55:22 +05:30
AmolandGitHub 612150cf4b Merge pull request #786 from OpenSignLabs/feat_bulksend 2024-05-29 16:59:49 +05:30
prafull-opensignlabs 634db83e8e fix: duplicate contact are creatable, Update the the header 'Document signed' in the success message popup 2024-05-29 16:58:35 +05:30
RaktimaNXG b32c8068ef fix: merge changes from staging 2024-05-29 16:07:05 +05:30
RaktimaNXG ae464274b0 fix: update ReportJson file 2024-05-29 15:55:07 +05:30
AmolandGitHub c5ceba8d57 Merge pull request #785 from OpenSignLabs/feat_bulksend
fix: issue of something went wrong due to quick send
2024-05-29 13:56:05 +05:30
prafull-opensignlabs 903d626c27 fix: issue of something went wrong due to quick send 2024-05-29 13:05:57 +05:30
prafull-opensignlabsandGitHub e5b4fb123f Merge pull request #782 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/nodemon-3.1.1
build(deps-dev): bump nodemon from 3.1.0 to 3.1.1 in /apps/OpenSignServer
2024-05-28 20:21:00 +05:30
prafull-opensignlabsandGitHub 116893c1b4 Merge pull request #781 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/babel/eslint-parser-7.24.6
build(deps-dev): bump @babel/eslint-parser from 7.24.5 to 7.24.6 in /apps/OpenSignServer
2024-05-28 20:20:36 +05:30
prafull-opensignlabsandGitHub 592254a9a3 Merge pull request #778 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/lint-staged-15.2.5
build(deps-dev): bump lint-staged from 15.2.4 to 15.2.5 in /apps/OpenSign
2024-05-28 20:20:19 +05:30
AmolandGitHub f52c6e24fe Merge pull request #784 from OpenSignLabs/feat_bulksend 2024-05-28 19:23:50 +05:30
prafull-opensignlabs 70ae1e24fc fix: merge conflict 2024-05-28 19:18:41 +05:30
prafull-opensignlabs c491595fc8 Fix: issues in quick send 2024-05-28 18:56:56 +05:30
AmolandGitHub 6875135ede Merge pull request #783 from OpenSignLabs/feat_bulksend
Fix: issues in quick send
2024-05-28 10:42:33 +05:30
RaktimaNXG 662049d878 fix: remove console 2024-05-28 09:50:38 +05:30
RaktimaNXG 1764d7f9ba feat: create public profile URLs for users and develop functionality to make the report template public 2024-05-27 21:08:05 +05:30
prafull-opensignlabs 07933b56b1 Fix: issue of 'something went wrong' err in guest login flow when using quick send 2024-05-27 20:53:38 +05:30
prafull-opensignlabs f6ba4db62d Fix: Role, type are not visible on placeholder for non-exist contact 2024-05-27 20:06:06 +05:30
prafull-opensignlabs 5bd36f91d9 Fix: issue with multiple linking user in quick send 2024-05-27 18:46:37 +05:30
prafull-opensignlabs c199c3019e feat: add message for all signers linked in template for quick send 2024-05-27 14:50:17 +05:30
prafull-opensignlabs facbc8e76e Fix: issue of showing 'Please add Signers or Roles in template' even when roles are already exist in quick send 2024-05-27 12:59:43 +05:30
prafull-opensignlabs c3c6f6bab3 fix: send mail only first signer if sendinorder, takeout quick send btn from 3 dots 2024-05-27 11:59:25 +05:30
prafull-opensignlabsandGitHub f5e216fedc Merge pull request #768 from OpenSignLabs/raktima-opensignlabs-patch-8
feat: add download, certificate & print buttons on the document completion popup
2024-05-27 10:40:31 +05:30
RaktimaandGitHub c40a923388 Merge branch 'staging' into raktima-opensignlabs-patch-8 2024-05-27 10:30:38 +05:30
dependabot[bot]andGitHub a86f4fd9b7 build(deps-dev): bump nodemon in /apps/OpenSignServer
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.0 to 3.1.1.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.0...v3.1.1)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 00:27:27 +00:00
dependabot[bot]andGitHub 59f6d022cf build(deps-dev): bump @babel/eslint-parser in /apps/OpenSignServer
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.24.5 to 7.24.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 00:27:04 +00:00
dependabot[bot]andGitHub b7beeee260 build(deps-dev): bump lint-staged in /apps/OpenSign
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 15.2.4 to 15.2.5.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md)
- [Commits](https://github.com/okonet/lint-staged/compare/v15.2.4...v15.2.5)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 00:14:13 +00:00
dependabot[bot]andGitHub 31c861cbe4 build(deps-dev): bump @babel/runtime-corejs2 in /apps/OpenSign
Bumps [@babel/runtime-corejs2](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime-corejs2) from 7.24.5 to 7.24.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.6/packages/babel-runtime-corejs2)

---
updated-dependencies:
- dependency-name: "@babel/runtime-corejs2"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-27 00:13:57 +00:00
AmolandGitHub 6a9408b7b4 docs: add Zathiel as a contributor for security 2024-05-24 19:24:52 +05:30
AmolandGitHub a9fd264a01 Merge pull request #772 from OpenSignLabs/feat_bulksend
Feat: quick send feature to create & send multiple documents at a time
2024-05-24 16:42:08 +05:30
prafull-opensignlabs 6c77bbfe75 feat: quick send feature to create & send multiple documents at a time 2024-05-24 16:29:03 +05:30
prafull-opensignlabs 890c3d2b1f Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into feat_bulksend 2024-05-24 11:34:58 +05:30
AmolandGitHub 3ede441f54 Merge pull request #771 from OpenSignLabs/staging
v1.7.4
2024-05-24 10:29:10 +05:30
AmolandGitHub 2433ad5957 Merge pull request #770 from OpenSignLabs/build_dep 2024-05-24 10:23:10 +05:30
prafull-opensignlabs fffd907d63 Fix: handle owner subscription has expired error in guest login 2024-05-24 10:13:34 +05:30
AmolandGitHub 51edc339cd Merge pull request #769 from OpenSignLabs/staging
v1.7.3
2024-05-23 18:22:49 +05:30
AmolandGitHub bf307abe9a Update plansArr.js 2024-05-23 18:17:00 +05:30
RaktimaNXG 08a97707b7 fix: completion popup design issue in tablet view 2024-05-23 13:46:17 +05:30
RaktimaNXG d99fecd46e feat: add download, certificate & print buttons on the document completion popup 2024-05-23 12:52:10 +05:30
prafull-opensignlabsandGitHub 6c59c0ec9a Merge pull request #767 from OpenSignLabs/raktima-opensignlabs-patch-8
feat: add help button for text validation
2024-05-23 10:35:45 +05:30
RaktimaNXG 32c75d1fc3 feat: add help button for text validation 2024-05-23 10:30:41 +05:30
AmolandGitHub 7f0f81c334 Merge pull request #766 from OpenSignLabs/build_dep
fix: issue in guestlogin of owner subscription has expired
2024-05-22 19:59:23 +05:30
prafull-opensignlabsandGitHub a882f07f57 Merge pull request #765 from OpenSignLabs/tour_message_requestsign
fix: download menu is not wokring inProgress report in mobile view
2024-05-22 19:58:21 +05:30
prafull-opensignlabs 74b81a5363 fix: issue in guestlogin of owner subscription has expired 2024-05-22 19:56:44 +05:30
AmolandGitHub 9b75764717 Merge pull request #764 from OpenSignLabs/build_dep
fix: resend mail not working, create contact API not working
2024-05-22 19:47:11 +05:30
RaktimaNXG c1468112b0 fix: download menu is not wokring inProgress report in mobile view 2024-05-22 19:46:22 +05:30
prafull-opensignlabs 950b74789c fix: create contact API not working 2024-05-22 19:46:09 +05:30
prafull-opensignlabs 7e4f12e41a feat: bulk send is working for exist contact 2024-05-22 19:31:32 +05:30
prafull-opensignlabs d75c1d7c0c fix: resend mail not working 2024-05-22 19:19:33 +05:30
prafull-opensignlabsandGitHub 7eacbedf59 Merge pull request #763 from OpenSignLabs/tour_message_requestsign
feat: add tour messages to request signatures flow
2024-05-22 19:02:12 +05:30
RaktimaNXG 2e5947279f fix:onclick upgrade button navigate user to new window 2024-05-22 18:51:57 +05:30
RaktimaNXG ef5ac7d626 fix: err is not defined 2024-05-22 17:49:24 +05:30
RaktimaNXG 658c37cc4d feat: add tour messages to request signatures flow 2024-05-22 17:48:15 +05:30
prafull-opensignlabs ffc8e4b23e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into feat_bulksend 2024-05-22 13:44:14 +05:30
prafull-opensignlabsandGitHub 3fb96fc792 Merge pull request #762 from OpenSignLabs/build_dep
build(deps): update dependencies
2024-05-22 12:55:31 +05:30
prafull-opensignlabs 71016ba5e0 build(deps): update dependecies 2024-05-22 12:46:34 +05:30
prafull-opensignlabs cd1a6fdaf6 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into feat_bulksend 2024-05-22 12:11:46 +05:30
prafull-opensignlabsandGitHub c2afeed448 Merge pull request #760 from OpenSignLabs/raktima-opensignlabs-patch-7
fix: unnecessary emails are sent when a signers sign a document via a shared link.
2024-05-22 11:24:15 +05:30
RaktimaNXG 37fd7bbeda fix: update migration script add SendCompletionMail filed in contracts_Document class 2024-05-22 11:22:35 +05:30
RaktimaNXG 71fbde0099 fix: add migration script to add new filed SendMail and handle email sent using share link from inProgress report 2024-05-22 09:50:12 +05:30
prafull-opensignlabs ca6cb60d78 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into feat_bulksend 2024-05-22 09:49:51 +05:30
prafull-opensignlabsandGitHub df1c0ea66c Merge pull request #761 from Zathiel/staging
build(deps): react-redux, autoprefixer , react-datepicker axios, @reduxjs/toolkit, react-tooltip, tailwindcss react-pdf
2024-05-22 09:45:54 +05:30
Zathiel 46279efc2c build(deps): react-redux, autoprefixer , react-datepicker axios, @reduxjs/toolkit, react-tooltip, tailwindcss react-pdf 2024-05-21 15:29:38 +00:00
prafull-opensignlabs aca2848406 add Ui for bulk send, changes in reportjson 2024-05-21 20:26:46 +05:30
prafull-opensignlabsandGitHub eaec3cf607 Merge pull request #750 from Zathiel/staging
build(deps): update @reduxjs/toolkit package
2024-05-21 20:00:00 +05:30
ZathielandGitHub ad25d73f26 Merge branch 'OpenSignLabs:staging' into staging 2024-05-21 16:19:31 +02:00
RaktimaNXG 481b9ac1e0 fix: add send mail false using share link from inprogress report 2024-05-21 17:36:41 +05:30
AmolandGitHub 4cd3791feb Merge pull request #759 from OpenSignLabs/feat_resendmail 2024-05-21 15:18:10 +05:30
RaktimaNXG 7de8699437 fix: unnecessary emails are sent when a user signs a document via a shared link. 2024-05-21 15:08:40 +05:30
prafull-opensignlabsandGitHub 4c162676b1 Merge pull request #753 from OpenSignLabs/raktima-opensignlabs-patch-7
feat:  celebration confetti on document completion
2024-05-21 14:02:28 +05:30
prafull-opensignlabs ca390bdfa6 refactor: remove Signpdf file 2024-05-21 13:56:37 +05:30
prafull-opensignlabs 22d064fd38 build(dep): update node-signpdf pacakge and it's dependencies, remove customsignpdf folder 2024-05-21 13:49:10 +05:30
AmolandGitHub 444c9bbfdf Merge pull request #756 from OpenSignLabs/feat_resendmail 2024-05-21 13:44:07 +05:30
prafull-opensignlabs b289686d42 build(dep): update parse-server package and it's dependencies 2024-05-21 13:42:50 +05:30
RaktimaNXG eeb878b340 refactor:refactor code 2024-05-21 13:40:54 +05:30
RaktimaNXG 7894d69cc9 fix: increase time to show confetti celebration 2024-05-21 13:35:22 +05:30
RaktimaNXG b992d7db24 fix: change celebration effect and add for whole screen 2024-05-21 13:03:51 +05:30
AmolandGitHub 50b72f4d85 Merge pull request #755 from OpenSignLabs/feat_resendmail 2024-05-20 18:43:49 +05:30
prafull-opensignlabs 44639f66b0 fix: autoreminder API not working 2024-05-20 18:42:32 +05:30
RaktimaNXG ae740efced refactor: refactor code 2024-05-20 17:46:07 +05:30
RaktimaNXG 66c020d903 fix: fix celebration ui for mobile view 2024-05-20 17:26:18 +05:30
AmolandGitHub 562a7d6fc8 Merge pull request #754 from OpenSignLabs/feat_resendmail
feat: now user can send automatic reminders
2024-05-20 15:44:54 +05:30
RaktimaNXG 090c17c2c0 refactor: add comment 2024-05-20 15:41:04 +05:30
RaktimaNXG 70637d6143 fix: handle condition to show complete message 2024-05-20 15:39:14 +05:30
prafull-opensignlabs f38e8705de feat: now user can sent automatic reminders 2024-05-20 15:31:20 +05:30
RaktimaNXG ba06ff72a0 fix: mismatch complete message issue in multiple signers flow 2024-05-20 14:01:06 +05:30
RaktimaNXG 82312a1e0d feat: celebration confetti on document completion 2024-05-20 11:42:37 +05:30
ZathielandGitHub 1a8cf11a3e Merge pull request #22 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/reduxjs/toolkit-2.2.4
build(deps): bump @reduxjs/toolkit from 2.2.1 to 2.2.4 in /apps/OpenSign
2024-05-19 02:18:41 +02:00
ZathielandGitHub 3deacbabc3 Merge pull request #8 from Zathiel/dependabot/npm_and_yarn/apps/OpenSignServer/babel/eslint-parser-7.24.5
build(deps-dev): bump @babel/eslint-parser from 7.24.1 to 7.24.5 in /apps/OpenSignServer
2024-05-19 02:18:23 +02:00
AmolandGitHub c28805add9 Merge pull request #749 from OpenSignLabs/feat_resendmail 2024-05-17 19:22:04 +05:30
prafull-opensignlabs 80fb320ae4 refactor: add mail in resend mail UI 2024-05-17 19:20:17 +05:30
AmolandGitHub 76c9c1fa53 Merge pull request #748 from OpenSignLabs/feat_resendmail 2024-05-17 14:53:57 +05:30
prafull-opensignlabs f38d83dbe0 style: change z-index of alert 2024-05-17 12:05:18 +05:30
AmolandGitHub ee9411729d Merge pull request #747 from OpenSignLabs/feat_resendmail
refactor: change in subject & body of resend mail
2024-05-17 10:57:29 +05:30
prafull-opensignlabs f6f7c6cecd refactor: change in subject & body of resend mail 2024-05-17 10:56:14 +05:30
AmolandGitHub a45fc0dd60 Merge pull request #746 from OpenSignLabs/feat_resendmail
fix: Cannot read properties of undefined (reading 'find') in resend mail & resend mail not available dashboard report
2024-05-17 10:49:06 +05:30
prafull-opensignlabs 348f7810b9 fix: Cannot read properties of undefined (reading 'find') in resend mail & resend mail not available dashboard report 2024-05-17 10:09:15 +05:30
AmolandGitHub 45bec128a9 Merge pull request #744 from OpenSignLabs/feat_resendmail 2024-05-16 19:37:39 +05:30
prafull-opensignlabs 4da307315c feat: add resend mail functionality for inprogress document 2024-05-16 19:34:28 +05:30
AmolandGitHub c87aa415a9 Merge pull request #743 from OpenSignLabs/signPdf
feat: move forget password button in login box
2024-05-15 20:13:38 +05:30
prafull-opensignlabs b1786ec16a feat: move forget password button in login box 2024-05-15 20:03:30 +05:30
AmolandGitHub 6ad35e979e Merge pull request #742 from OpenSignLabs/signPdf 2024-05-15 16:12:25 +05:30
prafull-opensignlabs 9d996d5cb7 feat: add copy icon beside API token 2024-05-15 16:09:11 +05:30
AmolandGitHub 008e3e8783 Merge pull request #741 from OpenSignLabs/signPdf 2024-05-15 14:52:04 +05:30
prafull-opensignlabs 8458906b0b fix: delete foler are still visible 2024-05-15 14:49:36 +05:30
AmolandGitHub 6933aa309b Merge pull request #740 from OpenSignLabs/signPdf 2024-05-15 13:55:49 +05:30
prafull-opensignlabs 067ea68d3b feat: add download button in 3 dots beside finish button 2024-05-15 13:25:13 +05:30
prafull-opensignlabs e4e8a0b721 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-15 11:10:29 +05:30
prafull-opensignlabs dc392adc1d feat: add delete folder API 2024-05-14 19:19:17 +05:30
AmolandGitHub 741efff1fa Merge pull request #739 from OpenSignLabs/staging
v1.7.2
2024-05-14 18:56:09 +05:30
AmolandGitHub 93b0d00ab0 Merge pull request #738 from OpenSignLabs/signPdf
fix: some webhook event are not working for second signer & add certificate in completed webhook
2024-05-14 18:33:31 +05:30
prafull-opensignlabs d731951159 fix: some webhook event are not working for second signer & add certificate in completed webhook 2024-05-14 18:30:26 +05:30
AmolandGitHub 24bf08c14f Merge pull request #737 from OpenSignLabs/signPdf
fix: resend mail API is not working
2024-05-14 16:44:42 +05:30
prafull-opensignlabs c03819f5a9 fix: resend mail API is not working 2024-05-14 16:36:58 +05:30
prafull-opensignlabsandGitHub 4521d16891 Merge pull request #719 from ugoconsonni/patch-4
Update createDocumentwithCoordinate.js
2024-05-14 13:57:08 +05:30
prafull-opensignlabs f77525cd8b fix: change baseUrl in APIs 2024-05-14 13:55:32 +05:30
AmolandGitHub 38b7311e66 Merge pull request #736 from OpenSignLabs/staging
v1.7.1
2024-05-14 10:45:00 +05:30
AmolandGitHub c934f8ccd6 Merge pull request #735 from OpenSignLabs/signPdf
fix: change  parameter name in updatefolder API
2024-05-14 10:40:30 +05:30
prafull-opensignlabs 4e128e5a5a fix: change parameter name in updatefolder API 2024-05-14 10:40:04 +05:30
AmolandGitHub e0ccf049a6 Merge pull request #734 from OpenSignLabs/staging
v1.7.0
2024-05-14 01:49:55 +05:30
AndrewandGitHub b202d78059 Merge pull request #730 from OpenSignLabs/signPdf 2024-05-13 19:35:04 +05:30
prafull-opensignlabsandGitHub e15136ce51 Merge pull request #705 from Zathiel/staging
[SECURITY] Package update
2024-05-13 19:34:38 +05:30
prafull-opensignlabs 39c9211167 fix: pass parentFolderId as query parameter in folderlist API 2024-05-13 19:31:01 +05:30
AndrewandGitHub 64864e3f54 Merge pull request #729 from OpenSignLabs/signPdf 2024-05-13 18:25:15 +05:30
prafull-opensignlabs af0bef6d29 refactor: update response for folder APIs 2024-05-13 18:24:12 +05:30
AndrewandGitHub 130704b32f Merge pull request #728 from OpenSignLabs/signPdf 2024-05-13 17:09:23 +05:30
prafull-opensignlabsandGitHub 5b139b66a9 Merge pull request #727 from OpenSignLabs/raktima-opensignlabs-patch-6
feat: add help message button on send-in-order in request signature form
2024-05-13 17:07:06 +05:30
prafull-opensignlabs f9c5041af1 feat: add folder api 2024-05-13 17:02:37 +05:30
dependabot[bot]andGitHub 1e58b35db5 build(deps): bump @reduxjs/toolkit from 2.2.1 to 2.2.4 in /apps/OpenSign
Bumps [@reduxjs/toolkit](https://github.com/reduxjs/redux-toolkit) from 2.2.1 to 2.2.4.
- [Release notes](https://github.com/reduxjs/redux-toolkit/releases)
- [Commits](https://github.com/reduxjs/redux-toolkit/compare/v2.2.1...v2.2.4)

---
updated-dependencies:
- dependency-name: "@reduxjs/toolkit"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-13 10:51:45 +00:00
RaktimaNXG 245bf8046b feat: add help message on send-in-order in request signature form 2024-05-13 15:49:26 +05:30
AndrewandGitHub 94eb5f9efe Merge pull request #726 from OpenSignLabs/signPdf 2024-05-13 13:22:16 +05:30
prafull-opensignlabs 044128f36a feat: make entire folder selection box clickable 2024-05-13 13:21:32 +05:30
AndrewandGitHub 594383fe14 Merge pull request #725 from OpenSignLabs/signPdf 2024-05-13 11:50:03 +05:30
prafull-opensignlabs 90a585367e fix: change body parameter of create document api 2024-05-13 11:48:03 +05:30
prafull-opensignlabsandGitHub 0b8382df00 Merge pull request #723 from OpenSignLabs/raktima-opensignlabs-patch-5
fix: help button fridge in user's profile of disable documentId
2024-05-13 11:15:19 +05:30
RaktimaNXG a54c7e7fd7 fix: help button fridge in user profile ofdisable documentId 2024-05-13 10:57:53 +05:30
prafull-opensignlabsandGitHub 45a2c88e90 Merge pull request #718 from OpenSignLabs/raktima-opensignlabs-patch-4
fix: tour & model for email verification is shown simaltaneously in signyourself flow, navigate tour message on another page
2024-05-13 10:25:55 +05:30
AndrewandGitHub e4526f026b Merge pull request #720 from OpenSignLabs/signPdf 2024-05-10 21:48:25 +05:30
prafull-opensignlabs 11a2f6b8ec feat: add resend mail api, add expiry date in create document api & return certificate url in get document api 2024-05-10 21:07:06 +05:30
RaktimaNXG 3bf35166fc fix: bookmark color according to user's widget color 2024-05-10 20:27:04 +05:30
RaktimaNXG cd50c8cf20 Merge branch 'raktima-opensignlabs-patch-4' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-4 2024-05-10 19:31:42 +05:30
RaktimaNXG 055ef3f209 fix: min required checkbox validation issue 2024-05-10 19:28:53 +05:30
ugoconsonniandGitHub 964ec0d422 Update createDocumentwithCoordinate.js
fix: email and api return url server and not front
2024-05-10 15:22:00 +02:00
RaktimaandGitHub ec0e291604 Update RenderAllPdfPage.js 2024-05-10 18:16:11 +05:30
RaktimaNXG 836e952ba0 fix: add comment 2024-05-10 18:14:19 +05:30
RaktimaNXG c5f39e2a24 fix: tour & model for email verification is shown simaltaneously, issue of navigate tour message on another page to show warning complete signature 2024-05-10 16:59:58 +05:30
AmolandGitHub 498bd18630 Merge pull request #717 from OpenSignLabs/staging
v1.6.2
2024-05-10 16:02:39 +05:30
AndrewandGitHub 56dd060531 Merge pull request #716 from OpenSignLabs/fix_guestlogin 2024-05-10 15:14:19 +05:30
prafull-opensignlabs 960fbdb0eb fix: issue of owner subscription has expired in guest login 2024-05-10 15:10:57 +05:30
AmolandGitHub b530ef882a Merge pull request #715 from OpenSignLabs/staging
v1.6.1
2024-05-10 11:18:58 +05:30
AndrewandGitHub f8145feebc Merge pull request #714 from OpenSignLabs/prafull-opensignlabs-patch-17 2024-05-10 11:14:26 +05:30
prafull-opensignlabsandGitHub df38d15611 fix: error something went wrong while signing document in request sign 2024-05-10 11:12:29 +05:30
ZathielandGitHub bb69518674 Merge branch 'OpenSignLabs:staging' into staging 2024-05-09 18:08:05 +02:00
prafull-opensignlabsandGitHub fee53fb020 Merge pull request #713 from OpenSignLabs/revert-712-raktima-opensignlabs-path-3
Revert "fix: owner's subsciption expired issue in guest login"
2024-05-09 20:56:08 +05:30
prafull-opensignlabsandGitHub d086e60ae5 Revert "fix: owner's subsciption expired issue in guest login" 2024-05-09 20:55:30 +05:30
prafull-opensignlabsandGitHub 51150d4e47 Merge pull request #712 from OpenSignLabs/raktima-opensignlabs-path-3
fix: owner's subsciption expired issue in guest login
2024-05-09 20:34:44 +05:30
RaktimaNXG 1d3cd02f9d fix: owner's subsciption expired issue in guest login 2024-05-09 20:32:33 +05:30
AmolandGitHub 850d14064a Merge pull request #710 from OpenSignLabs/staging
v1.6.0
2024-05-09 16:38:17 +05:30
prafull-opensignlabsandGitHub d03515efc4 Merge pull request #708 from OpenSignLabs/raktima-opensignlabs-patch-2
feat: implemented a solution allowing for the simultaneous signing of a document by multiple signers
2024-05-09 16:36:37 +05:30
AmolandGitHub 30e6e4b3fd Merge pull request #709 from OpenSignLabs/prafull-opensignlabs-patch-16 2024-05-09 15:08:26 +05:30
prafull-opensignlabsandGitHub cbdd729e95 fix: pdf download is not working 2024-05-09 14:53:11 +05:30
RaktimaNXG ac82209d52 feat: implemented a solution allowing for the simultaneous signing of a document by multiple signers 2024-05-09 13:36:02 +05:30
RaktimaandGitHub d5cc6f6758 Merge pull request #707 from OpenSignLabs/prafull-opensignlabs-patch-15
refactor: change default cursor pointer to crosshair cursor pointer in debugUI
2024-05-09 12:33:50 +05:30
prafull-opensignlabsandGitHub 3aa211dc1a refactor: change default cursor pointer to crosshair cursor pointer in debugUI 2024-05-09 12:31:42 +05:30
prafull-opensignlabsandGitHub 33d5eecbbf Merge pull request #706 from OpenSignLabs/raktima-opensignlabs-patch-1
fix: can not access template before initialization
2024-05-09 12:00:25 +05:30
RaktimaNXG 1d6c108ba4 fix: can not access template before initialization 2024-05-09 11:58:57 +05:30
prafull-opensignlabsandGitHub 79da0f285d Merge pull request #704 from OpenSignLabs/raktima-opensignlabs-patch-1
fix:  integrate validation for the signature widget as mandatory using API
2024-05-09 11:36:36 +05:30
RaktimaNXG e8eef32958 fix: update condition for checking signature widget validation 2024-05-09 11:33:20 +05:30
ZathielandGitHub dc1fab5250 Merge pull request #14 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/ejs-3.1.10
build(deps): bump ejs from 3.1.9 to 3.1.10 in /apps/OpenSign
2024-05-08 23:21:58 +02:00
ZathielandGitHub 3e185ef796 Merge pull request #15 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/tar-6.2.1
build(deps): bump tar from 6.2.0 to 6.2.1 in /apps/OpenSign
2024-05-08 23:19:47 +02:00
ZathielandGitHub 8cf5adb273 Merge pull request #16 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/webpack-dev-middleware-5.3.4
build(deps): bump webpack-dev-middleware from 5.3.3 to 5.3.4 in /apps/OpenSign
2024-05-08 23:15:43 +02:00
ZathielandGitHub 803faed97b Merge pull request #17 from Zathiel/dependabot/npm_and_yarn/apps/OpenSign/react-pdf-7.7.3
build(deps): bump react-pdf from 7.7.1 to 7.7.3 in /apps/OpenSign
2024-05-08 23:13:12 +02:00
RaktimaNXG 660eaeb714 fix: integrate validation for the signature widget as mandatory using API 2024-05-08 19:25:21 +05:30
prafull-opensignlabsandGitHub 273ada837b Merge pull request #703 from OpenSignLabs/staging
v1.5.9
2024-05-08 16:12:46 +05:30
RaktimaandGitHub 79126700c4 Merge pull request #702 from OpenSignLabs/prafull-opensignlabs-patch-14
fix: issue of user name is visible in subject of mail instead of document title
2024-05-08 16:06:43 +05:30
prafull-opensignlabsandGitHub 6355c02485 Update createDocumentwithCoordinate.js 2024-05-08 16:00:02 +05:30
prafull-opensignlabsandGitHub bce028877b Fix: issue of user name is visible in subject of mail instead of document title 2024-05-08 15:58:51 +05:30
prafull-opensignlabsandGitHub e47211a8c0 Merge pull request #701 from OpenSignLabs/raktima-opensignlabs-patch-1
fix: modal design of signature validation in both flow signyour-self and request-sign
2024-05-08 15:57:01 +05:30
RaktimaNXG 4a4c9eaa2d refactor: remove line 2024-05-08 15:54:48 +05:30
RaktimaNXG 0a5653f53d fix: modal design of signature validation in both flow signyour-self and request-sign 2024-05-08 15:48:53 +05:30
AmolandGitHub 5a90a0f484 Merge pull request #700 from OpenSignLabs/prafull-opensignlabs-patch-13
refactor: update certificate generating message
2024-05-08 15:43:24 +05:30
prafull-opensignlabsandGitHub a3a398bd92 refactor: update certificate generating message 2024-05-08 15:38:27 +05:30
AmolandGitHub 7d70162137 Merge pull request #699 from OpenSignLabs/raktima-opensignlabs-patch-1 2024-05-08 14:13:48 +05:30
RaktimaNXG e4c62c4eeb feat : integrate validation for the signature widget as mandatory. 2024-05-08 14:12:18 +05:30
AmolandGitHub 3eadca01fa Merge pull request #698 from OpenSignLabs/prafull-opensignlabs-patch-12
fix: issue of showing originator in completion certificate's signer list when add only one signer
2024-05-08 12:55:15 +05:30
prafull-opensignlabsandGitHub 659b7fe0f4 fix: issue of showing originator in completion certificate's signer list when add only one signer 2024-05-08 12:54:01 +05:30
AmolandGitHub ade6694427 Merge pull request #695 from OpenSignLabs/signPdf
fix: issue of showing document in drafts report after creating from document API
2024-05-08 12:13:20 +05:30
RaktimaandGitHub f8160665a4 Merge pull request #697 from OpenSignLabs/prafull-opensignlabs-patch-10
fix: something went wrong error in guest login when document created through Document API
2024-05-08 12:11:12 +05:30
prafull-opensignlabsandGitHub 5778bdecae fix: something went wrong error in guest login when document created through Document API 2024-05-08 12:10:11 +05:30
prafull-opensignlabs 9122c71c90 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-08 12:00:53 +05:30
prafull-opensignlabs 2c2065aca8 fix: issue of showing document in drafts report after creating from document API 2024-05-08 11:03:38 +05:30
dependabot[bot]andGitHub b1dd4df3c0 build(deps): bump react-pdf from 7.7.1 to 7.7.3 in /apps/OpenSign
Bumps [react-pdf](https://github.com/wojtekmaj/react-pdf/tree/HEAD/packages/react-pdf) from 7.7.1 to 7.7.3.
- [Release notes](https://github.com/wojtekmaj/react-pdf/releases)
- [Commits](https://github.com/wojtekmaj/react-pdf/commits/v7.7.3/packages/react-pdf)

---
updated-dependencies:
- dependency-name: react-pdf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-07 17:03:30 +00:00
AmolandGitHub 64fd3580ac Merge pull request #691 from OpenSignLabs/all-contributors/add-daniel-mutwiri
docs: add daniel-mutwiri as a contributor for code
2024-05-07 20:42:06 +05:30
AmolandGitHub 01273777b7 Merge branch 'staging' into all-contributors/add-daniel-mutwiri 2024-05-07 20:41:56 +05:30
AmolandGitHub 23eaa1ae81 Merge pull request #690 from OpenSignLabs/all-contributors/add-Zathiel
docs: add Zathiel as a contributor for code
2024-05-07 20:37:35 +05:30
allcontributors[bot]andGitHub 3fd0613407 docs: update .all-contributorsrc 2024-05-07 15:07:21 +00:00
allcontributors[bot]andGitHub d45566257f docs: update README.md 2024-05-07 15:07:20 +00:00
allcontributors[bot]andGitHub e9eb59aa7a docs: update .all-contributorsrc 2024-05-07 15:06:43 +00:00
allcontributors[bot]andGitHub 9e3ba2cf5c docs: update README.md 2024-05-07 15:06:42 +00:00
AmolandGitHub 81afc92c14 Merge pull request #689 from OpenSignLabs/all-contributors/add-ugoconsonni
docs: add ugoconsonni as a contributor for code
2024-05-07 20:36:07 +05:30
allcontributors[bot]andGitHub d3bf9e9e9b docs: update .all-contributorsrc 2024-05-07 15:05:51 +00:00
allcontributors[bot]andGitHub 18e25f6cd9 docs: update README.md 2024-05-07 15:05:50 +00:00
AmolandGitHub a6d97d587f Merge pull request #688 from OpenSignLabs/main
Main
2024-05-07 20:04:14 +05:30
AmolandGitHub ff9a495775 Merge pull request #656 from OpenSignLabs/staging
v1.5.8
2024-05-07 20:02:52 +05:30
AmolandGitHub b29c7d5c7f Merge pull request #687 from OpenSignLabs/revert-680-documentLoad_issue
Revert "fix: document loading error No PDF file specified"
2024-05-07 20:00:41 +05:30
AmolandGitHub fddeba8d44 Revert "fix: document loading error No PDF file specified" 2024-05-07 20:00:26 +05:30
prafull-opensignlabsandGitHub c564b5741c Merge pull request #686 from OpenSignLabs/guestsign_url
fix: document loading error No PDF file specified
2024-05-07 19:59:20 +05:30
RaktimaNXG 179c3c5dfe fix: document loading error No PDF file specified 2024-05-07 19:57:39 +05:30
prafull-opensignlabsandGitHub 221f71c061 Merge pull request #685 from OpenSignLabs/guestsign_url
fix: x is not defined error in createDocumentWithTemplate and createDocumentwithCoordinate function
2024-05-07 19:32:19 +05:30
RaktimaNXG a26e65f76f fix: x is not defined error in createDocumentWithTemplate and createDocumentwithCoordinate function 2024-05-07 19:29:09 +05:30
prafull-opensignlabsandGitHub 9ba05d23fd Merge pull request #676 from OpenSignLabs/guestsign_url
fix: issue of url encoded parameter in guest login url
2024-05-07 18:53:27 +05:30
RaktimaNXG 0f67013005 refactor: remove commented code and remove extra condition 2024-05-07 18:30:45 +05:30
RaktimaNXG 65d5c7f337 refactor: remove commented code and remove extra condition 2024-05-07 18:30:06 +05:30
AndrewandGitHub a7dd47f1b1 Merge pull request #683 from OpenSignLabs/signPdf 2024-05-07 16:08:50 +05:30
prafull-opensignlabs 350212c02f refactor: changes in subject of request signature mail 2024-05-07 16:06:31 +05:30
RaktimaNXG d9126d6701 refactor: add comment 2024-05-07 16:04:47 +05:30
RaktimaNXG 51c3a0f71a fix: in opensign-drive unnecessary render tour message and onclick same folder route then it show no data found issue 2024-05-07 15:55:46 +05:30
RaktimaNXG a4bf9ff915 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into guestsign_url 2024-05-07 14:39:44 +05:30
AmolandGitHub 28a2a113e2 Merge pull request #682 from OpenSignLabs/signPdf
fix: issue of showing currently encrypted file not supported when try to sign document
2024-05-07 14:22:35 +05:30
prafull-opensignlabs 07382822bd fix: issue of showing currently incrypted file not supported when try to sign document 2024-05-07 13:39:39 +05:30
prafull-opensignlabsandGitHub 06677d923c Merge pull request #680 from OpenSignLabs/documentLoad_issue
fix: document loading error No PDF file specified
2024-05-07 12:24:38 +05:30
AmolandGitHub 534dc3d3f5 Merge pull request #681 from OpenSignLabs/revert-679-document_load
Revert "fix: Document loading error `No PDF file specified`"
2024-05-07 12:21:01 +05:30
AmolandGitHub a69682cebf Revert "fix: Document loading error No PDF file specified" 2024-05-07 12:20:43 +05:30
RaktimaNXG 2ae7e8ced2 fix: document loading error No PDF file specified 2024-05-07 11:53:17 +05:30
prafull-opensignlabsandGitHub e20b75cfc1 Merge pull request #679 from OpenSignLabs/document_load
fix: Document loading error `No PDF file specified`
2024-05-07 11:52:43 +05:30
RaktimaNXG 37b73e36ca fix: document error No PDF file specified 2024-05-07 11:42:48 +05:30
prafull-opensignlabsandGitHub cc6b64d140 Merge pull request #678 from daniel-mutwiri/staging
Fix: draftDocument.js saveFileUsage is not defined.
2024-05-07 11:35:59 +05:30
Daniel MutwiriandGitHub 4b765a8e1d Merge pull request #1 from gitonga123/fix/draftdocument
Bug Fix:  draftDocument.js saveFileUsage is not defined.
2024-05-07 08:59:45 +03:00
Daniel Mutwiri 4a7108e4f6 Bug Fix: saveFileUsage is not defined.
saveFileUsage imported from Utils.js
2024-05-07 08:48:55 +03:00
RaktimaNXG 66bf577676 refactor: add comment 2024-05-07 09:54:10 +05:30
dependabot[bot]andGitHub 13e2d92ce6 build(deps): bump webpack-dev-middleware in /apps/OpenSign
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 5.3.3 to 5.3.4.
- [Release notes](https://github.com/webpack/webpack-dev-middleware/releases)
- [Changelog](https://github.com/webpack/webpack-dev-middleware/blob/v5.3.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-middleware/compare/v5.3.3...v5.3.4)

---
updated-dependencies:
- dependency-name: webpack-dev-middleware
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-07 02:26:18 +00:00
dependabot[bot]andGitHub 4b9f22a5bc build(deps): bump tar from 6.2.0 to 6.2.1 in /apps/OpenSign
Bumps [tar](https://github.com/isaacs/node-tar) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: tar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-07 02:25:30 +00:00
dependabot[bot]andGitHub 77c61c89a2 build(deps): bump ejs from 3.1.9 to 3.1.10 in /apps/OpenSign
Bumps [ejs](https://github.com/mde/ejs) from 3.1.9 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.9...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-07 02:24:33 +00:00
prafull-opensignlabsandGitHub d04b79001a Merge pull request #675 from Zathiel/staging
Package Update
2024-05-06 19:01:09 +05:30
AmolandGitHub e14b2a2a17 Merge pull request #674 from OpenSignLabs/signPdf 2024-05-06 18:54:23 +05:30
RaktimaNXG 6afa97a76b fix: encode in base64 of guest signing url and decode it in guest login page 2024-05-06 18:54:07 +05:30
RaktimaNXG 3a28b1cdd7 fix: encode in base64 of guest signing url and decode it in guest login page 2024-05-06 18:51:47 +05:30
Zathiel | Sys 23b48838ae deps : upgrade prettier, pretty-quick, postcss, autoprefixer, @babel/runtime-corejs2, reactour, react-rnd, @react-pdf/renderer, @formkit/auto-animate 2024-05-06 13:18:57 +00:00
Zathiel | Sys bddebf2758 deps : upgrade pdfkit, nodemailer, mongoose, express, eslint package version 2024-05-06 13:18:04 +00:00
prafull-opensignlabs 5a68e18c48 fix: pdf url are not downloadable after some time 2024-05-06 18:46:12 +05:30
dependabot[bot]andGitHub 280b812e5b build(deps-dev): bump @babel/eslint-parser in /apps/OpenSignServer
Bumps [@babel/eslint-parser](https://github.com/babel/babel/tree/HEAD/eslint/babel-eslint-parser) from 7.24.1 to 7.24.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.24.5/eslint/babel-eslint-parser)

---
updated-dependencies:
- dependency-name: "@babel/eslint-parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-06 12:51:25 +00:00
AndrewandGitHub 082f0569e2 Merge pull request #673 from OpenSignLabs/signPdf 2024-05-06 17:18:32 +05:30
prafull-opensignlabs b01618c8ca fix: Ip address overlap with security level in certificate 2024-05-06 17:16:28 +05:30
AndrewandGitHub 7b0f0d5b9f Merge pull request #672 from OpenSignLabs/signPdf 2024-05-06 15:19:52 +05:30
prafull-opensignlabs 1381a65c0b Merge branch 'signPdf' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-06 15:18:02 +05:30
prafull-opensignlabs 961ec2b2ba fix: pdf password modal is not closeable in form 2024-05-06 15:17:53 +05:30
prafull-opensignlabsandGitHub 022cb20b64 Merge pull request #662 from OpenSignLabs/signPdf
fix: verify email otp varification for inProgress reports
2024-05-06 14:36:56 +05:30
RaktimaNXG 7b4fa4fe23 fix: design of verify email 2024-05-06 13:55:15 +05:30
prafull-opensignlabs c85c21ef8a Merge branch 'signPdf' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-06 13:31:43 +05:30
prafull-opensignlabs 09fb01862d feat: handle encrypted pdf 2024-05-06 13:29:31 +05:30
RaktimaNXG f6ce1b45fb refactor: add comment for email verify functions 2024-05-06 12:11:48 +05:30
RaktimaNXG 8fd2d4210e refactor : add comment, handle unnecessary query call 2024-05-06 11:32:30 +05:30
prafull-opensignlabsandGitHub 0f7f1f1ba2 Merge pull request #667 from ugoconsonni/patch-3
Update sendMailv3.js
2024-05-06 10:08:02 +05:30
ugoconsonniandGitHub 8693ef29f6 Update sendMailv3.js
Fix: [Bug]: createTransport was not imported #666
2024-05-05 21:51:36 +02:00
AmolandGitHub dc67d2c23d Merge pull request #664 from OpenSignLabs/fix_login
fix: login with google issue
2024-05-05 18:29:40 +05:30
prafull-opensignlabs 761925fced fix: login issue 2024-05-05 18:25:50 +05:30
RaktimaNXG c240eb133d fix: add verify email otp verification in signyour-self flow 2024-05-05 15:53:51 +05:30
RaktimaNXG 9daed4a5ed refactor : remove commented code 2024-05-03 20:19:38 +05:30
RaktimaNXG ee18f82fa9 fix: verify email otp varification for inProgress reports 2024-05-03 20:11:57 +05:30
AndrewandGitHub 4085f8259d Merge pull request #661 from OpenSignLabs/signPdf 2024-05-03 18:31:39 +05:30
prafull-opensignlabs 98f84f6cc8 fix: update description of plan 2024-05-03 18:15:28 +05:30
prafull-opensignlabs da9363f588 fix: update description of plan 2024-05-03 18:11:50 +05:30
AndrewandGitHub 5afb60a687 Merge pull request #660 from OpenSignLabs/signPdf 2024-05-03 18:09:45 +05:30
prafull-opensignlabs a18733ad79 fix: show correct document creation date on certificate 2024-05-03 18:08:53 +05:30
AndrewandGitHub 400e132f11 Merge pull request #659 from OpenSignLabs/signPdf 2024-05-03 17:23:40 +05:30
prafull-opensignlabs c56830b7be fix: show correct ipv4 address on completion certificate 2024-05-03 17:14:26 +05:30
AndrewandGitHub f988c25491 Merge pull request #658 from OpenSignLabs/signPdf 2024-05-03 15:51:57 +05:30
RaktimaNXG 53bd13e03b fix: signature is not visiable in completion certificate 2024-05-03 15:50:52 +05:30
prafull-opensignlabs 68edecae18 fix: issue of encryptedpdf error api not working 2024-05-03 14:26:54 +05:30
prafull-opensignlabs b421c0f1a9 fix: remove uncessary import statement 2024-05-03 14:05:00 +05:30
prafull-opensignlabs 3858746d64 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-03 13:24:02 +05:30
prafull-opensignlabs 41e8aa18a7 feat: add pdftopdf and wordtopdf functionality 2024-05-03 13:20:31 +05:30
AmolandGitHub fb8915e9db Merge pull request #654 from OpenSignLabs/encrypted_pdf
refactor: add encrypted pdf error event
2024-05-02 00:04:24 +05:30
AmolandGitHub ba2bb07aa9 Merge pull request #655 from OpenSignLabs/staging
v1.5.7
2024-05-02 00:02:02 +05:30
prafull-opensignlabs 74083d0606 refactor: add encrypted pdf error event 2024-05-01 11:36:03 +05:30
prafull-opensignlabs 0f2862c52e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into signPdf 2024-05-01 10:30:46 +05:30
AndrewandGitHub 61e11b7105 Merge pull request #652 from OpenSignLabs/encrypted_pdf 2024-04-30 21:30:07 +05:30
prafull-opensignlabs d7de8648e9 fix: stuck on loader screen while sending pdf for signing 2024-04-30 19:43:11 +05:30
prafull-opensignlabs c279fd6525 feat: add email verification in user profile 2024-04-29 21:12:54 +05:30
prafull-opensignlabs 3599fbee21 fix: changes in certificate 2024-04-29 17:38:25 +05:30
prafull-opensignlabsandGitHub 9ad51a1d6a Merge pull request #648 from OpenSignLabs/fix_issue
fix: send wrong user activeMailAdapter data in sendmailv3 and signPdf function
2024-04-26 18:05:13 +05:30
RaktimaNXG 03e22dbf33 refactor: remove comment 2024-04-26 17:49:03 +05:30
RaktimaNXG 5693fa230b fix: send wrong user activeMailAdapter data in sendmailv3 and signPdf function 2024-04-26 17:45:22 +05:30
RaktimaNXG 8cd90911a8 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into fix_issue 2024-04-26 17:22:50 +05:30
prafull-opensignlabsandGitHub 7cf820d205 Merge pull request #645 from OpenSignLabs/signPdf
fix: document not found
2024-04-25 12:42:51 +05:30
prafull-opensignlabs 4d01547fd5 fix: document not found 2024-04-25 12:39:49 +05:30
AndrewandGitHub 6597f67f0a Merge pull request #644 from OpenSignLabs/signPdf 2024-04-25 12:25:48 +05:30
prafull-opensignlabs 14ce91350e fix: missing credintials in config aws_config_file 2024-04-25 12:16:45 +05:30
RaktimaNXG 7a5f75ea79 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into fix_issue 2024-04-24 18:05:40 +05:30
AmolandGitHub a44cb7168f Merge pull request #643 from OpenSignLabs/staging
v1.5.6
2024-04-24 17:53:59 +05:30
prafull-opensignlabsandGitHub 1c6b056d6c Merge pull request #642 from OpenSignLabs/signPdf
fix: freeplan users are forcefully navigate to subscription
2024-04-24 17:05:58 +05:30
prafull-opensignlabs cf6759ce79 fix: handle freeplan users are forcefully navigate to subscription 2024-04-24 17:03:45 +05:30
AmolandGitHub d422f46f47 Merge pull request #641 from OpenSignLabs/staging
v1.5.5
2024-04-24 12:10:12 +05:30
AndrewandGitHub 85f94c8ffa Merge pull request #640 from OpenSignLabs/prafull-opensignlabs-patch-8 2024-04-24 10:57:57 +05:30
prafull-opensignlabsandGitHub 91da331ce6 fix: after connected to gmail mail is not sending from personal mail 2024-04-24 10:33:17 +05:30
AmolandGitHub 3db6946198 Merge pull request #639 from OpenSignLabs/staging
v1.5.4
2024-04-23 16:13:35 +05:30
AmolandGitHub 94c8caa3e6 Merge pull request #638 from OpenSignLabs/prafull-opensignlabs-patch-7
fix: redirect_uri mismatch in googleauth
2024-04-23 16:13:01 +05:30
prafull-opensignlabsandGitHub ed1120927c fix: redirect_uri mismatch in googleauth 2024-04-23 12:31:47 +05:30
AmolandGitHub 79b4c38587 Merge pull request #637 from OpenSignLabs/staging
v1.5.3
2024-04-23 12:04:51 +05:30
AmolandGitHub 2cc5517bc1 Merge pull request #636 from OpenSignLabs/prafull-opensignlabs-patch-7
fix : Update googleauth.js
2024-04-23 11:57:09 +05:30
prafull-opensignlabsandGitHub eddf70f102 Update googleauth.js 2024-04-23 11:56:20 +05:30
AmolandGitHub b6774519f9 Merge pull request #635 from OpenSignLabs/staging
v1.5.2
2024-04-23 10:53:39 +05:30
RaktimaNXG d6bb83f997 fix: remove conosle log 2024-04-22 18:56:28 +05:30
AndrewandGitHub 55e8042b0a Merge pull request #634 from OpenSignLabs/fix_issue 2024-04-22 18:56:20 +05:30
RaktimaNXG d5bc2df20f feat: add mailProvider parameter in sendmailv3 function and signPdf function 2024-04-22 18:53:41 +05:30
prafull-opensignlabsandGitHub c623e868f7 Merge pull request #633 from OpenSignLabs/fix_issue
fix:  subscription issue in guest login flow
2024-04-22 17:52:46 +05:30
RaktimaNXG 174b9b9941 fix: fix subscription issue 2024-04-22 17:45:48 +05:30
prafull-opensignlabsandGitHub 0eb247ae0c Merge pull request #632 from OpenSignLabs/fix_issue
fix: update getSubscription cloud function and handle subscription details for guest login flow
2024-04-22 17:30:22 +05:30
RaktimaNXG f96df699e4 fix: update getSubscription cloud function for guest login flow and handle subscription details 2024-04-22 17:26:14 +05:30
AmolandGitHub dcaca98aec Merge pull request #631 from OpenSignLabs/staging
v1.5.2
2024-04-22 16:57:12 +05:30
AmolandGitHub d70f4dddb6 Merge pull request #629 from OpenSignLabs/fix_issue
fix: text could not be selectable from validation dropdown issue
2024-04-22 16:21:23 +05:30
AmolandGitHub db34c4248c Merge pull request #630 from OpenSignLabs/gmail_provider
feat: add google mail provider
2024-04-22 15:42:11 +05:30
prafull-opensignlabs 1d39db6d7d refactor: change parameter for custom mail adapter 2024-04-22 14:22:55 +05:30
RaktimaNXG 654dadce93 fix: text could not be selectable from validation dropdown issue 2024-04-22 12:14:54 +05:30
prafull-opensignlabsandGitHub e578981791 Merge pull request #628 from OpenSignLabs/fix_issue
fix: remove validation for text input widgets
2024-04-22 11:32:58 +05:30
RaktimaNXG c4297a260e fix: remove validation for text input widgets 2024-04-22 11:17:55 +05:30
AmolandGitHub ecc453957c Merge pull request #625 from OpenSignLabs/staging
v1.5.1
2024-04-21 16:02:53 +05:30
AndrewandGitHub 498e0c6be6 Merge pull request #624 from OpenSignLabs/fix_issue 2024-04-21 11:08:48 +05:30
RaktimaNXG b4d73b2b44 fix: text input validation issue in template flow 2024-04-21 10:30:33 +05:30
prafull-opensignlabs f14c98505a feat: provide send mail through user gmail 2024-04-19 20:56:43 +05:30
AmolandGitHub 3f23bdd372 Merge pull request #622 from OpenSignLabs/staging
v1.5.1
2024-04-18 22:07:23 +05:30
prafull-opensignlabsandGitHub 440d253124 Merge pull request #621 from OpenSignLabs/fix_issue
fix: text input validation issue if user not select any validation
2024-04-18 19:40:39 +05:30
RaktimaNXG 2b6bf7bcaa fix: server url and parse appid issue to save file in parters classess 2024-04-18 19:25:21 +05:30
RaktimaNXG cb6d9e9b9b fix: text input validation issue if user not select any validation or free user also 2024-04-18 17:20:44 +05:30
prafull-opensignlabsandGitHub ebeeedc96c Merge pull request #620 from OpenSignLabs/fix_issue
fix: change the message to show for user who is not the last signer after signs the document
2024-04-18 15:42:14 +05:30
RaktimaNXG 5cecf3390b fix: change the message to show for user who is not the last signer after signs the document 2024-04-18 15:24:29 +05:30
prafull-opensignlabsandGitHub 9890d568fd Merge pull request #619 from OpenSignLabs/fix_issue
feat: add tour message on add role button when user try to add widget without add any role
2024-04-18 14:46:51 +05:30
RaktimaNXG 06bcae64be feat: add tour message on add role button when user try to add widget without add any role 2024-04-18 14:39:32 +05:30
RaktimaandGitHub fb017c4bcd Merge pull request #618 from OpenSignLabs/patch-7
refactor: change widget warning
2024-04-18 14:29:17 +05:30
prafull-opensignlabs 01d3edd262 refactor: change user icon tour message 2024-04-18 14:17:28 +05:30
prafull-opensignlabs 41a025715e refactor: change widget warning 2024-04-18 14:14:57 +05:30
prafull-opensignlabsandGitHub 306768fbd2 Merge pull request #617 from OpenSignLabs/fix_issue
fix: input number type validation issue with space and fix send mail modal design
2024-04-18 14:04:59 +05:30
RaktimaNXG d82da53258 fix: input number type validation issue with space and fix send mail modal design 2024-04-18 14:02:33 +05:30
prafull-opensignlabsandGitHub 673245df02 Merge pull request #616 from OpenSignLabs/encode_url
fix: encode url parameters
2024-04-18 12:51:29 +05:30
prafull-opensignlabs 42549e5792 fix: encode url parameters 2024-04-18 12:49:24 +05:30
AndrewandGitHub 493731a6af Merge pull request #615 from OpenSignLabs/staging 2024-04-17 21:15:27 +05:30
prafull-opensignlabsandGitHub cc8b4b7157 Merge pull request #614 from OpenSignLabs/fix_issue
fix: onclick upgrade to customize email button redirect new window of subscription page
2024-04-17 21:05:01 +05:30
RaktimaNXG 4cbd7e74f7 fix: onclick upgrade to customize email button redirect new window of subscription page 2024-04-17 21:02:05 +05:30
prafull-opensignlabsandGitHub b300a5bb39 Merge pull request #613 from OpenSignLabs/fix_issue
fix : logo issue in guest login flow
2024-04-17 20:37:26 +05:30
RaktimaNXG 5422fefc54 fix: add default logo from assets 2024-04-17 20:34:48 +05:30
RaktimaNXG 248b0cac0b fix : logo issue in guest login flow 2024-04-17 20:28:18 +05:30
prafull-opensignlabsandGitHub a388a7c345 Merge pull request #612 from OpenSignLabs/fix_issue
fix: issue of signUrl and subscription details in guest login flow
2024-04-17 20:14:47 +05:30
RaktimaNXG fc564ca37b fix: check subscription details issue in guest login flow 2024-04-17 20:11:19 +05:30
RaktimaNXG 37b2e4c97a fix: in page section show signed pdf after sign successfully 2024-04-17 19:52:36 +05:30
prafull-opensignlabsandGitHub a3bc9817e7 Merge pull request #611 from OpenSignLabs/feat_report
fix: z-index of loader in report
2024-04-17 19:42:51 +05:30
prafull-opensignlabs 8a216e6c0d fix: z-index of loader in report 2024-04-17 19:42:12 +05:30
prafull-opensignlabsandGitHub 613b1cfe07 Merge pull request #610 from OpenSignLabs/fix_issue
fix: invalid document url convert in arrayBuffer issue
2024-04-17 19:19:49 +05:30
RaktimaNXG ee77534e35 fix: change enter label to enter text for text widget plaaceholder 2024-04-17 19:12:56 +05:30
RaktimaNXG 737b33db1e fix: add try catch to convert pdf array buffer 2024-04-17 19:04:57 +05:30
RaktimaNXG ac214d0467 fix: invalid document url convert in arrayBuffer issue 2024-04-17 18:26:12 +05:30
prafull-opensignlabsandGitHub f8b21e7a93 Merge pull request #609 from OpenSignLabs/fix_issue
fix: opensign-drive and template report tour message
2024-04-17 16:55:02 +05:30
RaktimaNXG 7e6df13b71 fix: change template report option tour message 2024-04-17 16:53:18 +05:30
RaktimaNXG 3e584cec1a feat: add console application url in header dropdown list 2024-04-17 16:38:47 +05:30
RaktimaNXG bc75dd109f fix: remove console log 2024-04-17 16:17:25 +05:30
RaktimaNXG 6b3abed782 fix:template report tour messsage 2024-04-17 16:14:21 +05:30
RaktimaNXG f163958cd0 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into fix_issue 2024-04-17 14:36:36 +05:30
RaktimaNXG ecd461b96c fix: remove changes 2024-04-17 14:36:13 +05:30
RaktimaNXG 1371c2c09a fix: opensign-drive tour message error andtemplate report tour message 2024-04-17 14:30:53 +05:30
AndrewandGitHub 3121f35158 Merge pull request #606 from OpenSignLabs/prafull-opensignlabs-patch-6 2024-04-17 12:13:30 +05:30
prafull-opensignlabsandGitHub 3c0db9473c feat: send custom completion mail if available 2024-04-17 12:12:08 +05:30
prafull-opensignlabsandGitHub 4f2b72dd4d Merge pull request #604 from OpenSignLabs/fix_issue
fix: remove add recipient button from signyour-self flow in mobile view
2024-04-16 20:03:52 +05:30
AmolandGitHub 7d9af1db95 Merge pull request #605 from OpenSignLabs/feat_report
style: change design of buttons in report
2024-04-16 19:55:32 +05:30
prafull-opensignlabs 0e38783560 style: change design of buttons in report 2024-04-16 19:54:22 +05:30
RaktimaNXG 6880ae35a8 fix: remove add recipient button from signyour-self flow in mobile view 2024-04-16 19:31:21 +05:30
prafull-opensignlabsandGitHub 9a07c67b69 Merge pull request #602 from OpenSignLabs/report_decline
feat: add revoke button in inprogress report to revoke/decline document
2024-04-16 17:28:01 +05:30
RaktimaandGitHub 33bfdeef15 Update GetReportDisplay.js 2024-04-16 17:27:26 +05:30
RaktimaandGitHub a404247604 Update PdfRequestFiles.js 2024-04-16 17:02:11 +05:30
RaktimaandGitHub c48579daa7 Update GetReportDisplay.js 2024-04-16 16:59:18 +05:30
AmolandGitHub 509f8f9a07 Merge pull request #603 from OpenSignLabs/plan_sub
refactor: update extra-info of user
2024-04-16 15:59:27 +05:30
prafull-opensignlabs 5b85d5b277 refactor: update extrainfo of user 2024-04-16 15:57:37 +05:30
RaktimaNXG bbf3170398 feat: add revoke button in inprogress report to revoke/decline document 2024-04-16 15:01:36 +05:30
prafull-opensignlabsandGitHub c172a7c55c Merge pull request #598 from OpenSignLabs/tour_message
feat : add recipient button in placeholder flow to add new signers in document signer's list
2024-04-16 14:01:28 +05:30
RaktimaNXG 90f3e0066b fix: remove already attached signers from add user link modal in dropdown 2024-04-16 13:36:10 +05:30
RaktimaNXG b6e028a2c0 fix: add comment 2024-04-16 13:09:30 +05:30
RaktimaNXG 11384192ba refactor code 2024-04-16 12:08:50 +05:30
prafull-opensignlabsandGitHub 9df63265fb Merge branch 'staging' into tour_message 2024-04-16 12:02:42 +05:30
RaktimaNXG ff6a8ce755 fix: customize email is not working for pro user in placeholder flow 2024-04-16 11:06:02 +05:30
AmolandGitHub 60c1aa253a Merge pull request #601 from OpenSignLabs/plan_sub 2024-04-15 19:49:49 +05:30
prafull-opensignlabs ad2fec0d66 fix: save planname and next billing date 2024-04-15 19:43:40 +05:30
AmolandGitHub 0451a6cae0 Merge pull request #600 from OpenSignLabs/plan_sub 2024-04-15 19:15:40 +05:30
prafull-opensignlabs 0d5f8981fe refactor: change in checking subscription 2024-04-15 19:14:24 +05:30
RaktimaNXG cfd8be0eee fix: add delete button to delete signers list in request send flow 2024-04-15 18:42:03 +05:30
RaktimaNXG 86e5ef6ef5 fix: invalid session token issue in recipient signature flow 2024-04-15 16:30:01 +05:30
RaktimaNXG 1af47c8425 fix: recipient color mismatch issue 2024-04-15 16:01:11 +05:30
AmolandGitHub f0395cb2c7 Merge pull request #599 from OpenSignLabs/feat_report
feat: add delete button in all reports and share button in in-progess report
2024-04-15 15:25:21 +05:30
prafull-opensignlabs 598aa2975f fix: share link not generate correctly 2024-04-15 14:28:23 +05:30
prafull-opensignlabs 0742291216 feat: add delete button in reports 2024-04-15 13:20:15 +05:30
RaktimaNXG 94eeceeaf9 feat : add recipient button in placeholder flow to add new signers in document signer's list 2024-04-15 12:58:16 +05:30
prafull-opensignlabsandGitHub 970fbecb7b Merge pull request #592 from OpenSignLabs/tour_message
fix: issue of return result when subscription result empty in getSubscription cloud function
2024-04-12 17:54:01 +05:30
RaktimaNXG 361e082930 fix: issue of return result when subscription result empty 2024-04-12 17:51:40 +05:30
AndrewandGitHub 3f9b6eb5a9 Merge pull request #591 from OpenSignLabs/plan 2024-04-12 17:24:33 +05:30
prafull-opensignlabs 2265bee61f feat: add widgets in API 2024-04-12 17:13:27 +05:30
prafull-opensignlabsandGitHub 6390706d8d Merge pull request #590 from OpenSignLabs/tour_message
fix: auto sign issue, change template report and open sign drive tour field name
2024-04-12 15:12:03 +05:30
RaktimaNXG 37e03c1026 fix: update getSubscriptions cloud function 2024-04-12 15:01:29 +05:30
RaktimaNXG c6fa6c710d fix: auto sign issue , change template report and sopen sign drive tour field name 2024-04-12 11:32:51 +05:30
AndrewandGitHub 2e921e30b6 Merge pull request #588 from OpenSignLabs/plan 2024-04-11 16:02:16 +05:30
prafull-opensignlabs 686a773d34 fix: update suibscription issue 2024-04-11 16:01:14 +05:30
AndrewandGitHub b03e1b8f9d Merge pull request #587 from OpenSignLabs/plan 2024-04-11 13:54:37 +05:30
prafull-opensignlabs f1ceae794f fix: logo fetch issue 2024-04-11 13:40:08 +05:30
AndrewandGitHub 5a8bcb8f9a Merge pull request #586 from OpenSignLabs/plan 2024-04-11 11:23:42 +05:30
prafull-opensignlabs ce70da7d8e fix: not able to get response of invoices 2024-04-11 11:14:37 +05:30
AmolandGitHub db03dfa641 Merge pull request #584 from OpenSignLabs/tour_message
feat: add tour message for template report and opensign drive
2024-04-10 19:02:39 +05:30
RaktimaNXG 7e70a5e111 remove console 2024-04-10 18:48:58 +05:30
RaktimaNXG f5f18f1630 fix: change tour message 2024-04-10 18:47:08 +05:30
AmolandGitHub fa6856c649 Merge pull request #583 from OpenSignLabs/user_profile 2024-04-10 16:28:14 +05:30
RaktimaNXG 3b4946e47b feat: add company and job title in user profile for modification 2024-04-10 16:21:17 +05:30
RaktimaNXG 05fe68d458 feat : add tour message on template report and opensign-drive 2024-04-10 15:57:05 +05:30
AmolandGitHub 2f3cdca821 Merge pull request #582 from OpenSignLabs/custom_logo 2024-04-10 11:04:02 +05:30
RaktimaNXG 4d8a54a6c5 fix: remove style attribute and use classname of tailwind in share signers list 2024-04-10 10:51:03 +05:30
RaktimaNXG 4fe5bdaad8 fix: signers list issue in share option 2024-04-10 10:45:59 +05:30
AmolandGitHub b95ede5273 Merge pull request #581 from OpenSignLabs/custom_logo 2024-04-09 19:16:39 +05:30
RaktimaNXG 5b2684a9f4 refactor: remove unnecessary code 2024-04-09 18:52:34 +05:30
RaktimaNXG d779720f26 implement share button option to share recipient's sign url after finishing a doc creation using request signature flow 2024-04-09 18:48:32 +05:30
AmolandGitHub 3cf8029978 Merge pull request #580 from OpenSignLabs/plan
chore: add test plan
2024-04-09 17:40:16 +05:30
prafull-opensignlabs 0bb857a942 chore: update url 2024-04-09 17:37:43 +05:30
prafull-opensignlabs 103d32edcb chore: add test plan 2024-04-09 17:35:26 +05:30
AmolandGitHub eca6ad374b Merge pull request #578 from OpenSignLabs/patch-1
fix: uncaught error in analytics
2024-04-09 15:24:48 +05:30
prafull-opensignlabs d2efe88266 fix: uncaught error in anaytics 2024-04-09 15:22:23 +05:30
prafull-opensignlabsandGitHub 450e5ec7f7 Merge pull request #577 from OpenSignLabs/patch-1
fix: path issue in server
2024-04-09 13:37:22 +05:30
prafull-opensignlabs 3bbbda91b8 fix: path issue in server 2024-04-09 13:35:23 +05:30
prafull-opensignlabsandGitHub a48db306bb Merge pull request #576 from OpenSignLabs/patch-1
fix: save invoice and payment analytics
2024-04-09 11:30:34 +05:30
prafull-opensignlabsandGitHub 2a60b30c75 Merge pull request #575 from OpenSignLabs/custom_logo
fix:  variables are not getting replace in custom email issue
2024-04-09 11:30:25 +05:30
prafull-opensignlabs 19cfa9d27d fix save invoice and payment analytics 2024-04-09 11:29:33 +05:30
RaktimaNXG 0fc12c957e fix: variables are not getting replace in custom email 2024-04-09 10:51:18 +05:30
prafull-opensignlabsandGitHub 6fd332f8b4 Merge pull request #568 from OpenSignLabs/custom_logo
feat: implement custom email template of request email and completion email
2024-04-08 20:26:06 +05:30
AmolandGitHub 39f7ccecaa Merge pull request #574 from OpenSignLabs/patch-1 2024-04-08 20:25:20 +05:30
prafull-opensignlabs 3bd306809b fix: change path of favicon and manifest 2024-04-08 20:24:32 +05:30
RaktimaNXG 04e9d13206 fix: change costomize upgrade message 2024-04-08 19:39:57 +05:30
AmolandGitHub b24739d61b Merge pull request #573 from OpenSignLabs/patch-1 2024-04-08 19:38:01 +05:30
prafull-opensignlabs 9dda211605 fix: classname of save subscription analytics 2024-04-08 19:35:00 +05:30
AmolandGitHub 93708a6875 Merge pull request #572 from OpenSignLabs/patch-1 2024-04-08 19:27:31 +05:30
prafull-opensignlabs e78f6f6df4 fix: save subscription analytics 2024-04-08 19:26:57 +05:30
AmolandGitHub ccaaca75ba Merge pull request #571 from OpenSignLabs/patch-1 2024-04-08 19:22:33 +05:30
prafull-opensignlabs dedff36b35 fix: save subscriptions in analytics 2024-04-08 19:21:50 +05:30
RaktimaNXG 4d561ca19e fix: opacity of costomize button 2024-04-08 19:11:10 +05:30
RaktimaNXG f70e9e2a3f fix: design of customize in email 2024-04-08 19:01:13 +05:30
AmolandGitHub e4a3ce405e Merge pull request #570 from OpenSignLabs/patch-1 2024-04-08 18:54:58 +05:30
prafull-opensignlabs c3be2a1fef fix: update query in fetch analytics 2024-04-08 18:54:13 +05:30
AmolandGitHub 8b2f7f1e95 Merge pull request #569 from OpenSignLabs/patch-1 2024-04-08 18:42:02 +05:30
RaktimaNXG 55465a035f fix: add function to hcnage variable string name to variable value for email body and subject 2024-04-08 18:02:03 +05:30
RaktimaNXG d30105299c fix: remove loading=true from login and signup page 2024-04-08 17:24:14 +05:30
prafull-opensignlabs 78b038da13 feat: implement of functionality to save and fetch analytics 2024-04-08 17:18:37 +05:30
RaktimaNXG d230ad2655 feat: add migration script of update contracts_document class to add field RequestBody and RequestSubject 2024-04-08 17:17:29 +05:30
AmolandGitHub 367c824918 Merge pull request #567 from OpenSignLabs/patch-1 2024-04-08 17:15:01 +05:30
RaktimaNXG 5f8affb1bb feat: implement custom email template of request email and completion email 2024-04-08 17:13:04 +05:30
prafull-opensignlabs 3bd23f83cf fix: completion certificate not generating for old completed documents 2024-04-08 17:05:46 +05:30
AmolandGitHub ab1dbecab2 Merge pull request #564 from OpenSignLabs/patch-1 2024-04-05 22:39:43 +05:30
prafull-opensignlabs 44c6aa9b0b fix: import issue and logo 2024-04-05 22:38:21 +05:30
AmolandGitHub ad922c16c7 Merge pull request #562 from OpenSignLabs/patch-1
fix: remove public access from do space and generate signed url wherever PDF files are accessed
2024-04-05 21:30:14 +05:30
prafull-opensignlabsandGitHub 87ee2e881e Merge branch 'staging' into patch-1 2024-04-05 21:23:28 +05:30
prafull-opensignlabs 3659a1de55 fix: remove public access from do space and generate signed url wherever PDF files are accessed 2024-04-05 21:21:19 +05:30
AmolandGitHub cc49a2fd4e Merge pull request #560 from OpenSignLabs/custom_logo 2024-04-04 20:29:46 +05:30
RaktimaNXG babd43e710 feat: display custom app logo saved by user from console app 2024-04-04 19:28:45 +05:30
prafull-opensignlabsandGitHub 8dde38edf1 Merge pull request #558 from OpenSignLabs/patch-1
feat: attach digital sign to completion certificate
2024-04-04 15:29:27 +05:30
prafull-opensignlabs 6c16d00031 feat: attach digital sign to completion certificate 2024-04-04 15:26:41 +05:30
prafull-opensignlabsandGitHub 99f444a8a1 Merge pull request #557 from OpenSignLabs/cookies_setup
fix: remove unnecessary session value to save in cookies session
2024-04-04 12:56:55 +05:30
RaktimaNXG f0149f6fd8 fix: redirect console user domain name 2024-04-04 12:06:21 +05:30
RaktimaNXG bdb7d9be8f fix: remove unnecessary session value to save in cookies session 2024-04-04 12:02:08 +05:30
prafull-opensignlabsandGitHub 27dcd7fefd Merge pull request #556 from OpenSignLabs/cookies_setup
feat: save user's details in session
2024-04-03 20:33:47 +05:30
RaktimaNXG a2467ef1a6 refactor code 2024-04-03 20:30:46 +05:30
RaktimaNXG 824b0a5407 fix: remove unnecessary cookies key value 2024-04-03 20:15:17 +05:30
RaktimaNXG ac8195a52d Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into cookies_setup 2024-04-03 15:29:41 +05:30
RaktimaNXG e0920bf5d8 feat: save user's details in cookies session 2024-04-03 15:28:48 +05:30
prafull-opensignlabsandGitHub 4a5f030d87 Merge pull request #555 from OpenSignLabs/patch-1
fix: Expired document message appears partially before appearing fully
2024-04-02 14:54:37 +05:30
prafull-opensignlabs ba839b0fc8 fix: Expired document message appears partially before appearing fully 2024-04-02 14:53:16 +05:30
prafull-opensignlabsandGitHub 27130fd223 Merge pull request #554 from OpenSignLabs/patch-1
fix: Expired report query
2024-04-02 14:17:08 +05:30
prafull-opensignlabs 19ba9e3091 fix: expired report query 2024-04-02 14:15:03 +05:30
prafull-opensignlabs 8c64a89f08 feat: add pro logo in header for paid user 2024-04-02 13:09:55 +05:30
prafull-opensignlabsandGitHub 1c3d2d5c9a Merge pull request #553 from OpenSignLabs/sorting_in_drive
fix: sorting by name issue in open-sign drive
2024-04-02 12:34:00 +05:30
prafull-opensignlabs b36df32b19 fix: design of subscription plans 2024-04-02 12:29:14 +05:30
RaktimaNXG 155a3eee14 fix: remove unnecessary code 2024-04-02 12:25:02 +05:30
prafull-opensignlabsandGitHub 7bb46782a3 Merge pull request #549 from OpenSignLabs/Input_text_widget
feat: make all type text input widgets consistent with the text widget
2024-04-02 12:18:44 +05:30
RaktimaNXG 40961936d0 fix: sorting issue by name in open-sign drive 2024-04-02 11:54:03 +05:30
RaktimaNXG db310b0d73 fix: remove text validation from name,company,job title widgets 2024-04-02 11:01:46 +05:30
prafull-opensignlabsandGitHub f0e9c6b3e4 Merge pull request #552 from OpenSignLabs/staging
sync branch
2024-04-02 09:46:08 +05:30
AmolandGitHub 28fd98c0db Merge pull request #551 from OpenSignLabs/staging
v1.4.2
2024-04-01 20:37:24 +05:30
AmolandGitHub 97bd375de3 Merge pull request #550 from OpenSignLabs/patch-1
Patch 1
2024-04-01 20:15:21 +05:30
prafull-opensignlabs e2d450f9aa fix: correct grammatical mistake in report help button 2024-04-01 20:13:56 +05:30
prafull-opensignlabs add01cb02a feat: auto assign free plan in google signup flow 2024-04-01 20:08:02 +05:30
prafull-opensignlabs cea1653805 feat: auto assign free plan to new signup 2024-04-01 19:51:11 +05:30
RaktimaNXG 9d81fce956 fix: input text width 2024-04-01 19:26:04 +05:30
prafull-opensignlabs 79e3762b36 feat: change subscription plans 2024-04-01 18:47:03 +05:30
RaktimaNXG aa04a957ed feat: change webhook & API token UI 2024-04-01 17:48:12 +05:30
prafull-opensignlabs d31910aefa fix: visibility of upgrade now button in header 2024-04-01 17:25:23 +05:30
prafull-opensignlabs 96a4a2e7fa feat: add help button for Role in template 2024-04-01 17:17:58 +05:30
prafull-opensignlabs e8aaab99f9 fix: change message of already subscribed 2024-04-01 16:54:26 +05:30
RaktimaNXG c61be7c35b fix: all text type fields similar to text widget 2024-04-01 15:38:06 +05:30
prafull-opensignlabs 0ee85f53da refactor: handle updgrade now visibililty in header 2024-04-01 14:31:14 +05:30
AmolandGitHub 748e2196da fix: typo in help text 2024-04-01 14:02:46 +05:30
RaktimaNXG 58199a934a fix: change upgrade button redirect ul, disable documentId issue 2024-04-01 13:27:43 +05:30
AmolandGitHub b04d2abbab Merge pull request #548 from OpenSignLabs/staging
v1.4.1
2024-04-01 11:48:17 +05:30
prafull-opensignlabs 7eec822a65 refactor: correct grammatical mistake in report help message 2024-03-31 14:16:23 +05:30
prafull-opensignlabs 6c21028523 feat: add upgrade now button in header 2024-03-31 14:11:26 +05:30
prafull-opensignlabs 099a3cc46d refactor: update Emailcount in OTP mail 2024-03-31 13:21:04 +05:30
prafull-opensignlabs 5adaffc684 feat: add emailcount of user 2024-03-31 12:42:08 +05:30
prafull-opensignlabs d588caf6d4 fix: not able to subscribed free plan & stop sending individual mail 2024-03-31 09:28:35 +05:30
prafull-opensignlabs 5930fd940d refactor: add loader in for free subscription 2024-03-31 09:08:27 +05:30
prafull-opensignlabs 741f66b019 feat: add documentCount and templateCount for user 2024-03-31 08:28:17 +05:30
AndrewandGitHub 0bf07c2703 Update README.md 2024-03-30 13:26:13 +05:30
RaktimaNXG de26c2d719 fix: handle condition of free validation warning message 2024-03-29 17:51:59 +05:30
prafull-opensignlabs 881ac12b03 feat: savefileusage in server side 2024-03-29 17:26:33 +05:30
RaktimaNXG 1932dda6d6 feat: disbale validation feature, documentId,webhook,API token for free users and add upgrade button to subscribe 2024-03-29 17:06:09 +05:30
prafull-opensignlabs 46159b7214 fix: freeplan navigation 2024-03-29 13:11:13 +05:30
prafull-opensignlabs c57c15a4cb feat: save tenant usage 2024-03-28 18:57:43 +05:30
prafull-opensignlabs 724fa7c27e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into patch-1 2024-03-28 16:51:42 +05:30
prafull-opensignlabs 2762c3e170 feat: add validation of free plan 2024-03-28 13:57:37 +05:30
prafull-opensignlabsandGitHub 10a0c60056 Merge pull request #540 from OpenSignLabs/opensign_drive_move
fix: document list sorting issue on scroll down in opensign-drive issue, disable recipient's list and field after send request
2024-03-28 13:55:06 +05:30
RaktimaNXG 036c9b8d4a fix: open-sign drive sorting issue by name 2024-03-28 12:46:15 +05:30
RaktimaNXG 1f50368259 fix: open-sign drive sorting issue by date and name 2024-03-28 12:30:28 +05:30
RaktimaNXG 6d5e07dac7 fix: sorting issue in open-sign drive 2024-03-28 11:03:54 +05:30
RaktimaNXG 3656175f11 fix: drive details sorting, signature and intial widgets signature url isuue 2024-03-27 20:31:30 +05:30
RaktimaNXG febb50c3cd fix: expired document message 2024-03-27 18:09:06 +05:30
RaktimaNXG f8ba0d357b fix: mismatch between signature URL in the signature widget and the initial widget on the signature pad. 2024-03-27 17:31:11 +05:30
prafull-opensignlabs 74bfd39abc feat: restrict guest route 2024-03-27 16:07:26 +05:30
RaktimaNXG 5a25b2d874 fix: disable tour message for completed document 2024-03-27 13:54:12 +05:30
prafull-opensignlabs 3115ff3e2a fix: restrict user from logging in when next renewal date is expired even when route is entered directly 2024-03-27 13:11:20 +05:30
RaktimaNXG d1e7b9a2ab fix: document list sorting issue on scroll down in opensign-drive issue, disable recipient's list and field after send request 2024-03-27 12:19:17 +05:30
prafull-opensignlabsandGitHub adbf1fe1e0 Merge pull request #539 from OpenSignLabs/staging
sync branch
2024-03-27 11:35:13 +05:30
AmolandGitHub 60140885e0 Merge pull request #537 from OpenSignLabs/tooltip_pos 2024-03-27 11:12:18 +05:30
prafull-opensignlabsandGitHub fe2f3c5f7c Merge pull request #538 from OpenSignLabs/staging
sync branch
2024-03-27 11:09:58 +05:30
prafull-opensignlabs a06723ff3f refactor: reduce size of tooltip and adjust it's position 2024-03-27 11:05:24 +05:30
AmolandGitHub 387154c07b Merge pull request #536 from OpenSignLabs/staging
v1.4.0-beta
2024-03-26 21:49:47 +05:30
AmolandGitHub 12a6a84450 Merge pull request #535 from OpenSignLabs/premium_alert 2024-03-26 20:49:51 +05:30
prafull-opensignlabs 1f350fc459 fix: move document to folder in opensigndrive not working 2024-03-26 20:48:41 +05:30
AmolandGitHub d9fd2a5531 Merge pull request #533 from OpenSignLabs/premium_alert 2024-03-26 20:14:41 +05:30
prafull-opensignlabs cd4fd6e3de refactor: change help message of contact report 2024-03-26 19:12:38 +05:30
prafull-opensignlabs 49cd30de96 refactor: update help message 2024-03-26 19:00:51 +05:30
prafull-opensignlabsandGitHub a24ebe25a8 Merge pull request #534 from OpenSignLabs/widget_copy
feat: implement feature of copy widget to next of that widget for any widgets except `signature,text,stamp,initial` widgets.
2024-03-26 18:36:11 +05:30
RaktimaNXG 3e664bf677 fix: checkbox position issue 2024-03-26 17:29:26 +05:30
prafull-opensignlabs 6161e975fb refactor: add help buttons for all sections 2024-03-26 17:03:08 +05:30
RaktimaNXG c2a45c230b fix: widget copy issue in sign-yourself flow 2024-03-26 16:02:03 +05:30
RaktimaNXG a629aa0bff Merge branch 'widget_copy' of https://github.com/OpenSignLabs/OpenSign into widget_copy 2024-03-26 14:42:54 +05:30
RaktimaNXG 899678d98a feat: implement feature of copy widget to next of that widget for any widgets except signature,text,stamp,initial widgets 2024-03-26 14:31:48 +05:30
prafull-opensignlabsandGitHub 076a0f54c5 Merge pull request #522 from OpenSignLabs/signature_pad
fix: change dashboard icon of signyour-self & request sign, add pen color for text signature
2024-03-26 10:48:22 +05:30
RaktimaNXG a94e67cd7c fix:signature pad pen color issue 2024-03-26 10:12:07 +05:30
RaktimaNXG e78a3991f0 fix: remoove unnecessary code 2024-03-26 09:50:46 +05:30
prafull-opensignlabs 593a8499f4 refactor: add premium features warning 2024-03-25 22:12:58 +05:30
AndrewandGitHub 70d643a5e2 Merge pull request #524 from OpenSignLabs/prafull-opensignlabs-patch-5 2024-03-23 19:35:07 +05:30
prafull-opensignlabsandGitHub f2b1a54695 change mongdb url in .env.local_dev 2024-03-23 19:33:14 +05:30
AndrewandGitHub 4353a4f732 Merge pull request #523 from OpenSignLabs/fix_localdocker 2024-03-23 17:38:39 +05:30
RaktimaNXG 34c99edf1a fix: increase text area height on press enter and fix text widget embeding issue on finished button 2024-03-23 17:08:09 +05:30
prafull-opensignlabs 91f3bdb299 fix: issue of cannot post /login in local docker setup 2024-03-23 17:06:40 +05:30
RaktimaNXG 276fc974fa fix: change dashboard icon of signyour-self & request sign,other minor issue fixed 2024-03-22 21:20:49 +05:30
prafull-opensignlabsandGitHub 081e11f9af Merge pull request #518 from OpenSignLabs/send_In_Order
feat: implement of draggable recipient's list to modify recipient's sequence when enable send-in-order during edit document
2024-03-22 18:08:34 +05:30
RaktimaandGitHub 4f056cc9d3 Update RecipientList.js 2024-03-22 18:06:18 +05:30
RaktimaNXG d9da25e908 fix: increase gap between up and down arrow icon 2024-03-22 16:42:51 +05:30
RaktimaNXG 99b3436c8c feat: add up and down key to change sequence of recipient's list 2024-03-22 16:29:08 +05:30
prafull-opensignlabsandGitHub 452d4d7a1d Merge pull request #519 from OpenSignLabs/signyourself-textinput
fix: change text-input widget to text in signyour-self flow
2024-03-22 12:46:52 +05:30
RaktimaNXG ed613c3424 fix: set default option field value oftextInput widget issue in signyourself 2024-03-22 12:30:53 +05:30
RaktimaNXG bf70fa0b11 fix: drag icon image select issue in ios 2024-03-22 12:01:27 +05:30
RaktimaNXG 8684197d49 fix: drag icon image select issue in ios 2024-03-22 11:55:55 +05:30
RaktimaNXG 648d1224b3 fix: disable default menu open on press on drag icon in mobile view 2024-03-21 18:30:46 +05:30
RaktimaNXG 126b6ad355 fix: change text-input widget to text in signyour-self flow 2024-03-21 18:01:02 +05:30
RaktimaNXG 0512f27e2e change drag icon 2024-03-21 17:41:48 +05:30
prafull-opensignlabsandGitHub 2c61630017 Update signature.css 2024-03-21 17:19:25 +05:30
prafull-opensignlabsandGitHub 72c2b2dde6 change size of custom cursor icon 2024-03-21 17:11:56 +05:30
prafull-opensignlabsandGitHub 29eb78c960 Update signature.css 2024-03-21 17:01:18 +05:30
prafull-opensignlabsandGitHub aa9ba05dde Update move cursor icon 2024-03-21 16:45:28 +05:30
RaktimaNXG 365b074f62 fix: change default value of send in order,dragging functionality to change sequence of signers list in mobile 2024-03-21 16:08:07 +05:30
RaktimaNXG af6cb4c17d feat: implement dragging functionality on recipient's list to modify sequence of send-in-order during edit document 2024-03-21 12:39:00 +05:30
prafull-opensignlabsandGitHub 6bcad91b0f Merge pull request #517 from OpenSignLabs/openSign_drive
feat: updated draft document query
2024-03-20 19:16:46 +05:30
RaktimaNXG 1aa2b003b2 feat: updated draft document query 2024-03-20 19:14:20 +05:30
prafull-opensignlabsandGitHub 1a27e9addf Merge pull request #515 from OpenSignLabs/openSign_drive
fix: data fetching issue in openSign_drive
2024-03-20 18:21:20 +05:30
RaktimaNXG c2891a0858 refactor code 2024-03-20 16:43:56 +05:30
RaktimaNXG c1b226db96 fix: handle condition of fetching opensign details 2024-03-20 15:59:06 +05:30
prafull-opensignlabsandGitHub 8a01efee4c Merge pull request #516 from OpenSignLabs/staging
sync branch
2024-03-20 15:27:47 +05:30
prafull-opensignlabs eafb26d462 feat: restrict route if user subscription is expired 2024-03-20 15:26:38 +05:30
RaktimaNXG 62e1a1bd9b fix: refactor code 2024-03-20 15:02:08 +05:30
RaktimaNXG 58eb71c52d fix: refactor code 2024-03-20 14:50:48 +05:30
RaktimaNXG c1e6f2b5ab add comment 2024-03-20 12:49:07 +05:30
RaktimaNXG ba0c18b591 fix: data fetching issue in openSign_drive 2024-03-20 12:38:34 +05:30
prafull-opensignlabsandGitHub c3a902dcc7 Merge pull request #514 from OpenSignLabs/unuse_package
Refactor: remove warning
2024-03-20 11:40:46 +05:30
prafull-opensignlabs 5ece3c0694 Refactor: remov warning 2024-03-20 11:38:15 +05:30
AmolandGitHub 1053540410 Merge pull request #513 from OpenSignLabs/unuse_package 2024-03-20 11:14:09 +05:30
prafull-opensignlabs 24773759c2 Refactor: Add LazyPage Component for Lazy Loading of Pages & update dependcies 2024-03-20 11:11:37 +05:30
AmolandGitHub 7a6de70169 Merge pull request #510 from OpenSignLabs/unuse_package 2024-03-19 19:52:34 +05:30
prafull-opensignlabs 04b2c7c4da build(deps): update dependencies 2024-03-19 19:51:20 +05:30
AmolandGitHub d11171e91c Merge pull request #508 from OpenSignLabs/migrate_redux 2024-03-19 18:52:44 +05:30
prafull-opensignlabs a4a78cb96c fix: stuck on loader in login screen after guest login 2024-03-19 18:51:04 +05:30
prafull-opensignlabs dfdcde8fcc Refactor: Implement Lazy Loading for OpenSign Drive 2024-03-19 18:03:13 +05:30
prafull-opensignlabs 5e1b84d2e6 refactor: migrate redux to redux-toolkit 2024-03-19 17:29:48 +05:30
AmolandGitHub f7606bce35 Merge pull request #507 from OpenSignLabs/staging
v1.3.2-beta
2024-03-19 16:03:24 +05:30
prafull-opensignlabsandGitHub e643d96797 Merge pull request #506 from OpenSignLabs/api_signembed
fix: handle uncaught error in createdocument and createmplate API
2024-03-19 13:24:05 +05:30
prafull-opensignlabs 1ff4160ca4 fix: handle uncaught error in createdocument and createmplate API 2024-03-19 13:23:10 +05:30
prafull-opensignlabsandGitHub 62f66bf524 Merge pull request #505 from OpenSignLabs/api_signembed
fix: widgets are not embedding in pdf using API
2024-03-19 13:12:46 +05:30
prafull-opensignlabs de3bc37fa5 fix: widgets are not embeding in pdf using API 2024-03-19 13:11:23 +05:30
AmolandGitHub ea660384c3 Merge pull request #504 from OpenSignLabs/prafull-opensignlabs-patch-4
chore: Update mongo url in local env
2024-03-19 12:01:08 +05:30
prafull-opensignlabsandGitHub 210cb5e754 chore: Update mongo url in local env 2024-03-19 12:00:28 +05:30
AmolandGitHub ed45bfef84 Merge pull request #503 from OpenSignLabs/prafull-opensignlabs-patch-1 2024-03-19 10:59:23 +05:30
prafull-opensignlabsandGitHub a8e77e93f7 chore: change serverURL 2024-03-19 10:58:32 +05:30
prafull-opensignlabsandGitHub 969071bb28 Merge pull request #501 from OpenSignLabs/placeholder_sign
fix: show warning on placeholder_sign page for document already send to signers for signature
2024-03-19 10:29:33 +05:30
RaktimaNXG 76491a77f1 fix: add comment 2024-03-19 10:27:41 +05:30
AmolandGitHub 9a51eed2bc Merge pull request #502 from OpenSignLabs/fix_maxupload 2024-03-19 10:09:48 +05:30
prafull-opensignlabs a675a3c8fd refactor: change console dir to console log 2024-03-18 19:49:13 +05:30
prafull-opensignlabs fea30930eb fix: handle uncaught err of max upload pdf in form 2024-03-18 19:47:56 +05:30
RaktimaNXG 7373b086e4 fix: change warning messages 2024-03-18 19:00:02 +05:30
RaktimaNXG 2fc1eda761 fix: remove console 2024-03-18 18:22:14 +05:30
RaktimaNXG 2c100ad399 fix: show warning on placeholder_sign page for document already send to signers for signature 2024-03-18 17:40:27 +05:30
prafull-opensignlabsandGitHub 1cefe01aee Merge pull request #499 from OpenSignLabs/fix_mobile_issue
fix:  width issue of text widget & default data for widgets information pop-ups in mobile display.
2024-03-18 17:09:17 +05:30
RaktimaNXG f1cf395761 fix: textInput widget popup select arrow key design 2024-03-18 17:05:15 +05:30
RaktimaNXG 4af3d8f78b fix: dragging issue of text widget in mobile view 2024-03-18 16:21:14 +05:30
AmolandGitHub cf21ac7405 Merge pull request #500 from OpenSignLabs/fix_Smtp 2024-03-18 15:16:27 +05:30
prafull-opensignlabs 5f11e92c0c fix: handle uncaught error of SMTP 2024-03-18 15:14:23 +05:30
RaktimaNXG c3ffda4dd2 fix: resolving width inconsistencies with of text widgets & default data for widgets information pop-ups in mobile display. 2024-03-18 12:45:08 +05:30
AmolandGitHub c929b6b7a9 Update Dockerhubfile 2024-03-18 00:19:06 +05:30
AmolandGitHub eb2abd3e6d Merge pull request #493 from OpenSignLabs/widget_modal 2024-03-15 19:33:53 +05:30
prafull-opensignlabs 1c4c2e87b4 refactor: change design of widgets in mobile 2024-03-15 19:30:33 +05:30
prafull-opensignlabs 4fda5b4531 refactor: stop showing initial popup when wdigets drag 2024-03-15 19:04:02 +05:30
AmolandGitHub df61e143af Merge pull request #492 from OpenSignLabs/update_menu 2024-03-15 14:27:06 +05:30
prafull-opensignlabs 3a55c35445 feat: add create document btn in dashboard, update menu & change Root to Opensign™ drive in folder input 2024-03-15 14:25:50 +05:30
AmolandGitHub f08758fd71 Merge pull request #491 from OpenSignLabs/staging 2024-03-14 21:45:14 +05:30
AmolandGitHub d3b1a37a7c Merge pull request #473 from OpenSignLabs/staging
v1.3.0-beta
2024-03-08 20:34:25 +05:30
AmolandGitHub 4718c653f7 Merge pull request #454 from OpenSignLabs/staging
minor package updates
2024-03-02 13:26:17 +05:30
AndrewandGitHub f9b8437981 Merge pull request #421 from OpenSignLabs/prafull-opensignlabs-patch-4 2024-02-22 18:28:02 +05:30
prafull-opensignlabsandGitHub b0c73bc63e Update Docker.yml 2024-02-22 18:27:35 +05:30
AmolandGitHub 269246659b Merge pull request #411 from OpenSignLabs/staging
v1.2.1-beta
2024-02-22 12:28:38 +05:30
AmolandGitHub 51fe5d133e Merge pull request #404 from OpenSignLabs/staging
refactor analytics code
2024-02-16 20:15:05 +05:30
AmolandGitHub 36a1123535 Merge pull request #402 from OpenSignLabs/staging
v1.2.0-beta
2024-02-16 19:57:43 +05:30
AmolandGitHub 3c1ceb4995 Merge pull request #331 from OpenSignLabs/staging
v1.1.1-beta
2024-01-15 19:21:24 +05:30
AmolandGitHub 53d154fa59 Merge pull request #315 from OpenSignLabs/staging
v1.1.0-beta
2024-01-10 21:07:41 +05:30
224 changed files with 26882 additions and 14338 deletions
+27
View File
@@ -205,6 +205,33 @@
"contributions": [
"code"
]
},
{
"login": "ugoconsonni",
"name": "ugoconsonni",
"avatar_url": "https://avatars.githubusercontent.com/u/13661702?v=4",
"profile": "https://github.com/ugoconsonni",
"contributions": [
"code"
]
},
{
"login": "daniel-mutwiri",
"name": "Daniel Mutwiri",
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
"profile": "https://github.com/daniel-mutwiri",
"contributions": [
"code"
]
},
{
"login": "Zathiel",
"name": "Zathiel",
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
"profile": "https://github.com/Zathiel",
"contributions": [
"code"
]
}
]
}
+2 -2
View File
@@ -20,7 +20,7 @@ appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
MASTER_KEY=XnAadwKxxByMr
# Mongodb URI to connect to
MONGODB_URI=mongodb://host.docker.internal:27017/OpenSignDB
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
@@ -40,7 +40,7 @@ DO_SECRET_ACCESS_KEY=
# Digital ocean spaces region or AWS s3 region
DO_REGION=us-west
# local storage
USE_LOCAL=TRUE
USE_LOCAL=true
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
MAILGUN_API_KEY=
+1 -1
View File
@@ -2,7 +2,7 @@ name: ci
on:
push:
branches:
- 'staging'
- 'main'
jobs:
docker:
+1 -1
View File
@@ -1,6 +1,6 @@
build:
cp .env.local_dev .env
cd apps/OpenSign && npm install && npm run build
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
docker compose up --build --force-recreate
run:
+14 -11
View File
@@ -42,24 +42,24 @@ Please star ⭐ the repo to support us! 😀
### Introduction
Welcome to OpenSign, the premier open-source docusign alternative - document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
Welcome to OpenSign, the premier open source docusign alternative - document e-signing solution designed to provide a secure, reliable and free alternative to commercial esign platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
---
### Features
- **Secure PDF E-Signing:** With the help of Robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that comes with hand drawn signatures support as well as uploaded images & saved signatures for the simplest signing experience.
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "One click signatures" and "OpenSign Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players.
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that allows hand drawn signatures, uploaded images, typed signatures & saved signatures for the simplest signing experience ever.
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "Templates", "One click signatures" and "OpenSign Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players in e-signature space.
- **Email Unique Code(OTP) verification support for guest signers:** With OpenSign™, your documents are fully secure even when being signed by guest users. Guest signers can only sign the document after entering a unique code sent to their email address. 
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign it. Not just this, OpenSign also allows signers to reject signing a document.
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign. Not just this, OpenSign also allows signers to reject signing a document.
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
- **PDF Template Creation(coming soon):** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time.
- **OpenSign™ Drive:** It is a centralised secure vault for your signed documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
- **PDF Template Creation:** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time & collect e-signatures seamlessly.
- **OpenSign™ Drive:** It is a centralised secure vault for your digital documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
- **Audit Trails & completion certificate:** Being a security focused solution, OpenSign™ makes it a top priority to save detailed logs for tracking document activities along with time-stamps, IP addresses, email IDs & phone numbers. A completion certificate is generated as soon as document is completed which contains all the document related logs for added safety.
- **API Support(coming soon):** OpenSign™ API allows seamless integration into existing systems and software. APIs will soon be available as a cloud hosted solution.
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is coming soon.
- **API Support:** OpenSign™ API allows seamless integration into existing systems and software. You can generate an API key from the app and refer the [docs](https://docs.opensignlabs.com) to start integrating it in your existing applications.
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is available. We also have a Zapier integration that allows you to integrate it with virtually any application.
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'>
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'>
@@ -76,7 +76,7 @@ Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructi
### Usage
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
---
@@ -134,6 +134,9 @@ We would like to thank all our contributors and users for their support and feed
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a><a href="#security-Zathiel" title="Security">🛡️</a></td>
</tr>
</tbody>
</table>
+1 -1
View File
@@ -21,5 +21,5 @@ EXPOSE 3000
# ENV NODE_ENV production
# Run the application
ENTRYPOINT npm run start-dev
ENTRYPOINT npm run start
+3
View File
@@ -14,6 +14,9 @@ RUN npm install
COPY apps/OpenSign/ .
COPY apps/OpenSign/.husky .
# build
RUN npm run build
# Make port 3000 available to the world outside this container
EXPOSE 3000
+4772 -4170
View File
File diff suppressed because it is too large Load Diff
+36 -31
View File
@@ -3,49 +3,50 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@radix-ui/themes": "^2.0.3",
"@react-pdf/renderer": "^3.3.8",
"@reduxjs/toolkit": "^2.2.1",
"axios": "^1.6.0",
"daisyui": "^3.9.2",
"@formkit/auto-animate": "^0.8.2",
"@radix-ui/themes": "^3.0.5",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.5",
"axios": "^1.7.2",
"file-saver": "^2.0.5",
"html-react-parser": "^4.2.2",
"jwt-decode": "^4.0.0",
"moment": "^2.29.4",
"parse": "^4.3.1",
"moment": "^2.30.1",
"nth-check": "^2.1.1",
"parse": "^5.1.0",
"pdf-lib": "^1.17.1",
"print-js": "^1.6.0",
"radix-ui": "^1.0.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.1",
"react-datepicker": "^6.2.0",
"react-bootstrap": "^2.10.2",
"react-confetti": "^6.1.0",
"react-cookie": "^7.1.4",
"react-datepicker": "^6.9.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
"react-dnd-touch-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-drag-listview": "^2.0.0",
"react-draggable": "^4.4.6",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-konva": "^18.2.10",
"react-modal": "^3.16.1",
"react-pdf": "^7.7.1",
"react-redux": "^8.1.3",
"react-rnd": "^10.4.1",
"react-pdf": "^9.0.0",
"react-quill": "^2.0.0",
"react-redux": "^9.1.2",
"react-rnd": "^10.4.11",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
"react-signature-canvas": "^1.0.6",
"react-tooltip": "^5.26.3",
"reactour": "^1.19.2",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"react-tooltip": "^5.26.4",
"react-web-share": "^2.0.2",
"reactour": "^1.19.4",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"serve": "^14.2.1",
"serve": "^14.2.3",
"styled-components": "^4.4.1",
"web-vitals": "^2.1.4"
"web-vitals": "^4.0.1"
},
"scripts": {
"start": "serve -s build",
@@ -83,16 +84,20 @@
}
},
"devDependencies": {
"@babel/runtime-corejs2": "^7.23.9",
"autoprefixer": "^10.4.18",
"@babel/runtime-corejs2": "^7.24.7",
"autoprefixer": "^10.4.19",
"commitizen": "^4.3.0",
"eslint": "^8.51.0",
"daisyui": "^4.12.2",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.33.2",
"lint-staged": "^15.2.2",
"postcss": "^8.4.35",
"prettier": "^2.8.0",
"pretty-quick": "^3.1.3",
"tailwindcss": "^3.3.3"
"eslint-plugin-react": "^7.34.1",
"lint-staged": "^15.2.5",
"postcss": "^8.4.38",
"prettier": "^2.8.8",
"pretty-quick": "^3.3.1",
"tailwindcss": "^3.4.4"
},
"overrides": {
"nth-check": "$nth-check"
}
}
+4 -4
View File
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-theme="opensigncss">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="/favicon.ico" />
<!-- <link rel="icon" type="image/png"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
sizes="40x40" /> -->
@@ -15,7 +15,7 @@
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="manifest" href="/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
@@ -50,4 +50,4 @@
-->
</body>
</html>
</html>
Binary file not shown.
+110 -91
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, Suspense, lazy } from "react";
import React, { useState, useEffect, lazy } from "react";
import { Routes, Route, BrowserRouter } from "react-router-dom";
import { pdfjs } from "react-pdf";
import Login from "./pages/Login";
@@ -9,12 +9,15 @@ import HomeLayout from "./layout/HomeLayout";
import PageNotFound from "./pages/PageNotFound";
import ValidateRoute from "./primitives/ValidateRoute";
import Validate from "./primitives/Validate";
import ManageSign from "./pages/Managesign";
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
import SignYourSelf from "./pages/SignyourselfPdf";
import DraftDocument from "./components/pdf/DraftDocument";
import PlaceHolderSign from "./pages/PlaceHolderSign";
import PdfRequestFiles from "./pages/PdfRequestFiles";
import LazyPage from "./primitives/LazyPage";
import { isEnableSubscription } from "./constant/const";
import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader";
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
@@ -23,28 +26,17 @@ const Subscriptions = lazy(() => import("./pages/PlanSubscriptions"));
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
const UserProfile = lazy(() => import("./pages/UserProfile"));
const Signup = lazy(() => import("./pages/Signup"));
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
const ManageSign = lazy(() => import("./pages/Managesign"));
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook"));
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
const Loader = () => {
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => {
return (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="flex justify-center items-center h-[100vh]">
<Loader />
</div>
);
};
@@ -73,88 +65,115 @@ function App() {
};
return (
<div className="bg-[#eef1f5]">
<div className="bg-base-200">
{isloading ? (
<Loader />
<AppLoader />
) : (
<BrowserRouter>
<Suspense fallback={<Loader />}>
<Routes>
<Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} />
</Route>
<Route element={<Validate />}>
<Route
path="/load/template/:templateId"
element={<TemplatePlaceholder />}
/>
<Route
exact
path="/load/placeholdersign/:docId"
element={<PlaceHolderSign />}
/>
<Route
exact
path="/load/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Routes>
<Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} />
<Route path="/signup" element={<LazyPage Page={Signup} />} />
</Route>
<Route element={<Validate />}>
<Route
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
element={<GuestLogin />}
path="/load/template/:templateId"
element={<TemplatePlaceholder />}
/>
<Route
path="/login/:id/:userMail/:contactBookId/:serverUrl"
element={<GuestLogin />}
exact
path="/load/placeholdersign/:docId"
element={<PlaceHolderSign />}
/>
<Route path="/debugpdf" element={<DebugPdf />} />
<Route path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
<>
<Route exact path="/pgsignup" element={<Pgsignup />} />
<Route path="/subscription" element={<Subscriptions />} />
</>
)}
<Route element={<HomeLayout />}>
<Route path="/changepassword" element={<ChangePassword />} />
<Route path="/form/:id" element={<Form />} />
<Route path="/report/:id" element={<Report />} />
<Route path="/dashboard/:id" element={<Dashboard />} />
<Route path="/profile" element={<UserProfile />} />
<Route path="/generatetoken" element={<GenerateToken />} />
<Route path="/webhook" element={<Webhook />} />
<Route path="/managesign" element={<ManageSign />} />
<Route path="/opensigndrive" element={<Opensigndrive />} />
<Route
exact
path="/load/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Route
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
element={<LazyPage Page={GuestLogin} />}
/>
<Route
path="/login/:id/:userMail/:contactBookId/:serverUrl"
element={<LazyPage Page={GuestLogin} />}
/>
<Route
path="/login/:base64url"
element={<LazyPage Page={GuestLogin} />}
/>
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
<Route
path="/forgetpassword"
element={<LazyPage Page={ForgetPassword} />}
/>
{isEnableSubscription && (
<>
<Route
path="/template/:templateId"
element={<TemplatePlaceholder />}
path="/pgsignup"
element={<LazyPage Page={Pgsignup} />}
/>
{/* signyouself route with no rowlevel data using docId from url */}
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
{/* draft document route to handle and navigate route page accordiing to document status */}
<Route path="/draftDocument" element={<DraftDocument />} />
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
<Route
path="/placeHolderSign/:docId"
element={<PlaceHolderSign />}
path="/subscription"
element={<LazyPage Page={Subscriptions} />}
/>
{/* for user signature (need your sign route) with row level data */}
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
{/* for user signature (need your sign route) with no row level data */}
<Route
path="/pdfRequestFiles/:docId"
element={<PdfRequestFiles />}
/>
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Route path="*" element={<PageNotFound />} />
</Routes>
</Suspense>
</>
)}
<Route element={<HomeLayout />}>
<Route
path="/changepassword"
element={<LazyPage Page={ChangePassword} />}
/>
<Route path="/form/:id" element={<Form />} />
<Route path="/report/:id" element={<Report />} />
<Route path="/dashboard/:id" element={<Dashboard />} />
<Route
path="/profile"
element={<LazyPage Page={UserProfile} />}
/>
<Route
path="/opensigndrive"
element={<LazyPage Page={Opensigndrive} />}
/>
<Route
path="/managesign"
element={<LazyPage Page={ManageSign} />}
/>
<Route
path="/generatetoken"
element={<LazyPage Page={GenerateToken} />}
/>
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
<Route
path="/template/:templateId"
element={<TemplatePlaceholder />}
/>
{/* signyouself route with no rowlevel data using docId from url */}
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
{/* draft document route to handle and navigate route page accordiing to document status */}
<Route path="/draftDocument" element={<DraftDocument />} />
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
<Route
path="/placeHolderSign/:docId"
element={<PlaceHolderSign />}
/>
{/* for user signature (need your sign route) with row level data */}
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
{/* for user signature (need your sign route) with no row level data */}
<Route
path="/pdfRequestFiles/:docId"
element={<PdfRequestFiles />}
/>
{/* recipient signature route with no rowlevel data using docId from url */}
<Route
path="/recipientSignPdf/:docId/:contactBookId"
element={<PdfRequestFiles />}
/>
</Route>
<Route path="/sso" element={<SSOVerify />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</BrowserRouter>
)}
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

+167 -172
View File
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
import Loader from "../primitives/Loader";
const AddSigner = (props) => {
const [name, setName] = useState("");
@@ -23,7 +23,7 @@ const AddSigner = (props) => {
);
if (savedUserDetails && addYourself) {
setName(savedUserDetails.name);
setPhone(savedUserDetails.phone);
setPhone(savedUserDetails?.phone || "");
setEmail(savedUserDetails.email);
}
}, [addYourself]);
@@ -49,144 +49,162 @@ const AddSigner = (props) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenetId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenetId")
});
}
if (localStorage.getItem("TenantId")) {
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
const user = Parse.User.current();
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", email);
const res = await query.first();
// console.log(res);
if (!res) {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
}
} else {
alert("Contact already exist!");
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_Guest",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
console.log("err in fetch contact", err);
setIsLoader(false);
alert("something went wrong, please try again later");
}
} catch (err) {
// console.log("err", err);
} else {
setIsLoader(false);
alert("something went wrong!");
alert("something went wrong, please try again later");
}
};
@@ -209,38 +227,29 @@ const AddSigner = (props) => {
<div className="h-full p-[20px]">
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<Loader />
</div>
)}
<div className="w-full mx-auto p-2">
<div className="w-full mx-auto p-2 text-base-content">
{isUserExist && (
<div className="mb-3">
<div className="mb-3 flex items-center">
<input
type="checkbox"
id="addYourself"
checked={addYourself}
onChange={handleAddYourselfChange}
className="form-checkbox"
className="op-checkbox op-checkbox-sm"
/>
<label htmlFor="addYourself" className="ml-2 text-gray-700">
<label htmlFor="addYourself" className="ml-2 mb-0">
Add Yourself
</label>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
<label htmlFor="name" className="block text-xs font-semibold">
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
@@ -249,59 +258,45 @@ const AddSigner = (props) => {
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
<label htmlFor="email" className="block text-xs font-semibold">
Email
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
required
disabled={addYourself}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
<label htmlFor="phone" className="block text-xs font-semibold">
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
disabled={addYourself}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mt-4 flex justify-start">
<button
type="submit"
style={{ backgroundColor: modalSubmitBtnColor }}
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
>
<div className="mt-4 flex gap-x-2 justify-start">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
style={{ backgroundColor: modalCancelBtnColor }}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
className="op-btn op-btn-secondary"
>
Reset
</button>
+14 -17
View File
@@ -3,6 +3,7 @@ import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
const AddUser = (props) => {
const [name, setName] = useState("");
@@ -48,11 +49,11 @@ const AddUser = (props) => {
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_User");
if (localStorage.getItem("TenetId")) {
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenetId")
objectId: localStorage.getItem("TenantId")
});
}
@@ -62,8 +63,10 @@ const AddUser = (props) => {
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = await _user.save();
if (user) {
@@ -193,13 +196,7 @@ const AddUser = (props) => {
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<Loader />
</div>
)}
<div className="w-full mx-auto">
@@ -211,7 +208,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
@@ -219,7 +216,7 @@ const AddUser = (props) => {
value={name}
onChange={(e) => setName(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
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">
@@ -228,7 +225,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Email
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
@@ -236,7 +233,7 @@ const AddUser = (props) => {
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
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">
@@ -245,7 +242,7 @@ const AddUser = (props) => {
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
@@ -253,7 +250,7 @@ const AddUser = (props) => {
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
+256
View File
@@ -0,0 +1,256 @@
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput";
import Loader from "../primitives/Loader";
const BulkSendUi = (props) => {
const [forms, setForms] = useState([]);
const [formId, setFormId] = useState(2);
const formRef = useRef(null);
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [allowedForm, setAllowedForm] = useState(0);
const [isSignatureExist, setIsSignatureExist] = useState();
const allowedSigners = 50;
useEffect(() => {
signatureExist();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function to check atleast one signature field exist
const signatureExist = () => {
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
};
useEffect(() => {
if (scrollOnNextUpdate && formRef.current) {
formRef.current.scrollIntoView({
behavior: "smooth",
block: "end",
inline: "nearest"
});
setScrollOnNextUpdate(false);
}
}, [forms, scrollOnNextUpdate]);
useEffect(() => {
(() => {
if (props?.Placeholders?.length > 0) {
let users = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
users = [
...users,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
const totalForms = Math.floor(allowedSigners / users?.length);
setAllowedForm(totalForms);
}
})();
// eslint-disable-next-line
}, []);
const handleInputChange = (index, signer, fieldIndex) => {
const newForms = [...forms];
newForms[index].fields[fieldIndex].email = signer?.Email
? signer?.Email
: signer || "";
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
setForms(newForms);
};
const handleAddForm = (e) => {
e.preventDefault();
// Check if the quick send limit has been reached
if (forms?.length < allowedForm) {
if (props?.Placeholders.length > 0) {
let newForm = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
newForm = [
...newForm,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms([...forms, { Id: formId, fields: newForm }]);
}
setFormId(formId + 1);
setScrollOnNextUpdate(true);
} else {
// If the limit has been reached, throw an error with the appropriate message
alert("Quick send reached limit.");
}
};
const handleRemoveForm = (index) => {
const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms);
};
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmit(true);
// Create a copy of Placeholders array from props.item
let Placeholders = [...props.item.Placeholders];
// Initialize an empty array to store updated documents
let Documents = [];
// Loop through each form
forms.forEach((form) => {
// Map through the copied Placeholders array to update email values
const updatedPlaceholders = Placeholders.map((placeholder) => {
// Find the field in the current form that matches the placeholder Id
const field = form.fields.find(
(element) => parseInt(element.fieldId) === placeholder.Id
);
// If a matching field is found, update the email value in the placeholder
const signer = field?.signer?.objectId ? field.signer : {};
if (field) {
return {
...placeholder,
email: field.email,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
}
// If no matching field is found, keep the placeholder as is
return placeholder;
});
// Push a new document object with updated Placeholders into the Documents array
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
});
// console.log("Documents ", Documents);
await batchQuery(Documents);
};
const batchQuery = async (Documents) => {
const serverUrl = localStorage.getItem("baseUrl");
const functionsUrl = `${serverUrl}functions/batchdocuments`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = {
Documents: JSON.stringify(Documents)
};
try {
const res = await axios.post(functionsUrl, params, { headers: headers });
// console.log("res ", res);
if (res.data && res.data.result) {
props.handleClose("success", Documents?.length);
}
} catch (err) {
console.log("Err ", err);
props.handleClose("error", 0);
} finally {
setIsSubmit(false);
}
};
return (
<>
{isSubmit && (
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
{props.Placeholders?.length > 0 ? (
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className="min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col" key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
>
<i className="fa-solid fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-solid fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="op-btn op-btn-secondary focus:outline-none"
>
<i className="fa-solid fa-paper-plane"></i>{" "}
<span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to
any contact.
</div>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please ensure there&#39;s at least one signature widget added for
all recipients.
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please add at least one role to this template in order to &apos;quick
send&apos; copies of it to multiple signers.
</div>
)}
</>
);
};
export default BulkSendUi;
+10 -6
View File
@@ -45,12 +45,16 @@ const Footer = () => {
};
return (
<>
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</div>
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
<aside>
<p>
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</p>
</aside>
</footer>
<button
className={`${
showButton ? "block" : "hidden"
@@ -67,7 +67,7 @@ const FullScreenButton = () => {
<div className="flex items-center">
<button
onClick={toggleFullScreen}
className="text-black p-2 text-sm focus:outline-none"
className="text-base-content p-2 text-sm focus:outline-none"
>
{isFullScreen ? (
<i className="fa-solid fa-compress"></i>
+113 -55
View File
@@ -4,23 +4,53 @@ import FullScreenButton from "./FullScreenButton";
import { useNavigate } from "react-router-dom";
import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize";
import { openInNewTab } from "../constant/Utils";
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
const Header = ({ showSidebar }) => {
const navigation = useNavigate();
const navigate = useNavigate();
const { width } = useWindowSize();
let applogo = localStorage.getItem("appLogo") || "";
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const [isOpen, setIsOpen] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(true);
const [isPro, setIsPro] = useState(false);
const [applogo, setAppLogo] = useState(
localStorage.getItem("appLogo") || " "
);
const toggleDropdown = () => {
setIsOpen(!isOpen);
};
useEffect(() => {
checkSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function checkSubscription() {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
if (getIsSubscribe) {
const applogo = await getAppLogo();
if (applogo) {
setAppLogo(applogo);
} else {
setAppLogo(localStorage.getItem("appLogo") || "");
}
}
setIsPro(getIsSubscribe);
setIsSubscribe(getIsSubscribe);
}
}
const closeDropdown = () => {
setIsOpen(false);
Parse.User.logOut();
if (Parse?.User?.current()) {
try {
Parse.User.logOut();
} catch (err) {
console.log("Err", err);
}
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let appName = localStorage.getItem("appName");
@@ -43,7 +73,7 @@ const Header = ({ showSidebar }) => {
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
navigation("/");
navigate("/");
};
//handle to close profile drop down menu onclick screen
@@ -63,14 +93,16 @@ const Header = ({ showSidebar }) => {
}, [isOpen]);
return (
<div className="flex flex-row justify-between items-center gap-x-3 md:gap-x-4 bg-white md:px-4 px-2 shadow h-[50px] md:w-full ">
<button
className="cursor-pointer focus:outline-none"
onClick={showSidebar}
>
<i className={`fa-solid fa-bars text-xl `}></i>
</button>
<div className="flex-1">
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
<button
className="op-btn op-btn-square op-btn-ghost focus:outline-none"
onClick={showSidebar}
>
<i className="fa-solid fa-bars text-xl text-base-content"></i>
</button>
</div>
<div className="flex-1 ml-2">
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
<img
className="object-contain h-full w-auto"
@@ -79,10 +111,22 @@ const Header = ({ showSidebar }) => {
/>
</div>
</div>
<div
id="profile-menu"
className="flex justify-between items-center gap-x-3"
>
<div id="profile-menu" className="flex-none gap-2">
{!isSubscribe && (
<div>
<button
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
onClick={() => navigate("/subscription")}
>
Upgrade Now
</button>
</div>
)}
{isPro && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
PRO
</div>
)}
<div>
<FullScreenButton />
</div>
@@ -99,52 +143,66 @@ const Header = ({ showSidebar }) => {
</div>
)}
{width >= 768 && (
<div onClick={toggleDropdown} className="cursor-pointer text-sm">
<div
onClick={toggleDropdown}
className="cursor-pointer text-base-content text-sm"
>
{username && username}
</div>
)}
<div className="relative">
<div className="cursor-pointer">
<i onClick={toggleDropdown} className="fa-solid fa-angle-down"></i>
<div className="op-dropdown op-dropdown-end" id="profile-menu">
<div tabIndex={0} role="button" className="op-btn op-btn-ghost">
<i
tabIndex={0}
role="button"
onClick={toggleDropdown}
className="fa-solid fa-angle-down text-base-content"
></i>
</div>
<div
className={`dropdown absolute text-sm text-gray-700 font-light right-0 mt-4 p-1 w-48 bg-white border rounded-lg shadow-lg z-10 ${
isOpen ? "block" : "hidden"
}`}
<ul
tabIndex={0}
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
>
<ul>
<li
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
>
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
<span>
<i className="fa-solid fa-book"></i> Docs
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigation("/profile");
}}
>
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<span>
<i className="fa-regular fa-user"></i> Profile
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigation("/changepassword");
}}
>
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<span>
<i className="fa-solid fa-lock"></i> Change Password
</li>
<li
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
onClick={closeDropdown}
>
</span>
</li>
<li
onClick={() => {
window.open("https://console.opensignlabs.com/");
}}
>
<span>
<i className="fa-regular fa-id-card"></i> Console
</span>
</li>
<li onClick={closeDropdown}>
<span>
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
</li>
</ul>
</div>
</span>
</li>
</ul>
</div>
</div>
</div>
+16 -47
View File
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
// import FacebookLogin from "react-facebook-login";
const LoginFacebook = ({
@@ -49,7 +50,7 @@ const LoginFacebook = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes.get("Phone") };
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
@@ -59,7 +60,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: details.Name,
email: details.Email,
phone: extRes.get("Phone"),
phone: extRes?.get("Phone") || "",
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -75,7 +76,7 @@ const LoginFacebook = ({
const handleSubmitbtn = async () => {
if (userDetails.Phone && userDetails.Company) {
setThirdpartyLoader(true);
const params = { ...fBDetails, Phone: userDetails.Phone };
const params = { ...fBDetails, Phone: userDetails?.Phone || "" };
const payload = await Parse.Cloud.run("facebooksign", params);
// console.log("payload ", payload);
@@ -85,7 +86,7 @@ const LoginFacebook = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
phone: userDetails?.Phone || "",
role: "contracts_User",
company: userDetails.Company
}
@@ -96,7 +97,7 @@ const LoginFacebook = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails.Phone,
phone: userDetails?.Phone || "",
company: userDetails.Company
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
@@ -112,29 +113,10 @@ const LoginFacebook = ({
}
};
return (
<div style={{ position: "relative" }}>
<div className="relative">
{thirdpartyLoader && (
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
</div>
)}
{/* <FacebookLogin
@@ -146,11 +128,10 @@ const LoginFacebook = ({
/> */}
{isModal && (
<div
className="modal fade show"
className="modal fade show block z-[1]"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{ display: "block", zIndex: 1 }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
@@ -163,13 +144,8 @@ const LoginFacebook = ({
<div className="modal-body">
<form>
<div className="form-group">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="col-form-label"
>
Phone{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
<label htmlFor="Phone" className="col-form-label">
Phone <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
@@ -186,13 +162,8 @@ const LoginFacebook = ({
/>
</div>
<div className="form-group">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
<label htmlFor="Company" className="col-form-label">
Company <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
@@ -212,16 +183,14 @@ const LoginFacebook = ({
<button
type="button"
onClick={() => handleSubmitbtn()}
className="btn btn-info"
style={{ marginRight: 10 }}
className="btn btn-info mr-[10px]"
>
Sign up
</button>
<button
type="button"
className="btn btn-secondary"
className="btn btn-secondary w-[90px]"
onClick={() => setIsModal(false)}
style={{ width: 90 }}
>
Cancel
</button>
+25 -58
View File
@@ -3,7 +3,7 @@ import Parse from "parse";
import { jwtDecode } from "jwt-decode";
import { useScript } from "../hook/useScript";
import ModalUi from "../primitives/ModalUi";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
import Loader from "../primitives/Loader";
/*
* `GoogleSignInBtn`as it's name indicates it render google sign in button
@@ -110,22 +110,22 @@ const GoogleSignInBtn = ({
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes.get("Phone") };
const params = { ...details, Phone: extRes?.get("Phone") || "" };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
// setThirdpartyLoader(true);
const billingDate =
extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
// const billingDate =
// extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
// console.log("billingDate expired", billingDate > new Date());
const LocalUserDetails = {
name: details.Name,
email: details.Gmail,
phone: extRes.get("Phone"),
phone: extRes?.get("Phone") || "",
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(payload.sessiontoken, billingDate);
thirdpartyLoginfn(payload.sessiontoken);
}
return { msg: "exist" };
} else {
@@ -139,7 +139,7 @@ const GoogleSignInBtn = ({
setThirdpartyLoader(true);
// e.preventDefault()
// console.log("handelSubmit", userDetails);
const params = { ...googleDetails, Phone: userDetails.Phone };
const params = { ...googleDetails, Phone: userDetails?.Phone };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
@@ -149,7 +149,7 @@ const GoogleSignInBtn = ({
name: userDetails.Name,
email: userDetails.Email,
// "passsword":userDetails.Phone,
phone: userDetails.Phone,
phone: userDetails?.Phone || "",
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
@@ -161,7 +161,7 @@ const GoogleSignInBtn = ({
const LocalUserDetails = {
name: userDetails.Name,
email: userDetails.Email,
phone: userDetails.Phone,
phone: userDetails?.Phone || "",
company: userDetails.Company
// jobTitle: userDetails.JobTitle
};
@@ -207,45 +207,22 @@ const GoogleSignInBtn = ({
localStorage.setItem("parseAppId", appid);
};
return (
<div style={{ position: "relative" }}>
<div className="relative">
{thirdpartyLoader && (
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
<Loader />
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<form className="px-4 py-3">
<form className="px-4 py-3 text-base-content">
<div className="mb-3">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
<label htmlFor="Phone" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="tel"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
@@ -258,16 +235,12 @@ const GoogleSignInBtn = ({
/>
</div>
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
@@ -280,16 +253,12 @@ const GoogleSignInBtn = ({
/>
</div>
<div className="mb-3">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
@@ -301,20 +270,18 @@ const GoogleSignInBtn = ({
required
/>
</div>
<div>
<div className="flex flex-row gap-2">
<button
type="button"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
className="op-btn op-btn-primary"
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
>
Sign up
</button>
<button
type="button"
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
className="op-btn op-btn-ghost"
onClick={handleCloseModal}
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
+3 -18
View File
@@ -4,26 +4,11 @@ import { Stage, Layer, Rect } from "react-konva";
const RenderDebugPdf = (props) => {
return (
<div>
<div
style={{
position: "sticky",
top: 0,
padding: 10,
zIndex: 1,
backgroundColor: "white",
border: "1px solid grey",
margin: "5px 0"
}}
>
<div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
</div>
<div
style={{
flex: 1,
position: "relative",
border: "1px solid grey",
overflow: "auto"
}}
className="relative flex-1 cursor-crosshair border-[1px] border-[gray] overflow-auto"
onMouseMove={props.handleMouseMoveDiv}
>
<Document
@@ -56,7 +41,7 @@ const RenderDebugPdf = (props) => {
onMouseUp={props.handleMouseUp}
width={props.pdfDimension.width}
height={props.pdfDimension.height}
style={{ position: "absolute", top: 0, left: 0 }}
className="absolute top-0 right-0"
>
<Layer>
{props.annotations
@@ -0,0 +1,47 @@
import React from "react";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../../constant/Utils";
const DashboardButton = (props) => {
const navigate = useNavigate();
function openReport() {
if (props.Data && props.Data.Redirect_type) {
const Redirect_type = props.Data.Redirect_type;
const id = props.Data.Redirect_id;
if (Redirect_type === "Form") {
navigate(`/form/${id}`);
} else if (Redirect_type === "Report") {
navigate(`/report/${id}`);
} else if (Redirect_type === "Url") {
openInNewTab(id);
}
}
}
return (
<div
onClick={() => openReport()}
className={`${
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full shadow-md px-3 py-2 op-card bg-base-100`}
>
<div className="flex flex-row items-center">
<span className="rounded-full bg-base-content bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="op-card-title text-lg ml-3 text-base-content">
{props.Label}
</div>
</div>
</div>
);
};
export default DashboardButton;
@@ -4,6 +4,7 @@ import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import Tooltip from "../../primitives/Tooltip";
const DashboardCard = (props) => {
const navigate = useNavigate();
@@ -148,7 +149,7 @@ const DashboardCard = (props) => {
}
})
.then((res) => {
const listData = res.data?.result.filter(
const listData = res.data?.result?.filter(
(x) => x.Signers.length > 0
);
let arr = [];
@@ -335,23 +336,30 @@ const DashboardCard = (props) => {
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden`}
}`}
>
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<div className="flex items-center justify-start gap-5 text-white">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
<i
className={`${
props.Icon ? props.Icon : "fa fa-solid fa-info"
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="">
<div className="font-medium">
<div className="text-base lg:text-lg"> {props.Label}</div>
<div className="text-2xl font-light">
{loading ? <div className="loader-01"></div> : setFormat(response)}
</div>
</div>
</div>
<div className="text-xs absolute top-3 right-2">
<Tooltip
id={props.Label}
iconColor={"white"}
message={props?.Data?.tourMessage}
/>
</div>
</div>
);
};
@@ -3,6 +3,7 @@ import Parse from "parse";
import ReportTable from "../../primitives/GetReportDisplay";
import reportJson from "../../json/ReportJson";
import axios from "axios";
import Loader from "../../primitives/Loader";
function DashboardReport(props) {
// console.log("props ", props)
const [List, setList] = useState([]);
@@ -97,11 +98,13 @@ function DashboardReport(props) {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
if (!res.data.result.error) {
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
}
setIsLoader(false);
} catch (err) {
@@ -110,6 +113,7 @@ function DashboardReport(props) {
console.log("err ", err);
setIsLoader(false);
}
setIsLoader(false);
}
} else {
setIsLoader(false);
@@ -118,21 +122,8 @@ function DashboardReport(props) {
return (
<>
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="h-[250px] flex justify-center items-center">
<Loader />
</div>
) : (
<>
@@ -1,29 +1,43 @@
import React, { Suspense, lazy } from "react";
const DashboardButton = lazy(() => import("./DashboardButton"));
const DashboardCard = lazy(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport"));
const buttonList = [
{
label: "Sign yourself",
redirectId: "sHAnZphf69",
redirectType: "Form",
icon: "fas fa-pen-nib"
},
{
label: "Request signature",
redirectId: "8mZzFxbG1z",
redirectType: "Form",
icon: "fa-solid fa-paper-plane"
}
];
const GetDashboard = (props) => {
const Button = ({ label, redirectId, redirectType, icon }) => (
<DashboardButton
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
);
const renderSwitchWithTour = (col) => {
switch (col.widget.type) {
case "Card":
return (
<div
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
data-tut={col.widget.data.tourSection}
style={{ background: col.widget.bgColor }}
>
<Suspense
fallback={
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="h-[150px] w-full flex justify-center items-center">
loading...
</div>
}
>
@@ -33,7 +47,6 @@ const GetDashboard = (props) => {
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -42,7 +55,7 @@ const GetDashboard = (props) => {
return (
<div data-tut={col.widget.data.tourSection}>
<Suspense fallback={<div>please wait</div>}>
<div className="mb-4 md:mb-0">
<div className="mb-3 md:mb-0">
<DashboardReport Record={col.widget} />
</div>
</Suspense>
@@ -50,7 +63,7 @@ const GetDashboard = (props) => {
);
}
default:
return null;
return <></>;
}
};
const renderSwitch = (col) => {
@@ -58,8 +71,9 @@ const GetDashboard = (props) => {
case "Card":
return (
<div
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
style={{ background: col.widget.bgColor }}
className={`${
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
>
<Suspense fallback={<div>please wait</div>}>
<DashboardCard
@@ -68,7 +82,6 @@ const GetDashboard = (props) => {
Format={col.widget.format && col.widget.format}
Data={col.widget.data}
FilterData={col.widget.filter}
DefaultQuery={props.DefaultQuery}
/>
</Suspense>
</div>
@@ -76,33 +89,47 @@ const GetDashboard = (props) => {
case "report": {
return (
<Suspense fallback={<div>please wait</div>}>
<div className="mb-4 md:mb-0">
<div className="mb-3 md:mb-0">
<DashboardReport Record={col.widget} />
</div>
</Suspense>
);
}
default:
return null;
return <></>;
}
};
return (
<div>
{props.dashboard.map((val, key) => (
<div key={"a" + key} className="row">
{val.columns.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={props.classnameArray[key][i]}>
{renderSwitch(col)}
</div>
)
)}
<div className="mb-3">
<div
data-tut={"tourbutton"}
className="flex flex-col md:flex-row gap-4"
>
{buttonList.map((btn) => (
<Button
key={btn.label}
label={btn.label}
redirectType={btn.redirectType}
redirectId={btn.redirectId}
icon={btn.icon}
/>
))}
</div>
))}
</div>
<div className="grid grid-cols-12 w-full gap-x-4">
{props?.dashboard?.columns?.map((col, i) =>
col.widget.data && col.widget.data.tourSection ? (
<div key={i} className={col?.colsize}>
{renderSwitchWithTour(col)}
</div>
) : (
<div key={i} className={col?.colsize}>
{renderSwitch(col)}
</div>
)
)}
</div>
</div>
);
};
@@ -8,19 +8,10 @@ import { saveAs } from "file-saver";
import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import SelectFolder from "../shared/fields/SelectFolder"; //check this one
import ModalUi from "../../primitives/ModalUi";
import { themeColor } from "../../constant/const";
import FolderModal from "../shared/fields/FolderModal";
function DriveBody({
pdfData,
setFolderName,
setDocId,
setIsLoading,
setPdfData,
isList,
setIsAlert
}) {
function DriveBody(props) {
const [rename, setRename] = useState("");
const [renameValue, setRenameValue] = useState("");
const inputRef = useRef(null);
@@ -50,28 +41,25 @@ function DriveBody({
name: data.Name,
objectId: data.objectId
};
setFolderName((prev) => [...prev, folderData]);
const loadObj = {
props.setFolderName((prev) => [...prev, folderData]);
props.setIsLoading({
isLoad: true,
message: "This might take some time"
};
setIsLoading(loadObj);
setDocId(data.objectId);
});
props.setDocId(data.objectId);
props.setPdfData([]);
props.setSkip(0);
};
//function for change doc name and update doc name in _document class
const handledRenameDoc = async (data) => {
setRename("");
const trimmedValue = renameValue.trim();
if (trimmedValue.length > 0) {
const updateName = {
Name: renameValue
};
const docId = data.objectId;
const docData = pdfData;
const docData = props.pdfData;
const updatedData = docData.map((item) => {
if (item.objectId === docId) {
// If the item's ID matches the target ID, update the name
@@ -80,8 +68,8 @@ function DriveBody({
// If the item's ID doesn't match, keep it unchanged
return item;
});
setPdfData(updatedData);
props.setPdfData(updatedData);
props.sortingData(null, null, updatedData);
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
@@ -102,7 +90,7 @@ function DriveBody({
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
@@ -199,13 +187,13 @@ function DriveBody({
.then((result) => {
const res = result.data;
if (res) {
const updatedData = pdfData.filter((x) => x.objectId !== docId);
setPdfData(updatedData);
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
props.setPdfData(updatedData);
}
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
@@ -262,10 +250,10 @@ function DriveBody({
// console.log("Listdata ", Listdata);
const res = Listdata.data;
if (res) {
const updatedData = pdfData.filter(
const updatedData = props.pdfData.filter(
(x) => x.objectId !== updateDocId
);
setPdfData(updatedData);
props.setPdfData(updatedData);
}
})
.catch((err) => {
@@ -292,14 +280,13 @@ function DriveBody({
//component to handle type of document and render according to type
const handleFolderData = (data, ind, listType) => {
let createddate, status, isDecline, signerExist, isComplete, isPlaceholder;
let createddate, status, isDecline, signerExist, isComplete;
if (data.Type !== "Folder") {
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
const createdDate = data.createdAt && data.createdAt;
createddate = new Date(createdDate).toLocaleDateString();
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
isDecline = data.IsDeclined && data.IsDeclined;
isPlaceholder = data.Placeholders && data.Placeholders;
signerExist = data.Signers && data.Signers;
const signedUrl = data.SignedUrl;
@@ -314,18 +301,7 @@ function DriveBody({
status = "Completed";
} else if (isDecline) {
status = "Declined";
} else if (!signerExist || signerExist?.length === 0) {
status = "Draft";
} else if (
signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)
) {
status = "Draft";
} else if (
signerExist?.length > 0 &&
isPlaceholder?.length > 0 &&
!signedUrl
) {
} else if (!signedUrl) {
status = "Draft";
} else if (isExpire) {
status = "Expired";
@@ -336,32 +312,21 @@ function DriveBody({
const signersName = () => {
const getSignersName = signerExist.map((data) => data.Name);
const signerName = getSignersName.join(",");
const signerName = getSignersName.join(", ");
return (
<span
className="statusSpan"
style={{ width: "90%", wordWrap: "break-word" }}
>
{signerName}{" "}
</span>
<span className="statusSpan w-[90%] break-words">{signerName}</span>
);
};
return listType === "table" ? (
data.Type === "Folder" ? (
<tr onClick={() => handleOnclikFolder(data)}>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<td className="cursor-pointer flex items-center">
<i
className="fa fa-folder"
className="fa fa-folder mr-[8px] text-[26px] text-[#f0ad26]"
aria-hidden="true"
style={{ color: "#f0ad26", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
<span className="text-[12px] font-medium">{data.Name}</span>
</td>
<td>_</td>
<td>Folder</td>
@@ -370,17 +335,9 @@ function DriveBody({
</tr>
) : (
<tr onClick={() => checkPdfStatus(data)}>
<td
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
>
<i
className="fa fa-file-pdf"
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
></i>
<span style={{ fontSize: "12px", fontWeight: "500" }}>
{data.Name}
</span>
<td className="cursor-pointer flex items-center">
<i className="fa fa-file-pdf mr-[8px] text-[26px] text-[#ed4d0e]"></i>
<span className="text-[12px] font-medium">{data.Name}</span>
</td>
<td>{createddate}</td>
<td>Pdf</td>
@@ -391,30 +348,22 @@ function DriveBody({
e.stopPropagation();
handleMenuItemClick("Download", data);
}}
className="fa fa-download"
className="fa fa-download mr-[8px] text-[#ed280e]"
aria-hidden="true"
style={{ color: "#ed280e", marginRight: "8px" }}
></i>
</td>
</tr>
)
) : listType === "list" && data.Type === "Folder" ? (
<div key={ind} className="folderBox">
<div key={ind} className="icon-container">
<ContextMenu.Root>
<ContextMenu.Trigger className="ContextMenuTrigger">
<div
style={{
display: "flex",
flexDirection: "column",
cursor: "pointer"
}}
>
<div data-tut={props.dataTutSeventh}>
<img
alt="no img"
alt="folder"
onClick={() => handleOnclikFolder(data)}
src={folder}
width={100}
height={103}
className="w-full h-full"
/>
{rename === data.objectId ? (
<input
@@ -433,12 +382,7 @@ function DriveBody({
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px"
}}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
/>
) : (
<span className="foldName">{data.Name}</span>
@@ -456,8 +400,8 @@ function DriveBody({
onClick={() => handleMenuItemClick("Rename", data)}
className="ContextMenuItem"
>
<i className="fa-solid fa-font"></i>
<span style={{ marginLeft: "8px" }}>Rename</span>
<i className="fa-solid fa-font mr-[8px]"></i>
<span>Rename</span>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
@@ -472,9 +416,10 @@ function DriveBody({
<ContextMenu.Trigger className="ContextMenuTrigger">
<img
alt="PDF"
className="pdf-icon"
className="w-full h-full"
src={pdfLogo}
onClick={() => checkPdfStatus(data)}
data-tut={props.dataTutSixth}
/>
{rename === data.objectId ? (
<input
@@ -493,12 +438,7 @@ function DriveBody({
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
style={{
width: "100px",
border: "1.5px solid black",
borderRadius: "2px",
fontSize: "10px"
}}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
/>
) : (
<span className="fileName">{data.Name}</span>
@@ -543,7 +483,7 @@ function DriveBody({
className="ContextMenuItem"
>
<i className={menu.icon}></i>
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
<span className="ml-[8px]">{menu.type}</span>
</ContextMenu.Item>
);
})}
@@ -554,21 +494,21 @@ function DriveBody({
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong style={{ fontSize: "13px" }}>Title: </strong>
<strong className="text-[13px]">Title: </strong>
<span className="statusSpan" style={{ marginBottom: "0px" }}>
{" "}
{data.Name}
</span>
<br />
<strong style={{ fontSize: "13px" }}>Status: </strong>
<strong className="text-[13px]">Status: </strong>
<span className="statusSpan"> {status}</span>
<br />
<strong style={{ fontSize: "13px" }}>Created Date: </strong>
<strong className="text-[13px]">Created Date: </strong>
<span className="statusSpan">{createddate}</span>
<br />
{signerExist && (
<>
<strong style={{ fontSize: "13px" }}>Signers: </strong>
<strong className="text-[13px]">Signers: </strong>
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
{signersName()}
</>
@@ -584,7 +524,7 @@ function DriveBody({
//component to handle type of document and render according to type
return (
<>
{isList ? (
{props.isList ? (
<div className="container" style={{ overflowX: "auto" }}>
<Table striped bordered hover>
<thead>
@@ -597,7 +537,7 @@ function DriveBody({
</tr>
</thead>
<tbody>
{pdfData.map((data, ind) => {
{props.pdfData.map((data, ind) => {
return (
<React.Fragment key={ind}>
{handleFolderData(data, ind, "table")}
@@ -608,65 +548,41 @@ function DriveBody({
</Table>
</div>
) : (
<div className="pdfContainer">
{pdfData.map((data, ind) => {
return (
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
</div>
);
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
{props.pdfData.map((data, ind) => {
return <div key={ind}>{handleFolderData(data, ind, "list")}</div>;
})}
</div>
)}
{isOpenMoveModal && (
<SelectFolder
<FolderModal
onSuccess={handleMoveFolder}
isOpenModal={isOpenMoveModal}
folderCls={"contracts_Document"}
setIsOpenMoveModal={setIsOpenMoveModal}
setPdfData={setPdfData}
setPdfData={props.setPdfData}
/>
)}
<ModalUi
isOpen={isDeleteDoc}
headerColor={themeColor}
title={"Delete Document"}
handleClose={() => {
setIsDeleteDoc(false);
}}
handleClose={() => setIsDeleteDoc(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="h-full p-[20px] text-base-content">
<p>Are you sure you want to delete this document?</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
<button
onClick={() => {
handleDeleteDocument(selectDoc);
}}
style={{
background: themeColor,
color: "white"
}}
onClick={() => handleDeleteDocument(selectDoc)}
type="button"
className="finishBtn"
className="op-btn op-btn-primary mr-2"
>
Yes
</button>
<button
onClick={() => {
setIsDeleteDoc(false);
}}
onClick={() => setIsDeleteDoc(false)}
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-neutral"
>
No
</button>
@@ -8,11 +8,8 @@ const AddRoleModal = (props) => {
isOpen={props.isModalRole}
handleClose={props.handleCloseRoleModal}
>
<div className="addusercontainer">
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={props.handleAddRole}
>
<div className="h-full py-[10px] px-[20px]">
<form className="flex flex-col" onSubmit={props.handleAddRole}>
<input
value={props.roleName}
onChange={(e) => props.setRoleName(e.target.value)}
@@ -21,39 +18,20 @@ const AddRoleModal = (props) => {
? "User " + (props.signersdata.length + 1)
: "User 1"
}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
/>
<p
style={{
color: "grey",
fontSize: 11,
margin: "10px 0 10px 5px"
}}
>
e.g: Hr, Director, Manager, New joinee, Accountant, etc...
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
e.g: Customer, Hr, Director, Manager, Student, etc...
</p>
<div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginBottom: "15px"
}}
></div>
<button
type="submit"
style={{
background: "#00a2b7"
}}
className="finishBtn"
>
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
<button type="submit" className="op-btn op-btn-primary">
Add
</button>
<button
onClick={props.handleCloseRoleModal}
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-ghost ml-2"
>
Close
</button>
@@ -1,5 +1,4 @@
import React from "react";
import { themeColor } from "../../constant/const";
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
@@ -17,55 +16,26 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
};
return (
<div>
<div
style={{
background: themeColor,
color: "white",
padding: "5px",
fontFamily: "sans-serif",
marginTop: "5px"
}}
>
<div data-tut="reactourThird">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signature
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
marginTop: "10px",
fontWeight: "600"
}}
>
<>
<p>Your Signature</p>
<div className="defaultSignBox">
<img
alt="default img"
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
src={defaultSignImg}
/>
</div>
<button
style={{
background: themeColor,
color: "white",
marginTop: "20px",
cursor: "pointer",
marginBottom: "10px"
}}
type="button"
className="finishBtn finishnHover"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</>
<div className="flex flex-col items-center mt-[10px] font-semibold">
<p className="text-base-content">Your Signature</p>
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
<img
alt="signature"
className="w-full h-full object-contain"
src={defaultSignImg}
/>
</div>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
</button>
</div>
</div>
);
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import Loader from "../../primitives/LoaderWithMsg";
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError";
function DraftDocument() {
@@ -86,7 +86,7 @@ function DraftDocument() {
return (
<div>
{isLoading.isLoader ? (
<Loader isLoading={isLoading} />
<LoaderWithMsg isLoading={isLoading} />
) : (
<HandleError handleError={isLoading.message} />
)}
@@ -1,7 +1,9 @@
import React, { useEffect, useState } from "react";
import { themeColor } from "../../constant/const";
import { isEnableSubscription } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
function DropdownWidgetOption(props) {
const [dropdownOptionList, setDropdownOptionList] = useState([
"option-1",
@@ -144,53 +146,34 @@ function DropdownWidgetOption(props) {
};
return (
//props.showDropdown
<ModalUi
// styleClass={"dropdownModal"}
isOpen={props.showDropdown}
title={props.title}
showClose={false}
>
<div style={{ height: "100%", padding: 20 }}>
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
<div className="h-full p-[15px] text-base-content">
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveOption();
}}
>
<div className="dropdownContainer">
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
<div>
<label className="text-[13px] font-semibold">
Name<span className="text-[red] text-[13px]"> *</span>
</label>
<input
required
defaultValue={dropdownName}
value={dropdownName}
onChange={(e) => setDropdownName(e.target.value)}
className="drodown-input"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<label
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
>
<label className="text-[13px] font-semibold mt-[5px]">
Options
</label>
<div
style={{
display: "flex",
flexDirection: "column"
}}
>
<div className="flex flex-col">
{dropdownOptionList.map((option, index) => (
<div
key={index}
style={{
display: "flex",
flexDirection: "row",
marginBottom: "5px",
alignItems: "center"
}}
className="flex flex-row mb-[5px] items-center"
>
{props.type === "checkbox" && !props.isSignYourself && (
<input
@@ -210,101 +193,95 @@ function DropdownWidgetOption(props) {
setDefaultCheckbox(removeOption);
}
}}
style={{
width: "23px",
height: "19px",
marginRight: "5px"
}}
className="op-checkbox focus:outline-none hover:border-base-content mr-[5px]"
/>
)}
<input
required
className="drodown-input"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
type="text"
value={option}
onChange={(e) => handleInputChange(index, e.target.value)}
/>
<i
className="fa-solid fa-rectangle-xmark"
className="fa-solid fa-rectangle-xmark text-[25px] ml-[10px] text-error"
onClick={() => handleDeleteInput(index)}
style={{
color: "red",
fontSize: "25px",
marginLeft: "10px"
}}
></i>
</div>
))}
<i
onClick={handleAddInput}
style={{
cursor: "pointer",
color: themeColor,
fontSize: "25px",
marginLeft: "10px"
}}
className="fa-solid fa-square-plus"
className="fa-solid fa-square-plus text-[25px] ml-[10px] text-primary"
></i>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Minimun check
</label>
<input
required
defaultValue={0}
value={minCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className="drodown-input"
/>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className="drodown-input"
/>
</>
)}
<div>
{props.type === "checkbox" && !props.isSignYourself && (
<>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
Minimun check
</label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
<input
required
defaultValue={0}
value={minCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMinCount(count);
}}
className={`${
props.isSubscribe || !isEnableSubscription
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/>
<label
className={`${
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
Maximum check
</label>
<input
required
defaultValue={0}
value={maxCount}
onChange={(e) => {
const count = handleSetMinMax(e);
setMaxCount(count);
}}
className={`${
props.isSubscribe || !isEnableSubscription
? ""
: "pointer-events-none bg-opacity-50"
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
/>
</>
)}
</div>
</div>
{["dropdown", radioButtonWidget].includes(props.type) && (
<>
<label
style={{
fontSize: "13px",
fontWeight: "600",
marginTop: "5px"
}}
>
<label className="text-[13px] font-semibold mt-[5px]">
Default value
</label>
<select
onChange={(e) => setDefaultValue(e.target.value)}
className="drodown-input"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
name="defaultvalue"
value={defaultValue}
placeholder="select default value"
>
<option value="" disabled hidden style={{ fontSize: "13px" }}>
<option value="" disabled hidden className="text-[13px]">
Select...
</option>
{dropdownOptionList.map((data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
<option className="text-[13px]" key={ind} value={data}>
{data}
</option>
);
@@ -314,35 +291,21 @@ function DropdownWidgetOption(props) {
)}
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
<>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginTop: "0.5rem"
}}
>
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div
key={ind}
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
className="flex flex-row gap-[5px] items-center"
>
<input
style={{ accentColor: "red" }}
className="op-radio op-radio-xs my-1"
type="radio"
name="status"
onChange={() => setStatus(data.toLowerCase())}
checked={status.toLowerCase() === data.toLowerCase()}
/>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
<div className="text-[13px] font-500">{data}</div>
</div>
);
})}
@@ -350,64 +313,65 @@ function DropdownWidgetOption(props) {
</>
)}
{["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 mt-2 items-center text-center">
<div className="flex flex-row gap-5 my-2 items-center text-center">
{!props.isSignYourself && (
<div>
<div className="flex items-center">
<input
id="isreadonly"
type="checkbox"
checked={isReadOnly}
onChange={(e) => {
setIsReadOnly(e.target.checked);
}}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsReadOnly(e.target.checked)}
/>
<label className="ml-1" htmlFor="isreadonly">
<label className="ml-1 mb-0" htmlFor="isreadonly">
Is read only
</label>
</div>
)}
<div>
<div className="flex items-center">
<input
id="ishidelabel"
type="checkbox"
checked={isHideLabel}
onChange={(e) => {
setIsHideLabel(e.target.checked);
}}
className="op-checkbox op-checkbox-sm"
onChange={(e) => setIsHideLabel(e.target.checked)}
/>
<label className="ml-1" htmlFor="ishidelabel">
Hide label
<label className="ml-1 mb-0" htmlFor="ishidelabel">
Hide labels
</label>
</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
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
className={`${
props.type === "checkbox" && !props.isSignYourself
? "mb-[15px]"
: "my-[15px]"
} w-full h-[1px] bg-[#9f9f9f]`}
></div>
<button
// onClick={() => handleSaveOption()}
disabled={dropdownOptionList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="submit"
className={"finishBtn"}
className="op-btn op-btn-primary"
>
Save
</button>
{props.currWidgetsDetails?.options?.values?.length > 0 && (
<button
type="submit"
className="finishBtn cancelBtn"
className="op-btn op-btn-ghost ml-1"
onClick={() => {
props.handleClose && props.handleClose();
resetState();
+105 -50
View File
@@ -1,5 +1,8 @@
import React, { useState } from "react";
import "../../styles/AddUser.css";
import React, { useState, useEffect } from "react";
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
@@ -8,9 +11,21 @@ const EditTemplate = ({ template, onSuccess }) => {
Name: template?.Name || "",
Note: template?.Note || "",
Description: template?.Description || "",
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
AutomaticReminders: template?.AutomaticReminders || false,
RemindOnceInEvery: template?.RemindOnceInEvery || 5
});
const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => {
fetchSubscription();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
};
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
@@ -24,34 +39,40 @@ const EditTemplate = ({ template, onSuccess }) => {
e.preventDefault();
e.stopPropagation();
const isChecked = formData.SendinOrder === "true" ? true : false;
const data = { ...formData, SendinOrder: isChecked };
const AutoReminder = formData?.AutomaticReminders || false;
let reminderDate = {};
if (AutoReminder) {
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
const ReminderDate = new Date(template?.createdAt);
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
reminderDate = { NextReminderDate: ReminderDate };
}
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
onSuccess(data);
};
const handleAutoReminder = () => {
setFormData((prev) => ({
...prev,
AutomaticReminders: !formData.AutomaticReminders
}));
};
return (
<div className="addusercontainer">
<div className="form-wrapper">
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
<div className="text-base-content">
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name" style={{ fontSize: 13 }}>
<label htmlFor="name" className="text-[13px]">
File
</label>
<div
style={{
padding: "0.5rem 0.75rem",
border: "1px solid #d1d5db",
borderRadius: "0.375rem",
fontSize: "0.75rem",
fontWeight: "700"
}}
>
{template.URL?.split("/")?.pop()?.split("_")[1]}
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
{getFileName(template.URL)}
</div>
</div>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
<label htmlFor="name" className="text-[13px]">
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
type="text"
@@ -59,11 +80,11 @@ const EditTemplate = ({ template, onSuccess }) => {
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label htmlFor="Note" style={{ fontSize: 13 }}>
<label htmlFor="Note" className="text-[13px]">
Note
</label>
<input
@@ -72,11 +93,11 @@ const EditTemplate = ({ template, onSuccess }) => {
id="Note"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label htmlFor="Description" style={{ fontSize: 13 }}>
<label htmlFor="Description" className="text-[13px]">
Description
</label>
<input
@@ -85,51 +106,85 @@ const EditTemplate = ({ template, onSuccess }) => {
id="Description"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label style={{ fontSize: 13 }}>Send In Order</label>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<label className="text-[13px]">Send In Order</label>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
type="radio"
value={"true"}
className="op-radio op-radio-xs"
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>Yes</div>
<div className="text-[12px]">Yes</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
type="radio"
value={"false"}
name="SendinOrder"
className="op-radio op-radio-xs"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>No</div>
<div className="text-[12px]">No</div>
</div>
</div>
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
<div className="buttoncontainer">
<button type="submit" className="submitbutton">
<div className="text-xs mt-2">
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Disable Auto reminder is free in beta, this feature will incur a fee later."
}
/>
)}
<span
className={
isSubscribe || !isEnableSubscription
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
className={`${
isSubscribe || !isEnableSubscription
? "cursor-pointer "
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
checked={formData.AutomaticReminders}
onChange={handleAutoReminder}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
/>
</label>
</div>
{isSubscribe && formData?.AutomaticReminders === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
type="number"
value={formData.RemindOnceInEvery}
name="RemindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
required
/>
</div>
)}
<div className="mt-[1rem] flex justify-start">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
</div>
@@ -0,0 +1,157 @@
import React from "react";
import { Quill } from "react-quill";
// Custom Undo button icon component for Quill editor. You can import it directly
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
// handle them correctly
const CustomUndo = () => (
<svg viewBox="0 0 18 18">
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
<path
className="ql-stroke"
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
/>
</svg>
);
// Redo button icon component for Quill editor
const CustomRedo = () => (
<svg viewBox="0 0 18 18">
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
<path
className="ql-stroke"
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
/>
</svg>
);
// Undo and redo functions for Custom Toolbar
function undoChange() {
this.quill.history.undo();
}
function redoChange() {
this.quill.history.redo();
}
// Add sizes to whitelist and register them
const Size = Quill.import("formats/size");
Size.whitelist = ["extra-small", "small", "medium", "large"];
Quill.register(Size, true);
// Add fonts to whitelist and register them
const Font = Quill.import("formats/font");
Font.whitelist = [
"arial",
"comic-sans",
"courier-new",
"georgia",
"helvetica",
"lucida"
];
Quill.register(Font, true);
// Modules object for setting up the Quill editor
export const module1 = {
toolbar: {
container: "#toolbar1",
handlers: {
undo: undoChange,
redo: redoChange
}
},
history: {
delay: 500,
maxStack: 100,
userOnly: true
}
};
// Formats objects for setting up the Quill editor
export const formats = [
"header",
"font",
"size",
"bold",
"italic",
"underline",
"align",
"strike",
"script",
"blockquote",
"background",
"list",
"bullet",
"indent",
"link",
"image",
"color",
"code-block"
];
// Quill Toolbar component
export const QuillToolbar = ({ containerId }) => (
<div id={containerId}>
<span className="ql-formats">
<select className="ql-font" defaultValue="arial">
<option value="arial">Arial</option>
<option value="comic-sans">Comic Sans</option>
<option value="courier-new">Courier New</option>
<option value="georgia">Georgia</option>
<option value="helvetica">Helvetica</option>
<option value="lucida">Lucida</option>
</select>
<select className="ql-size" defaultValue="medium">
<option value="extra-small">Size 1</option>
<option value="small">Size 2</option>
<option value="medium">Size 3</option>
<option value="large">Size 4</option>
</select>
<select className="ql-header" defaultValue="3">
<option value="1">Heading</option>
<option value="2">Subheading</option>
<option value="3">Normal</option>
</select>
</span>
<span className="ql-formats">
<button className="ql-bold" />
<button className="ql-italic" />
<button className="ql-underline" />
<button className="ql-strike" />
</span>
<span className="ql-formats">
<button className="ql-list" value="ordered" />
<button className="ql-list" value="bullet" />
<button className="ql-indent" value="-1" />
<button className="ql-indent" value="+1" />
</span>
<span className="ql-formats">
<button className="ql-script" value="super" />
<button className="ql-script" value="sub" />
<button className="ql-blockquote" />
<button className="ql-direction" />
</span>
<span className="ql-formats">
<select className="ql-align" />
<select className="ql-color" />
<select className="ql-background" />
</span>
<span className="ql-formats">
<button className="ql-link" />
<button className="ql-image" />
<button className="ql-video" />
</span>
<span className="ql-formats">
<button className="ql-formula" />
<button className="ql-code-block" />
<button className="ql-clean" />
</span>
<span className="ql-formats">
<button className="ql-undo">
<CustomUndo />
</button>
<button className="ql-redo">
<CustomRedo />
</button>
</span>
</div>
);
export default QuillToolbar;
@@ -0,0 +1,42 @@
import React from "react";
import Tooltip from "../../primitives/Tooltip";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import EditorToolbar, { module1, formats } from "./EditorToolbar";
export function EmailBody(props) {
return (
<form className="flex flex-col text-base-content text-lg font-normal">
<div className="m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
<label className="text-sm ml-2">
Subject <Tooltip message={"email subject"} />
</label>
<input
required
value={props.requestSubject}
onChange={(e) => props.setRequestSubject(e.target.value)}
placeholder='${senderName} has requested you to sign "${documentName}"'
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<label className="text-sm ml-2 mt-3">
Body <Tooltip message={"email body"} />
</label>
<div className="px-3 py-2 w-full focus:outline-none text-xs">
<EditorToolbar containerId="toolbar1" />
<ReactQuill
theme="snow"
value={props.requestBody}
// value={typedText}
placeholder="add body of email "
// readOnly={loading}
// onChangeSelection={handleTextSelection} // Listen for text selection
ref={props.editorRef}
modules={module1}
formats={formats}
onChange={props.handleOnchangeRequest}
/>
</div>
</div>
</form>
);
}
@@ -5,24 +5,27 @@ import axios from "axios";
import { getBase64FromUrl } from "../../constant/Utils";
import { themeColor } from "../../constant/const";
import printModule from "print-js";
import loader from "../../assets/images/loader2.gif";
import Loader from "../../primitives/Loader";
function EmailComponent({
isEmail,
pdfUrl,
isCeleb,
setIsEmail,
setSuccessEmail,
pdfName,
sender,
setIsAlert
setIsAlert,
extUserId,
activeMailAdapter
}) {
const [emailList, setEmailList] = useState([]);
const [emailValue, setEmailValue] = useState();
const [isLoading, setIsLoading] = useState(false);
const [isEmailCelebration, setIsEmailCelebration] = useState(false);
//function for send email
const sendEmail = async () => {
setIsLoading(true);
let sendMail;
for (let i = 0; i < emailList.length; i++) {
try {
@@ -38,6 +41,8 @@ function EmailComponent({
const openSignUrl = "https://www.opensignlabs.com/contact-us";
const themeBGcolor = themeColor;
let params = {
mailProvider: activeMailAdapter,
extUserId: extUserId,
pdfName: pdfName,
url: pdfUrl,
recipient: emailList[i],
@@ -57,6 +62,10 @@ function EmailComponent({
" target=_blank>here</a> </p></div></div></body></html>"
};
sendMail = await axios.post(url, params, { headers: headers });
setIsEmailCelebration(true);
setTimeout(() => {
setIsEmailCelebration(false);
}, 3000);
} catch (error) {
console.log("error", error);
setIsLoading(false);
@@ -164,148 +173,59 @@ function EmailComponent({
<div className="modaloverlay">
<div className="modalcontainer">
{isLoading && (
<div
style={{
position: "absolute",
height: "100%",
width: "100%",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
zIndex: "20",
backgroundColor: "#e6f2f2",
opacity: 0.8
}}
>
<img
alt="no img"
src={loader}
style={{ width: "70px", height: "70px" }}
/>
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2] bg-opacity-80">
<Loader />
<span className="text-[12px] font-bold">
This might take some time
</span>
</div>
)}
<div style={{ background: "#32a3ac" }} className="modalheader">
<span style={{ color: "white" }}>Successfully signed!</span>
<div style={{ display: "flex", flexDirection: "row" }}>
<span className="text-white">Successfully signed!</span>
<div className="flex flex-row">
<div></div>
{!isAndroid && (
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="btn btn-primary btn-sm"
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
<i className="fa fa-print" aria-hidden="true"></i>
Print
</button>
)}
<button
className="btn btn-danger btn-sm"
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
onClick={() => handleDownloadPdf()}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
<i className="fa fa-download" aria-hidden="true"></i>
Download
</button>
</div>
</div>
<div style={{ height: "100%", padding: 20 }}>
{isCeleb && (
<div
style={{
position: "absolute",
marginLeft: "50px"
}}
>
<img
alt="print img"
width={300}
height={250}
// style={styles.gifCeleb}
src={celebration}
/>
<div className="h-full p-[20px]">
{isEmailCelebration && (
<div className="absolute w-[100%] flex justify-center items-center">
<img alt="celeb" width={300} height={250} src={celebration} />
</div>
)}
<p
style={{
fontFamily: "system-ui",
verticalAlign: "baseline",
fontWeight: "500",
color: "#403f3e",
fontSize: "15px",
marginBottom: "5px"
}}
>
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
Recipients added here will get a copy of the signed document.
</p>
{emailList.length > 0 ? (
<>
<div className="addEmail">
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap"
}}
>
<div className="flex flex-row flex-wrap">
{emailList.map((data, ind) => {
return (
<div
className="emailChip"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="flex flex-row items-center bg-[#47a3ad] m-[4px] rounded-md py-[5px] px-[10px]"
key={ind}
>
<span
style={{
color: "white",
fontSize: "13px"
}}
>
<span className="text-white text-[13px]">
{data}
</span>
<span
style={{
color: "white",
fontSize: 13,
fontWeight: 600,
marginLeft: 7,
cursor: "pointer"
}}
className="text-white text-[13px] font-semibold ml-[7px] cursor-pointer"
onClick={() => removeChip(ind)}
>
<i className="fa-solid fa-xmark"></i>
@@ -318,7 +238,7 @@ function EmailComponent({
<input
type="text"
value={emailValue}
className="addEmailInput"
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-white outline-none"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => {
@@ -336,7 +256,7 @@ function EmailComponent({
<input
type="text"
value={emailValue}
className="emailInput"
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-white border-2 border-[#47a3ad]"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add the email addresses"
@@ -349,67 +269,36 @@ function EmailComponent({
/>
</div>
)}
<span
style={{
cursor: emailValue && "pointer"
}}
<button
className={`${
emailValue ? "cursor-pointer" : "cursor-default"
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
onClick={() => {
if (emailValue) {
handleEnterPress("add");
}
}}
>
<i
style={{
backgroundColor: themeColor,
padding: "5px 7px",
marginTop: "10px",
color: "white",
borderRadius: "2px"
}}
className="fa fa-plus"
aria-hidden="true"
></i>
</span>
<i className="fa fa-plus" aria-hidden="true"></i>
</button>
<div
style={{
background: "#e3e2e1",
marginTop: "10px",
padding: "5px",
borderRadius: "3px"
}}
>
<span style={{ fontWeight: "700" }}>Note:</span>
<span style={{ fontSize: "15px" }}>
{" "}
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
<span className="font-bold">Note: </span>
<span className="text-[15px]">
You can only send to ten recipients at a time.
</span>
</div>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
disabled={emailList.length === 0 && true}
style={{
background: themeColor,
color: "white"
}}
type="button"
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
onClick={() => sendEmail()}
className="op-btn op-btn-primary"
onClick={() => emailList.length > 0 && sendEmail()}
>
Send
</button>
<button
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-ghost ml-2"
onClick={() => {
setIsEmail(false);
setEmailValue("");
+254 -521
View File
@@ -1,26 +1,24 @@
import React from "react";
import React, { useState } from "react";
import PrevNext from "./PrevNext";
import { PDFDownloadLink } from "@react-pdf/renderer";
import Certificate from "./Certificate";
import printModule from "print-js";
import { getBase64FromUrl } from "../../constant/Utils";
import { saveAs } from "file-saver";
import {
handleDownloadCertificate,
handleDownloadPdf,
handleToPrint
} from "../../constant/Utils";
import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useNavigate } from "react-router-dom";
import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
function Header({
isPdfRequestFiles,
isPlaceholder,
recipient,
setIsDecline,
pageNumber,
isAlreadySign,
allPages,
changePage,
pdfUrl,
documentStatus,
embedWidgetsData,
pdfDetails,
signerPos,
@@ -42,15 +40,9 @@ function Header({
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
const navigate = useNavigate();
const [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner");
//for go to previous page
function previousPage() {
changePage(-1);
}
//for go to next page
function nextPage() {
changePage(1);
}
//function for show decline alert
const handleDeclinePdfAlert = async () => {
const currentDecline = {
@@ -60,143 +52,6 @@ function Header({
setIsDecline(currentDecline);
};
//function for print digital sign pdf
const handleToPrint = async (event) => {
event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
!window.MSStream;
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(
atob(pdf)
.split("")
.map((char) => char.charCodeAt(0))
);
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
};
//handle download signed pdf
const handleDownloadPdf = () => {
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
//certificate generate and download component in mobile view
const CertificateDropDown = () => {
//after generate download certifcate pdf
const handleDownload = (pdfBlob, fileName) => {
if (pdfBlob) {
const url = window.URL.createObjectURL(pdfBlob);
// Create a temporary anchor element
const link = document.createElement("a");
link.href = url;
link.download = fileName;
// Append the anchor to the body
document.body.appendChild(link);
// Programmatically click the anchor to trigger the download
link.click();
// Remove the anchor from the body
document.body.removeChild(link);
// Release the object URL to free up resources
window.URL.revokeObjectURL(url);
}
};
return (
<PDFDownloadLink
onClick={(e) => e.preventDefault()}
style={{ textDecoration: "none", zIndex: "35" }}
document={<Certificate pdfData={pdfDetails} />}
>
{({ blob, loading }) => (
<>
{loading ? (
<div
style={{
border: "none",
backgroundColor: "#fff"
}}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
) : (
<div
style={{ border: "none", backgroundColor: "#fff" }}
onClick={() =>
handleDownload(
blob,
`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`
)
}
>
<i
className="fa-solid fa-award"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Certificate
</div>
)}
</>
)}
</PDFDownloadLink>
);
};
const CertificateComponent = () => {
return (
<PDFDownloadLink
style={{ textDecoration: "none" }}
document={<Certificate pdfData={pdfDetails} />}
fileName={`completion certificate-${
pdfDetails[0] && pdfDetails[0].Name
}.pdf`}
>
{({ loading }) => (
<button
type="button"
className="defaultBtn certificateBtn"
disabled={loading}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
>
<i
className="fa-solid fa-award"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Certificate
</button>
)}
</PDFDownloadLink>
);
};
return (
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
{isMobile && isShowHeader ? (
@@ -209,64 +64,25 @@ function Header({
: window.innerWidth - 30 + "px"
}}
>
<div className="preBtn2">
<div
onClick={() => {
navigate(-1);
}}
>
<div className="flex justify-between items-center py-[5px] px-[10px] ">
<div onClick={() => navigate(-1)}>
<i
className="fa fa-arrow-left"
className="fa fa-arrow-left text-base-content"
aria-hidden="true"
style={{ color: themeColor, cursor: "pointer" }}
></i>
</div>
<div>
<i
onClick={() => {
if (pageNumber > 1) {
previousPage();
}
}}
className="fa fa-backward"
style={{ color: "gray", cursor: "pointer" }}
></i>
<span
style={{
fontSize: "13px",
fontWeight: "600",
marginLeft: "10px",
marginRight: "10px"
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<i
onClick={() => {
if (pageNumber < allPages) {
nextPage();
}
}}
className="fa fa-forward"
style={{ color: "gray", cursor: "pointer" }}
></i>
</div>
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{pdfUrl && alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px",
padding: "0px 3px 0px 5px"
}}
>
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="DropdownMenuContent"
@@ -274,74 +90,61 @@ function Header({
>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => handleDownloadPdf()}
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<div className="flex flex-row">
<i
className="fa fa-arrow-down"
className="fa fa-arrow-down mr-[2px]"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Download
</div>
</DropdownMenu.Item>
{recipient && pdfDetails[0] && pdfDetails.length > 0 ? (
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
{isCompleted && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() =>
handleDownloadCertificate(
pdfDetails,
setIsDownloading
)
}
>
<div className="border-none bg-[#fff]">
<i
className="fa-solid fa-award mr-[2px]"
aria-hidden="true"
></i>
Certificate
</div>
</DropdownMenu.Item>
) : isPdfRequestFiles &&
alreadySign &&
isCompleted.isCertificate ? (
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
)}
{isSignYourself && (
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div className="flex flex-row">
<i
className="fa fa-envelope mr-[2px]"
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
) : (
isSignYourself && (
<>
<DropdownMenu.Item className="DropdownMenuItem">
<CertificateDropDown />
</DropdownMenu.Item>
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={() => setIsEmail(true)}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
<i
className="fa fa-envelope"
style={{ marginRight: "2px" }}
aria-hidden="true"
></i>
Mail
</div>
</DropdownMenu.Item>
</>
)
)}
<DropdownMenu.Item
className="DropdownMenuItem"
onClick={handleToPrint}
onClick={(e) =>
handleToPrint(e, pdfUrl, setIsDownloading)
}
>
<div
style={{
display: "flex",
flexDirection: "row"
}}
>
{" "}
<div className="flex flex-row">
<i
className="fa fa-print"
className="fa fa-print mr-[2px]"
aria-hidden="true"
style={{ marginRight: "2px" }}
></i>
Print
</div>
@@ -350,24 +153,16 @@ function Header({
</DropdownMenu.Portal>
</DropdownMenu.Root>
) : (
<div style={{ display: "flex", justifyContent: "space-around" }}>
<div className="flex justify-around items-center">
{/* current signer is checking user send request and check status of pdf sign than if current
user exist than show finish button else no
*/}
{currentSigner && (
<>
<div className="flex items-center" data-tut="reactourFifth">
{decline && (
<div
onClick={() => {
handleDeclinePdfAlert();
}}
style={{
color: "red",
border: "none",
fontWeight: "650",
fontSize: "16px",
marginRight: "10px"
}}
onClick={() => handleDeclinePdfAlert()}
className="text-[red] border-none font-[650] text-[14px] mr-2"
>
Decline
</div>
@@ -379,12 +174,9 @@ function Header({
alertSendEmail();
}
}}
style={{
color: isMailSend ? "gray" : themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
className={`${
isMailSend ? "" : "op-link-primary"
} op-link no-underline font-[650] text-[14px]`}
data-tut={dataTut4}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
@@ -399,343 +191,259 @@ function Header({
embedWidgetsData();
}
}}
style={{
color: themeColor,
border: "none",
fontWeight: "650",
fontSize: "16px"
}}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
>
Finish
</div>
)}
</>
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="font-[650] text-[18px] px-2 ml-[4px] op-link op-link-info no-underline">
<i
className="fa fa-ellipsis-v"
aria-hidden="true"
></i>
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="bg-white shadow-md rounded px-2 py-1"
sideOffset={5}
>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i
className="fa fa-arrow-down mr-[5px]"
aria-hidden="true"
></i>
<span className="font-[500]">Download</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</div>
)}
</div>
)}
</div>
</div>
) : (
<div className="signatureHeader headerBtn">
<div className="flex flex-wrap justify-between items-center mt-[5px]">
<PrevNext
pageNumber={pageNumber}
allPages={allPages}
changePage={changePage}
/>
{recipient ? (
pdfUrl || isAlreadySign.mssg ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{pdfDetails && pdfDetails.length > 0 && (
<CertificateComponent />
)}
<button
onClick={handleToPrint}
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn printBtn"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
</button>
<button
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn downloadBtn"
onClick={() => handleDownloadPdf()}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
</button>
</div>
) : (
<div style={{ display: "flex", flexDirection: "row" }}>
<button
style={{
background: "#de4337"
}}
className="finishBtn hoverCss"
onClick={() => {
handleDeclinePdfAlert();
}}
>
Decline
</button>
<button
data-tut="reactourThird"
style={{
background: !pdfUrl ? "#188ae2" : "#d3edeb"
}}
className="finishBtn sendHover"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
>
Finish
</button>
</div>
)
) : isPlaceholder ? (
{isPlaceholder ? (
<>
{!isMailSend &&
signersdata.length > 0 &&
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span style={{ fontSize: "13px", color: "#f5405e" }}>
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
)}
</div>
)}
<div>
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
{!isMailSend &&
signersdata.length > 0 &&
signersdata.length !== filterPrefill.length && (
<div>
{filterPrefill.length === 0 ? (
<span className="text-[13px] text-[#f5405e]">
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
</span>
) : (
<span className="text-[13px] text-[#f5405e]">
Add {signersdata.length - filterPrefill.length} more
recipients signature
</span>
)}
</div>
)}
</div>
<div className="flex">
{setIsEditTemplate && (
<button
onClick={() => setIsEditTemplate(true)}
style={{
border: "none",
outline: "none",
textAlign: "center"
}}
className="outline-none border-none text-center mr-[5px]"
>
<i className="fa-solid fa-gear fa-lg"></i>
</button>
)}
<button
onClick={() => {
navigate(-1);
}}
onClick={() => navigate(-1)}
type="button"
className="defaultBtn backBtn"
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
<button
disabled={isMailSend && true}
data-tut="reactourFour"
style={{
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
color: isMailSend && "rgb(77, 75, 75)"
}}
onClick={() => {
alertSendEmail();
}}
className={isMailSend ? "sendMail" : "sendMail sendHover"}
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
onClick={() => alertSendEmail()}
>
{completeBtnTitle ? completeBtnTitle : "Send"}
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
</button>
</div>
</>
) : isPdfRequestFiles ? (
alreadySign ? (
<div style={{ display: "flex", flexDirection: "row" }}>
{isCompleted.isCertificate && <CertificateComponent />}
<div className="flex flex-row">
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
>
<i
className="fa-solid fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Certificate</span>
</button>
)}
<button
onClick={handleToPrint}
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
className="defaultBtn printBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="op-btn op-btn-neutral op-btn-sm mr-[5px] shadow"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
onClick={() => handleDownloadPdf()}
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
<span className="hidden lg:block">Download</span>
</button>
</div>
) : (
<div>
<div className="flex" data-tut="reactourFifth">
<button
onClick={() => {
navigate(-1);
}}
onClick={() => navigate(-1)}
type="button"
className="defaultBtn backBtn"
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
{currentSigner && (
<>
<button
style={{
background: "#de4337"
}}
className="finishBtn hoverCss"
onClick={() => {
handleDeclinePdfAlert();
}}
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
onClick={() => handleDeclinePdfAlert()}
>
Decline
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="finishBtn sendHover"
onClick={() => {
embedWidgetsData();
}}
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
onClick={() => embedWidgetsData()}
>
Finish
</button>
<div className="op-dropdown op-dropdown-end">
<div
tabIndex={0}
role="button"
className="op-btn op-btn-info op-btn-sm shadow"
>
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
</div>
<ul
tabIndex={0}
className="op-dropdown-content z-[1] op-menu op-menu-sm p-1 shadow bg-base-100 rounded-box mt-1"
>
<li
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<span className="font-semibold text-[12px]">
<i
className="fa fa-arrow-down"
aria-hidden="true"
></i>{" "}
Download
</span>
</li>
</ul>
</div>
</>
)}
</div>
)
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
<div style={{ display: "flex", flexDirection: "row" }}>
<CertificateComponent />
) : isCompleted ? (
<div className="flex flex-row">
{isCompleted && (
<button
type="button"
onClick={() =>
handleDownloadCertificate(pdfDetails, setIsDownloading)
}
className="op-btn op-btn-secondary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
>
<i className="fa-solid fa-award" aria-hidden="true"></i>
<span className="hidden lg:block">Certificate</span>
</button>
)}
<button
onClick={handleToPrint}
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
type="button"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center"
}}
className="defaultBtn printBtn"
className="op-btn op-btn-neutral op-btn-sm font-medium text-[13px] mr-[5px] shadow"
>
<i
className="fa fa-print"
style={{
fontSize: "15px",
marginRight: "3px",
color: "white"
}}
aria-hidden="true"
></i>
Print
<i className="fa fa-print" aria-hidden="true"></i>
<span className="hidden lg:block">Print</span>
</button>
<button
type="button"
className="defaultBtn downloadBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
onClick={() => handleDownloadPdf()}
className="op-btn op-btn-primary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i
className="fa fa-download"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Download
<i className="fa fa-download" aria-hidden="true"></i>
<span className="hidden lg:block">Download</span>
</button>
<button
type="button"
className="defaultBtn mailBtn"
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginLeft: "10px"
}}
className="op-btn op-btn-info op-btn-sm font-medium text-[13px] mr-[5px] shadow"
onClick={() => setIsEmail(true)}
>
<i
className="fa fa-envelope"
style={{
color: "white",
fontSize: "15px",
marginRight: "3px"
}}
aria-hidden="true"
></i>
Mail
<i className="fa fa-envelope" aria-hidden="true"></i>
<span className="hidden lg:block">Mail</span>
</button>
</div>
) : (
<div>
<div className="flex">
<button
onClick={() => {
navigate(-1);
}}
onClick={() => navigate(-1)}
type="button"
className="defaultBtn backBtn"
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
>
Back
</button>
<button
style={{
background: "#188ae2"
}}
type="button"
className="finishBtn sendHover"
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
@@ -748,6 +456,31 @@ function Header({
)}
</div>
)}
{isDownloading === "pdf" && (
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<ModalUi
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
}
handleClose={() => setIsDownloading("")}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
{isDownloading === "certificate"}{" "}
<p>
Your completion certificate is being generated. Please wait
momentarily.
</p>
<p>
If the download doesn&apos;t start shortly, click the button again.
</p>
</div>
</ModalUi>
</div>
);
}
@@ -4,6 +4,7 @@ import PlaceholderBorder from "./PlaceholderBorder";
import { Rnd } from "react-rnd";
import {
defaultWidthHeight,
handleCopyNextToWidget,
isMobile,
onChangeInput,
radioButtonWidget,
@@ -215,7 +216,8 @@ function Placeholder(props) {
"company",
"job title",
"date",
"email"
"email",
textWidget
].includes(props.pos.type);
if (widgetTypeExist) {
@@ -273,6 +275,7 @@ function Placeholder(props) {
}
};
const handleOnClickPlaceholder = () => {
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
if (!props.isNeedSign) {
props.setWidgetType(props.pos.type);
}
@@ -301,8 +304,14 @@ function Placeholder(props) {
props.setWidgetType(props.pos.type);
props.setCurrWidgetsDetails(props.pos);
};
//function ro set state value of onclick on widget's copy icon
//function to set required state value onclick on widget's copy icon
const handleCopyPlaceholder = (e) => {
e.stopPropagation();
//condition to handle text widget signer obj id and unique id
//when user click on copy icon of text widget in that condition text widget does not have any signerObjId
//in that case i have to save in tempSignerId as a unique id of previous select signer's unique id
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
@@ -311,9 +320,24 @@ function Placeholder(props) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
}
e.stopPropagation();
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
//checking widget's type and open widget copy modal for required widgets
if (
["signature", textWidget, "stamp", "initial"].includes(props.pos.type)
) {
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
} else {
//function to create new widget next to just widget
handleCopyNextToWidget(
props.pos,
props.pos.type,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
//function to save date and format on local array onchange date and onclick format
@@ -392,7 +416,7 @@ function Placeholder(props) {
{props.pos.type !== textWidget && !props.isSignYourself && (
<i
data-tut="reactourLinkUser"
data-tut="assignSigner"
className="fa-regular fa-user signUserIcon"
onClick={(e) => {
e.stopPropagation();
@@ -518,7 +542,15 @@ function Placeholder(props) {
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
key={props.pos.key}
lockAspectRatio={
props.pos.type !== textWidget &&
// props.pos.type !== textWidget
![
textWidget,
"email",
"name",
"company",
"job title",
textInputWidget
].includes(props.pos.type) &&
(props.pos.Width
? props.pos.Width / props.pos.Height
: defaultWidthHeight(props.pos.type).width /
@@ -594,7 +626,7 @@ function Placeholder(props) {
disableDragging={
props.isNeedSign
? true
: props.isPlaceholder && props.pos.type !== "date"
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
? false
: !isDraggingEnabled
}
@@ -652,8 +684,18 @@ function Placeholder(props) {
style={{
left: props.xPos(props.pos, props.isSignYourself),
top: props.yPos(props.pos, props.isSignYourself),
width: "auto", //props.posWidth(props.pos, props.isSignYourself),
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
// "auto", //props.posWidth(props.pos, props.isSignYourself),
// height: props.posHeight(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10"
}}
onTouchEnd={() => handleOnClickPlaceholder()}
@@ -4,7 +4,8 @@ import {
defaultWidthHeight,
isMobile,
radioButtonWidget,
resizeBorderExtraWidth
resizeBorderExtraWidth,
textWidget
} from "../../constant/Utils";
function PlaceholderBorder(props) {
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
@@ -31,7 +32,10 @@ function PlaceholderBorder(props) {
};
return (
<div
onMouseEnter={!isMobile && props?.setDraggingEnabled(true)}
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(true)}
onTouchEnd={() =>
props.pos.type === textWidget && props?.setDraggingEnabled(true)
}
className="borderResize"
style={{
borderColor: themeColor,
@@ -1,5 +1,4 @@
import React, { useState } from "react";
import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { randomId, textWidget } from "../../constant/Utils";
@@ -195,13 +194,13 @@ function PlaceholderCopy(props) {
props.setIsPageCopy(false);
}}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="h-full p-[20px] text-base-content">
{copyType.map((data, key) => {
return (
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
<div key={key} className="flex flex-col">
<label className="text-[16px] font-medium">
<input
style={{ accentColor: "red", marginRight: "10px" }}
className="mr-[8px] op-radio op-radio-xs"
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
@@ -214,33 +213,22 @@ function PlaceholderCopy(props) {
);
})}
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="flex bg-[#9f9f9f] w-full my-[15px]"></div>
<button
onClick={() => {
handleApplyCopy();
handleUniqueId();
}}
style={{ background: themeColor }}
type="button"
disabled={!selectCopyType}
className="finishBtn"
className="op-btn op-btn-primary"
>
Apply
</button>
<button
type="button"
className="finishBtn cancelBtn"
onClick={() => {
handleUniqueId();
}}
className="op-btn op-btn-ghost"
onClick={() => handleUniqueId()}
>
Cancel
</button>
@@ -2,6 +2,8 @@ import React, { useEffect, useState, forwardRef, useRef } from "react";
import {
getMonth,
getYear,
isMobile,
onChangeHeightOfTextArea,
onChangeInput,
radioButtonWidget,
range,
@@ -36,15 +38,17 @@ function PlaceholderType(props) {
"December"
];
const validateExpression = (regexValidation) => {
let regexObject = regexValidation;
if (props.pos?.options.validation.type === "regex") {
regexObject = RegexParser(regexValidation);
}
// new RegExp(regexValidation);
let isValidate = regexObject.test(textValue);
if (!isValidate) {
props?.setValidateAlert(true);
inputRef.current.focus();
if (textValue && regexValidation) {
let regexObject = regexValidation;
if (props.pos?.options?.validation?.type === "regex") {
regexObject = RegexParser(regexValidation);
}
// new RegExp(regexValidation);
let isValidate = regexObject.test(textValue);
if (!isValidate) {
props?.setValidateAlert(true);
inputRef.current.focus();
}
}
};
@@ -52,22 +56,18 @@ function PlaceholderType(props) {
props.setDraggingEnabled(true);
const validateType = props.pos?.options?.validation?.type;
let regexValidation;
if (validateType) {
if (validateType && validateType !== "text") {
switch (validateType) {
case "email":
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
validateExpression(regexValidation);
break;
case "number":
regexValidation = /^\d+$/;
validateExpression(regexValidation);
break;
case "text":
regexValidation = /^[a-zA-Z\s]+$/;
regexValidation = /^[0-9\s]*$/;
validateExpression(regexValidation);
break;
default:
regexValidation = props.pos?.options.validation.pattern;
regexValidation = props.pos?.options?.validation?.pattern || "";
validateExpression(regexValidation);
}
}
@@ -85,11 +85,11 @@ function PlaceholderType(props) {
case "number":
setValidatePlaceholder("12345");
break;
case textInputWidget:
setValidatePlaceholder("enter text");
case "text":
setValidatePlaceholder("please enter text");
break;
default:
setValidatePlaceholder("enter text");
setValidatePlaceholder("please enter value");
}
}
@@ -133,7 +133,7 @@ function PlaceholderType(props) {
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.pos]);
}, [props.pos?.options?.defaultValue]);
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div
@@ -284,11 +284,28 @@ function PlaceholderType(props) {
};
props.handleSaveDate(dateObj, isDateChange);
};
//handle height on enter press in text area
const handleEnterPress = (e) => {
const height = 18;
if (e.key === "Enter") {
//function to save height of text area
onChangeHeightOfTextArea(
height,
props.pos.type,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
switch (type) {
case "signature":
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
@@ -310,6 +327,7 @@ function PlaceholderType(props) {
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
@@ -334,12 +352,9 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
onBlur={handleInputBlur}
disabled={
props.isNeedSign &&
@@ -383,19 +398,12 @@ function PlaceholderType(props) {
case textInputWidget:
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input
className="inputPlaceholder"
<textarea
ref={inputRef}
placeholder={validatePlaceholder}
style={{ fontSize: calculateFontSize() }}
placeholder={validatePlaceholder || "please enter value"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
type="text"
tabIndex="0"
disabled={
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
? true
: props.isPlaceholder
}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
@@ -409,6 +417,13 @@ function PlaceholderType(props) {
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", color: "initial" }}
cols="50"
/>
) : (
<div
@@ -425,7 +440,7 @@ function PlaceholderType(props) {
<select
className="inputPlaceholder"
id="myDropdown"
style={{ fontSize: "12px" }}
style={{ fontSize: "12px", color: "initial" }}
value={selectOption}
onChange={(e) => {
setSelectOption(e.target.value);
@@ -442,7 +457,7 @@ function PlaceholderType(props) {
>
{/* Default/Title option */}
<option value="" disabled hidden>
{props.pos.options.name}
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values.map((data, ind) => {
@@ -465,6 +480,7 @@ function PlaceholderType(props) {
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
@@ -485,15 +501,12 @@ function PlaceholderType(props) {
case "name":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input
tabIndex="0"
<textarea
ref={inputRef}
placeholder={"name"}
style={{ fontSize: calculateFontSize() }}
className="inputPlaceholder"
type="text"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
const isDefault = false;
handleTextValid(e);
@@ -507,6 +520,13 @@ function PlaceholderType(props) {
isDefault
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", color: "initial" }}
cols="50"
/>
) : (
<div
@@ -521,14 +541,12 @@ function PlaceholderType(props) {
case "company":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input
className="inputPlaceholder"
type="text"
<textarea
ref={inputRef}
placeholder={"company"}
style={{ fontSize: calculateFontSize() }}
placeholder="Enter text"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
@@ -541,6 +559,13 @@ function PlaceholderType(props) {
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", color: "initial" }}
cols="50"
/>
) : (
<div
@@ -555,14 +580,12 @@ function PlaceholderType(props) {
case "job title":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input
className="inputPlaceholder"
type="text"
<textarea
ref={inputRef}
placeholder={"job title"}
style={{ fontSize: calculateFontSize() }}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
handleTextValid(e);
onChangeInput(
@@ -575,6 +598,13 @@ function PlaceholderType(props) {
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", color: "initial" }}
cols="50"
/>
) : (
<div
@@ -650,6 +680,7 @@ function PlaceholderType(props) {
return props.pos.SignUrl ? (
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
@@ -670,12 +701,11 @@ function PlaceholderType(props) {
case "email":
return props.isSignYourself ||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
<input
className="inputPlaceholder"
type="text"
<textarea
ref={inputRef}
placeholder={"email"}
style={{ fontSize: calculateFontSize() }}
placeholder={"enter email"}
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
@@ -690,6 +720,13 @@ function PlaceholderType(props) {
false
);
}}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{ whiteSpace: "pre-wrap", color: "initial" }}
cols="50"
/>
) : (
<div
@@ -708,12 +745,11 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
}}
className={`${ind === 0 ? "mt-0" : "mt-[5px]"} ${
props?.pos?.Width
? `w-[${props?.pos?.Width}px] h-[${props?.pos?.Width}px]`
: "w-[15px] h-[15px]"
} flex justify-center op-radio`}
type="radio"
disabled={
props.isNeedSign &&
@@ -738,9 +774,11 @@ function PlaceholderType(props) {
case textWidget:
return (
<textarea
placeholder="Enter label"
placeholder="Enter text"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
setTextValue(e.target.value);
onChangeInput(
@@ -753,8 +791,16 @@ function PlaceholderType(props) {
false
);
}}
className="labelTextArea"
style={{ whiteSpace: "pre-wrap" }}
className={
isMobile
? "labelTextArea labelWidthMobile"
: "labelTextArea labelWidthDesktop"
}
style={{
whiteSpace: "pre-wrap",
overflow: "hidden",
color: "initial"
}}
cols="50"
/>
);
@@ -763,6 +809,7 @@ function PlaceholderType(props) {
<div style={{ pointerEvents: "none" }}>
<img
alt="signimg"
draggable="false"
src={props.pos.SignUrl}
style={{
width: "99%",
+23 -39
View File
@@ -11,46 +11,30 @@ function PrevNext({ pageNumber, allPages, changePage }) {
}
return (
<div>
<div className="preBtn1">
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
border: "0px",
marginRight: "5px"
}}
disabled={pageNumber <= 1}
onClick={previousPage}
>
Prev
</button>
<span
style={{
fontSize: "13px",
fontWeight: "600"
}}
>
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
<div className="flex items-center">
<button
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<i className="fa fa-backward" aria-hidden="true"></i>
</span>
<button
style={{
padding: "3px 20px ",
fontSize: "10px",
background: "#d3edeb",
fontWeight: "600",
marginLeft: "5px",
border: "0px"
}}
disabled={pageNumber >= allPages}
onClick={nextPage}
>
Next
</button>
</div>
<span className="lg:block hidden">Prev</span>
</button>
<span className="text-xs text-base-content font-medium mx-2">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
</span>
<button
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<i className="fa fa-forward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden">Next</span>
</button>
</div>
);
}
+190 -132
View File
@@ -1,126 +1,137 @@
import React, { useRef, useState } from "react";
import { darkenColor, getFirstLetter } from "../../constant/Utils";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import {
color,
darkenColor,
getFirstLetter,
isMobile,
nameColor
} from "../../constant/Utils";
const cursor =
"cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC'),_pointer]";
const RecipientList = (props) => {
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const nameColor = [
"#304fbf",
"#7d5270",
"#5f825b",
"#578077",
"#576e80",
"#6d527d",
"#cc00cc",
"#006666",
"#cc00ff",
"#ff9900",
"#336699",
"#cc9900"
];
const [animationParent] = useAutoAnimate();
const [isHover, setIsHover] = useState();
const [isEdit, setIsEdit] = useState(false);
//function for onhover signer name change background color
const inputRef = useRef(null);
const onHoverStyle = (ind, blockColor) => {
const style = {
background: blockColor ? blockColor : color[ind % color.length],
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
//function for onhover signer name remove background color
const nonHoverStyle = () => {
const style = {
padding: "10px",
marginTop: "2px",
display: "flex",
flexDirection: "row",
borderBottom: "1px solid #e3e1e1",
alignItems: "center"
};
return style;
};
const isWidgetExist = (Id) => {
return props.signerPos.some((x) => x.Id === Id);
};
//handle drag start
const handleDragStart = (e, id) => {
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
// The first argument specifies the type of data being set, and the second argument is the actual data you want to transfer.
e.dataTransfer.setData("text/plain", id);
};
//handleDragOver prevents the default behavior of the dragover event, which is necessary for the drop event to be triggered.
const handleDragOver = (e) => {
e.preventDefault();
};
//handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list
const handleChangeSequence = (e, ind, isUp, isDown) => {
e.preventDefault();
let draggedItemId;
let index = ind;
//if isUp true then set item's id and index in mobile view
if (isUp) {
draggedItemId = index;
index = index - 1;
}
//if isDown true then set item's id and index in mobile view
else if (isDown) {
draggedItemId = index;
index = index + 1;
} //else set id on drag element in desktop viiew
else {
//`e.dataTransfer.getData('text/plain')` is used to get data that you have saved.
draggedItemId = e.dataTransfer.getData("text/plain");
}
//convert string to number
const intDragId = parseInt(draggedItemId);
//get that item to change position
const draggedItem = props.signersdata.filter((_, ind) => ind === intDragId);
const remainingItems = props.signersdata.filter(
(_, ind) => ind !== intDragId
);
//splice method is used to replace or add new value in array at specific index
remainingItems.splice(index, 0, ...draggedItem);
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
props.setSignersData(remainingItems);
props.setSignerObjId(remainingItems[index]?.objectId || "");
props.setIsSelectId(index);
props.setContractName(remainingItems[index]?.className || "");
props.setUniqueId(remainingItems[index]?.Id);
props.setRoleName(remainingItems[index]?.Role);
props.setBlockColor(remainingItems[index]?.blockColor);
};
return (
<>
{props.signersdata.length > 0 &&
props.signersdata.map((obj, ind) => {
return (
<div
ref={animationParent}
key={ind}
draggable={props.sendInOrder && !isMobile ? true : false}
onDragStart={(e) =>
props.sendInOrder && !isMobile && handleDragStart(e, ind)
}
onDragOver={(e) =>
props.sendInOrder && !isMobile && handleDragOver(e)
}
onDrop={(e) =>
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
}
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
key={ind}
style={
isHover === ind || props.isSelectListId === ind
? onHoverStyle(ind, obj.blockColor)
: nonHoverStyle(ind)
}
onClick={() => {
className={`${
props.sendInOrder
? props.isMailSend
? "pointer-events-none bg-opacity-80"
: `text-[12px] font-bold ${cursor}`
: props.isMailSend && "pointer-events-none bg-opacity-80"
} flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
style={{
background:
(!isMobile && isHover === ind) || props.isSelectListId === ind
? obj?.blockColor
? obj?.blockColor
: color[ind % color.length]
: "transparent"
}}
onClick={(e) => {
e.preventDefault();
props.setSignerObjId(obj?.objectId || "");
props.setIsSelectId(ind);
props.setContractName(obj?.className || "");
props.setUniqueId(obj.Id);
props.setRoleName(obj.Role);
props.setBlockColor(obj?.blockColor);
if (props.handleModal) {
props.handleModal();
}
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
width: "100%"
}}
>
<div className="flex flex-row items-center w-full">
<div
className="signerStyle"
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
style={{
background: obj.blockColor
? darkenColor(obj.blockColor, 0.4)
: nameColor[ind % nameColor.length],
width: 30,
height: 30,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "12px"
background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4)
: nameColor[ind % nameColor.length]
}}
>
<span
style={{
fontSize: "12px",
textAlign: "center",
fontWeight: "bold",
color: "white",
textTransform: "uppercase"
}}
>
<span className="text-white uppercase font-bold text-center text-[12px]">
{isWidgetExist(obj.Id) ? (
<i className="fa-solid fa-check"></i>
) : (
@@ -133,65 +144,112 @@ const RecipientList = (props) => {
</span>
</div>
<div
style={{
display: "flex",
flexDirection: obj.Name ? "column" : "row",
alignItems: "center"
}}
className={`${
obj?.Name ? "flex-col" : "flex-row"
} flex items-center`}
>
{obj.Name ? (
<span className="userName" style={{ cursor: "default" }}>
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj.Name}
</span>
) : (
<>
<span
className="userName"
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
style={{
backgroundColor: "transparent",
width: "inherit"
}}
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => {
setIsEdit({});
props.handleOnBlur(obj.Role, obj.Id);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
inputRef.current.blur();
}
}}
/>
) : (
obj.Role
)}
</span>
</>
<span
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
}}
>
{isEdit?.[obj.Id] && props.handleRoleChange ? (
<input
ref={inputRef}
className="bg-transparent p-[3px"
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
onBlur={() => {
setIsEdit({});
props.handleOnBlur(obj.Role, obj.Id);
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
e.preventDefault();
inputRef.current.blur();
}
}}
/>
) : (
<span className="p-[3px]">{obj.Role}</span>
)}
</span>
)}
{obj.Name && (
<span className="useEmail" style={{ cursor: "default" }}>
{obj.Role}
<span
className={` ${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
>
{obj?.Role || obj?.Email}
</span>
)}
</div>
</div>
{isMobile && props.sendInOrder && (
<div className="flex flex-row items-center gap-[5px]">
<div
onClick={(e) => {
if (ind !== 0) {
e.stopPropagation();
handleChangeSequence(e, ind, "up");
}
}}
className={ind === 0 ? "text-[gray]" : "text-black"}
>
</div>
<div
onClick={(e) => {
if (ind !== props.signersdata.length - 1) {
e.stopPropagation();
handleChangeSequence(e, ind, null, "down");
}
}}
className={
ind === props.signersdata.length - 1
? "text-[gray]"
: "text-black"
}
>
</div>
</div>
)}
{props.handleDeleteUser && (
<div
onClick={(e) => {
e.stopPropagation();
props.handleDeleteUser(obj.Id);
}}
style={{ cursor: "pointer" }}
className={`${
(!isMobile && isHover === ind) ||
props.isSelectListId === ind
? "text-[#424242]"
: "text-base-content"
} cursor-pointer ml-[5px]`}
>
<i className="fa-regular fa-trash-can"></i>
</div>
@@ -1,41 +1,58 @@
import React from "react";
import React, { useState } from "react";
import RSC from "react-scrollbars-custom";
import { Document, Page } from "react-pdf";
import { themeColor } from "../../constant/const";
function RenderAllPdfPage({
pdfUrl,
signPdfUrl,
allPages,
setAllPages,
setPageNumber,
setSignBtnPosition,
pageNumber
pageNumber,
signerPos,
signerObjectId
}) {
const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState("");
//set all number of pages after load pdf
function onDocumentLoad({ numPages }) {
setAllPages(numPages);
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
if (signerPos) {
const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
setBookmarkColor(checkUser[0]?.blockColor);
let pageNumberArr = [];
if (checkUser?.length > 0) {
checkUser[0]?.placeHolder?.map((data) => {
pageNumberArr.push(data?.pageNumber);
});
setSignPageNumber(pageNumberArr);
}
}
}
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
const addSignatureBookmark = (index) => {
const ispageNumber = signPageNumber.includes(index + 1);
return (
ispageNumber && (
<div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
<i
style={{ color: bookmarkColor || "red" }}
className="fa-solid fa-bookmark"
></i>
</div>
)
);
};
return (
<div className="showPages">
<div
style={{
display: "flex",
flexDirection: "row",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
backgroundColor: "white",
height: "100%"
}}
>
<div style={{ width: "140px" }}>
<div
style={{
background: themeColor
// color:"white"
}}
className="signedStyle"
>
<div>
<div className=" hidden md:flex flex-row bg-base-100 h-full">
<div className="w-[140px]">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Pages
</div>
@@ -50,26 +67,16 @@ function RenderAllPdfPage({
<Document
loading={"Loading Document.."}
onLoadSuccess={onDocumentLoad}
file={pdfUrl ? pdfUrl : signPdfUrl}
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
file={signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
key={index}
style={{
width: "100px",
border:
pageNumber - 1 === index
? "2px solid red"
: "2px solid #878787",
// padding: "5px",
margin: "10px",
display: "flex",
justifyContent: "center",
alignItems: "center",
contain: "content"
}}
className={`${
pageNumber - 1 === index
? "border-[red]"
: "border-[#878787]"
} border-2 w-[100px] m-[10px] flex justify-center items-center relative`}
onClick={() => {
setPageNumber(index + 1);
if (setSignBtnPosition) {
@@ -77,15 +84,19 @@ function RenderAllPdfPage({
}
}}
>
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={100}
height={100}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
{signerPos && addSignatureBookmark(index)}
<div className="relative z-[1] overflow-hidden">
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={100}
height={100}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
</div>
))}
</Document>
+37 -55
View File
@@ -204,34 +204,32 @@ function RenderPdf({
};
//function for render placeholder block over pdf document
const checkSignedSignes = (data) => {
const checkSign = signedSigners.filter(
(sign) => sign.objectId === data.signerObjId
let checkSign = [];
//condition to handle quick send flow and using normal request sign flow
checkSign = signedSigners.filter(
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
);
if (data.signerObjId === signerObjectId) {
setCurrentSigner(true);
}
const handleAllUserName = (Id, Role, type) => {
return pdfDetails[0].Signers.map((signerData, key) => {
return (
signerData.objectId === data.signerObjId && (
<React.Fragment key={key}>
{signerData?.Name && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{signerData.Name}
</div>
)}
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
{Role && (
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
)}
</React.Fragment>
)
);
});
return (
<React.Fragment>
<div className="text-black text-[8px] font-medium">
{
pdfDetails[0].Signers?.find(
(signer) => signer.objectId === data.signerObjId
)?.Name
}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{Role && (
<div className="text-black text-[8px] font-medium">
{`(${Role})`}
</div>
)}
</React.Fragment>
);
};
return (
@@ -272,6 +270,7 @@ function RenderPdf({
unSignedWidgetId={unSignedWidgetId}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
</React.Fragment>
)
@@ -289,38 +288,30 @@ function RenderPdf({
if (checkSign?.Name) {
return (
<>
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
<div className="text-black text-[8px] font-medium">
{checkSign?.Name}
</div>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
{Role && (
<div className="text-black text-[8px] font-medium">
{`(${Role})`}
</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{`(${Role})`}
</div>
</>
);
} else {
return (
<>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
</>
);
}
} else {
return (
<>
{type && (
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
)}
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
{Role}
</div>
{type && <div className="text-[11px] font-bold">{type}</div>}
<div className="text-black text-[8px] font-medium">{Role}</div>
</>
);
}
@@ -331,6 +322,7 @@ function RenderPdf({
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{isMobile && scale ? (
<div
data-tut="reactourForth"
style={{
border: "0.1px solid #ebe8e8",
marginTop: isGuestSigner && "30px"
@@ -458,17 +450,9 @@ function RenderPdf({
}))}
{/* this component for render pdf document is in middle of the component */}
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<div className="flex items-center justify-center">
<Document
onLoadError={() => {
setPdfLoadFail(true);
}}
onLoadError={() => setPdfLoadFail(true)}
loading={"Loading Document.."}
onLoadSuccess={pageDetails}
// ref={pdfRef}'
@@ -514,10 +498,8 @@ function RenderPdf({
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
>
<div
style={{
border: "0.1px solid #ebe8e8",
width: pdfOriginalWidth
}}
data-tut="reactourForth"
style={{ border: "0.1px solid #ebe8e8", width: pdfOriginalWidth }}
ref={drop}
id="container"
>
+147 -218
View File
@@ -1,6 +1,5 @@
import React, { useRef, useState, useEffect } from "react";
import SignatureCanvas from "react-signature-canvas";
import { themeColor } from "../../constant/const";
function SignPad({
isSignPad,
@@ -20,7 +19,9 @@ function SignPad({
isInitial,
setIsInitial,
setIsStamp,
widgetType
widgetType,
currWidgetsDetails,
setCurrWidgetsDetails
}) {
const [penColor, setPenColor] = useState("blue");
const allColor = ["blue", "red", "black"];
@@ -31,6 +32,7 @@ function SignPad({
const [signValue, setSignValue] = useState("");
const [textWidth, setTextWidth] = useState(null);
const [textHeight, setTextHeight] = useState(null);
const [signatureType, setSignatureType] = useState("draw");
const fontOptions = [
{ value: "Fasthand" },
{ value: "Dancing Script" },
@@ -48,19 +50,11 @@ function SignPad({
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
const currentUserName = jsonSender && jsonSender.name;
useEffect(() => {
const trimmedName = currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName);
setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function for clear signature image
const handleClear = () => {
setCurrWidgetsDetails({});
if (isTab === "draw") {
if (canvasRef.current) {
canvasRef.current.clear();
@@ -72,11 +66,10 @@ function SignPad({
} else if (isTab === "uploadImage") {
setImage("");
}
setIsInitial(false);
// setIsInitial(false);
};
//function for set signature url
const handleSignatureChange = () => {
// canvasRef.current.backgroundColor = 'rgb(165, 26, 26)'
setSignature(canvasRef.current.toDataURL());
setIsSignImg(canvasRef.current.toDataURL());
};
@@ -84,45 +77,40 @@ function SignPad({
//save button component
const SaveBtn = () => {
return (
<div style={{ marginTop: "2px" }}>
<div>
{(isTab === "draw" || isTab === "uploadImage") && (
<button
style={{
color: "black",
border: "1px solid #ccc"
}}
type="button"
className="finishBtn saveBtn"
className="op-btn op-btn-ghost mr-1 mt-[2px]"
onClick={() => handleClear()}
>
Clear
</button>
)}
<button
onClick={() => {
setCurrWidgetsDetails({});
if (!image) {
if (isTab === "mysignature") {
setIsSignImg("");
if (isInitial) {
onSaveSign("initials");
onSaveSign(signatureType, "initials");
} else {
onSaveSign("default");
onSaveSign(null, "default");
}
} else {
if (isTab === "type") {
setIsSignImg("");
onSaveSign(false, textWidth, textHeight);
onSaveSign(null, false, textWidth, textHeight);
} else {
setIsSignImg("");
onSaveSign();
onSaveSign(signatureType);
}
}
setPenColor("blue");
} else {
setIsSignImg("");
onSaveImage();
onSaveImage(signatureType);
}
setIsSignPad(false);
setIsInitial(false);
@@ -133,26 +121,47 @@ function SignPad({
setSignValue("");
setIsStamp(false);
}}
style={{
background: themeColor,
color: "white"
}}
disabled={
isSignImg || image || isDefaultSign || textWidth ? false : true
}
type="button"
className={
className={`${
isSignImg || image || isDefaultSign || textWidth
? "finishBtn saveBtn"
: "disabledFinish saveBtn"
}
? ""
: "pointer-events-none"
} op-btn op-btn-primary shadow-lg`}
>
Save
</button>
</div>
);
};
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
useEffect(() => {
if (currWidgetsDetails && canvasRef.current) {
const isWidgetType = currWidgetsDetails?.type;
const signatureType = currWidgetsDetails?.signatureType;
const url = currWidgetsDetails?.SignUrl;
//checking widget type and draw type signature url
if (isInitial) {
if (isWidgetType === "initials" && signatureType === "draw" && url) {
canvasRef.current.fromDataURL(url);
}
} else if (
isWidgetType === "signature" &&
signatureType === "draw" &&
url
) {
canvasRef.current.fromDataURL(url);
}
}
const trimmedName = currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName);
setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignPad]);
useEffect(() => {
const loadFont = async () => {
try {
@@ -186,7 +195,7 @@ function SignPad({
}, [isTab]);
//function for convert input text value in image
const convertToImg = async (fontStyle, text) => {
const convertToImg = async (fontStyle, text, color) => {
//get text content to convert in image
const textContent = text;
const fontfamily = fontStyle
@@ -199,6 +208,7 @@ function SignPad({
const span = document.createElement("span");
span.textContent = textContent;
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
span.style.color = color ? color : penColor;
span.style.display = "hidden";
document.body.appendChild(span); // Replace 'container' with the ID of the container element
@@ -216,13 +226,9 @@ function SignPad({
canvasElement.width = width * pixelRatio;
canvasElement.height = height * pixelRatio;
// Render the content of the span onto the canvas
// ctx.fillStyle = "white"; // Set the background color of the canvas
// ctx.fillRect(0, 0, width*pixelRatio, height*pixelRatio);
// You can customize text styles if needed
ctx.font = font;
ctx.fillStyle = "black"; // Set the text color
ctx.fillStyle = color ? color : penColor; // Set the text color
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.scale(pixelRatio, pixelRatio);
@@ -235,36 +241,56 @@ function SignPad({
setSignature(dataUrl);
};
const PenColorComponent = (props) => {
return (
<div className="flex flex-row items-start">
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
props?.convertToImg &&
props?.convertToImg(fontSelect, signValue, data);
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
);
};
return (
<div>
{isSignPad && (
<div className="modaloverlay">
<div className="modalcontainer">
<div
className="modalheader"
style={{ padding: "0 13px", marginTop: "5px" }}
>
<div className="modaltitle">
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
background: "white",
marginTop: "3px"
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "end",
gap: 10
}}
>
<div className="op-modal op-modal-open">
<div className="op-modal-box px-[13px] pt-2 pb-0">
<div className="flex justify-between text-base-content items-center">
<div className="text-[1.2rem]">
<div className="flex flex-row justify-between mt-[3px]">
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
{isStamp ? (
<span style={{ color: themeColor }} className="signTab">
<span className="op-link-primary op-link">
{widgetType === "image"
? "Upload image"
: "Upload stamp image"}
@@ -279,23 +305,14 @@ function SignPad({
setIsTab("draw");
setImage();
}}
style={{
color: isTab === "draw" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Draw
</span>
<div
style={{
border:
isTab === "draw"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
@@ -303,23 +320,16 @@ function SignPad({
setIsDefaultSign(false);
setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
style={{
color:
isTab === "uploadImage" ? themeColor : "#515252"
}}
className="signTab"
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Upload Image
</span>
<div
style={{
border:
isTab === "uploadImage"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
<div>
<span
@@ -327,25 +337,17 @@ function SignPad({
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
setImage();
}}
style={{
color: isTab === "type" ? themeColor : "#515252",
marginLeft: "2px"
}}
className="signTab"
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Type
</span>
<div
style={{
border:
isTab === "type"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
{!isInitial && defaultSign ? (
<div>
@@ -354,26 +356,17 @@ function SignPad({
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Signature
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
) : (
isInitial &&
@@ -384,26 +377,17 @@ function SignPad({
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
style={{
color:
isTab === "mysignature"
? themeColor
: "#515252"
}}
className="signTab"
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Initials
</span>
<div
style={{
border:
isTab === "mysignature"
? "1.5px solid #108783"
: "1.5px solid #ffffff"
}}
></div>
</div>
)
)}
@@ -413,8 +397,7 @@ function SignPad({
</div>
</div>
<div
className="closebtn"
style={{ color: "black" }}
className="text-[1.5rem] cursor-pointer"
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
@@ -423,6 +406,7 @@ function SignPad({
setIsDefaultSign(false);
setImage();
setIsTab("draw");
setSignatureType("draw");
setSignValue("");
setIsStamp(false);
}}
@@ -430,10 +414,10 @@ function SignPad({
&times;
</div>
</div>
<div style={{ height: "100%", padding: 20 }}>
<div className="p-[20px] h-full">
{isDefaultSign ? (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
@@ -451,28 +435,23 @@ function SignPad({
>
<img
alt="stamp img"
style={{
width: "100%",
height: "100%",
background: "rgb(255, 255, 255)",
objectFit: "contain"
}}
className="w-full h-full object-contain bg-white"
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div style={{ display: "flex", justifyContent: "center" }}>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
@@ -489,7 +468,7 @@ function SignPad({
type="file"
onChange={onImageChange}
className="filetype"
accept="image/*"
accept="image/png,image/jpeg"
ref={imageRef}
hidden
/>
@@ -499,7 +478,7 @@ function SignPad({
</div>
) : (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
@@ -517,39 +496,27 @@ function SignPad({
alt="print img"
ref={imageRef}
src={image.src}
style={{
objectFit: "contain",
height: "100%",
width: "100%"
}}
draggable="false"
className=" object-contain h-full w-full"
/>
</div>
</div>
<div
style={{ display: "flex", justifyContent: "flex-end" }}
>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}}
>
<div className="flex justify-between items-center">
<span className="signatureText">
{isInitial ? "Initials" : "Signature"}:
</span>
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect }}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="signatureInput"
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
placeholder="Your signature"
value={signValue}
onChange={(e) => {
@@ -558,7 +525,6 @@ function SignPad({
}}
/>
</div>
<div className="fontOptionContainer">
{fontOptions.map((font, ind) => {
return (
@@ -578,7 +544,8 @@ function SignPad({
<div
style={{
padding: "5px 10px 5px 10px",
fontSize: "20px"
fontSize: "20px",
color: penColor
}}
>
{signValue ? signValue : "Your signature"}
@@ -588,13 +555,14 @@ function SignPad({
})}
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
) : (
<>
<div style={{ display: "flex", justifyContent: "center" }}>
<div className="flex justify-center">
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
@@ -614,47 +582,8 @@ function SignPad({
dotSize={1}
/>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent />
<SaveBtn />
</div>
</>
+7 -40
View File
@@ -1,5 +1,4 @@
import React from "react";
import { themeColor } from "../../constant/const";
import "../../styles/signature.css";
function Signedby({ pdfDetails }) {
const getFirstLetter = (name) => {
@@ -8,50 +7,18 @@ function Signedby({ pdfDetails }) {
};
return (
<div className="signerComponent">
<div
style={{
background: themeColor,
color: "white"
}}
className="signedStyle"
>
<div className="hidden md:block w-[180px] h-full bg-base-100">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signed By
</div>
<div style={{ marginTop: "2px", background: "white" }}>
<div
style={{
display: "flex",
flexDirection: "row",
padding: "10px",
background: "#93a3db"
}}
>
<div
className="signerStyle"
style={{
background: "#abd1d0",
width: 20,
height: 20,
display: "flex",
borderRadius: 30 / 2,
justifyContent: "center",
alignItems: "center",
marginRight: "20px",
marginTop: "5px"
}}
>
<span
style={{
fontSize: "8px",
textAlign: "center",
fontWeight: "bold"
}}
>
<div className="mt-[2px] bg-base-100">
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
<div className="bg-[#576081] flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1">
<span className="text-[12px] text-center font-bold text-white uppercase">
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
</span>
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<div className="flex flex-col">
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
</div>
@@ -1,33 +1,84 @@
import React from "react";
import { themeColor } from "../../constant/const";
import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip";
function SignerListPlace(props) {
return (
<div>
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>
{props.title ? props.title : "Recipients"}
<span className="text-xs mt-1 ml-0.5">
{props?.title === "Roles" && (
<>
<a data-tooltip-id="my-tooltip">
<sup>
<i className="fa-solid fa-question rounded-full border-[1.5px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
</sup>
</a>
<Tooltip id="my-tooltip" className="z-[100]">
<div className="max-w-[450px]">
<p className="font-bold">What are template roles?</p>
<p>
Begin by specifying each role needed for the completion of
the document. Think about the parties involved in the
signing process and what their responsibilities are.
Common roles include HR for internal documents, Customer
for agreements or Vendor for business agreements.{" "}
</p>
<p className="font-bold">
Why pre-attach users to some roles?
</p>
<p>
For roles that consistently involve the same individual
(e.g., the CEO&apos;s signature on employee offer
letters), you can pre-attach a user to a role within the
template. This step is optional but recommended for
efficiency and consistency across documents.
</p>
<p className="font-bold">
When do i specify the user attached to each role?
</p>
<p>
When you create a document from your template, you&apos;ll
be prompted to attach users to each defined role. If a
role already has a user attached, this will be pre-filled,
but you can modify it as needed before sending out the
document.
</p>
</div>
</Tooltip>
</>
)}
</span>
</span>
</div>
<div className="signerList">
<div className="overflow-auto hide-scrollbar max-h-[180px]">
<RecipientList {...props} />
</div>
{props.handleAddSigner && (
<div
data-tut="reactourAddbtn"
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => props.handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
)}
<div className="mx-1">
{props.handleAddSigner ? (
<div
role="button"
data-tut="reactourAddbtn"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false}
onClick={() => props.handleAddSigner()}
>
<i className="fa-solid fa-plus"></i> Add role
</div>
) : (
<div
role="button"
data-tut="reactourAddbtn"
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
disabled={props?.isMailSend ? true : false}
onClick={() => props.setIsAddSigner(true)}
>
<i className="fa-solid fa-plus"></i> Add recipients
</div>
)}
</div>
</div>
);
}
@@ -0,0 +1,67 @@
import React from "react";
import Loader from "../../primitives/Loader";
function VerifyEmail(props) {
return (
<dialog className="op-modal op-modal-open absolute z-[1999]">
<div className="md:w-[40%] w-[80%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="font-bold text-lg pt-[15px] px-[20px] text-base-content">
OTP verification
</h3>
{props.isVerifyModal ? (
<form
onSubmit={(e) => {
props.setIsVerifyModal(false);
props.handleVerifyEmail(e);
}}
>
<div className="px-6 py-3 text-base-content">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter OTP received over email"
value={props.otp}
onChange={(e) => props.setOtp(e.target.value)}
/>
</div>
<div className="px-6 my-3">
<button type="submit" className="op-btn op-btn-primary">
Verify
</button>
<button
className="op-btn op-btn-secondary ml-2"
onClick={(e) => props.handleResend(e)}
>
Resend
</button>
</div>
</form>
) : props.otpLoader ? (
<div className="h-[150px] flex items-center justify-center">
<Loader />
</div>
) : (
<div className="px-6 py-3 text-base-content">
<p className="mb-2 text-sm">Please verify your email !</p>
<div className="px-0 mt-3">
<button
className="op-btn op-btn-primary"
type="submit"
onClick={() => {
props.handleVerifyBtn();
}}
>
Send OTP
</button>
</div>
</div>
)}
</div>
</dialog>
);
}
export default VerifyEmail;
@@ -9,7 +9,6 @@ import {
textWidget,
widgets
} from "../../constant/Utils";
import { themeColor } from "../../constant/const";
function WidgetComponent({
dragSignature,
signRef,
@@ -34,8 +33,12 @@ function WidgetComponent({
handleRoleChange,
handleOnBlur,
title,
isTemplateFlow
setSignersData,
sendInOrder,
isTemplateFlow,
setBlockColor,
blockColor,
setIsAddSigner
}) {
const [isSignersModal, setIsSignersModal] = useState(false);
@@ -233,7 +236,7 @@ function WidgetComponent({
(data) =>
data.type !== "dropdown" &&
data.type !== radioButtonWidget &&
data.type !== textWidget
data.type !== textInputWidget
);
const textWidgetData = widget.filter((data) => data.type !== textWidget);
const updateWidgets = isSignYourself
@@ -252,103 +255,82 @@ function WidgetComponent({
className="stickyfooter"
style={{ width: window.innerWidth + "px" }}
>
{isSigners && (
<div
data-tut={dataTut}
style={{
background: isSelectListId
? color[isSelectListId % color.length]
: color[0],
padding: "10px 20px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
onClick={() => {
// if (signersdata?.length) {
handleModal();
// }
}}
>
<span
<div className="mx-1">
{isSigners && (
<div
data-tut={dataTut}
className="py-[10px] flex justify-center items-center op-card"
style={{
fontSize: "13px",
fontWeight: "700",
textAlign: "center"
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0]
}}
onClick={() => handleModal()}
>
{title ? title : "Recipient"}
</span>
<span
style={{
fontSize: "13px",
fontWeight: "700",
display: "flex",
alignItems: "center"
}}
>
{signersdata[isSelectListId]?.Role && (
<div>
{signersdata[isSelectListId]?.Name ? (
<>
:{" "}
{signersdata[isSelectListId]?.Name?.length > 12
? `${signersdata[isSelectListId].Name.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Name}
</>
) : (
<>
:{" "}
{signersdata[isSelectListId]?.Role?.length > 12
? `${signersdata[isSelectListId].Role.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Role}
</>
)}
<span className="text-[13px] font-bold flex items-center">
{title ? title : "Recipient"}
{signersdata[isSelectListId]?.Role && (
<div>
{signersdata[isSelectListId]?.Name ? (
<>
:{" "}
{signersdata[isSelectListId]?.Name?.length > 12
? `${signersdata[isSelectListId].Name.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Name}
</>
) : (
<>
:{" "}
{signersdata[isSelectListId]?.Role?.length > 12
? `${signersdata[isSelectListId].Role.slice(
0,
12
)}...`
: signersdata[isSelectListId]?.Role}
</>
)}
</div>
)}
<div className="ml-[6px] text-[16px]">
<i className="fa-solid fa-angle-down"></i>
</div>
)}
<div style={{ marginLeft: 6, fontSize: 16 }}>
<i className="fa-solid fa-angle-down"></i>
</span>
</div>
)}
</div>
<div className="mx-1">
{handleAddSigner ? (
<div
data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span>Add role</span>
</div>
) : (
setIsAddSigner && (
<div
data-tut="reactourAddbtn"
className="op-btn op-btn-accent w-full my-[2px]"
onClick={() => setIsAddSigner(true)}
>
<i className="fa-solid fa-plus"></i>
<span>Add recipients</span>
</div>
</span>
</div>
)}
{handleAddSigner && (
<div
data-tut="reactourAddbtn"
style={{
margin: "5px 0 5px 0",
backgroundColor: themeColor,
color: "white"
}}
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
onClick={() => handleAddSigner()}
>
<i className="fa-solid fa-plus"></i>
<span style={{ marginLeft: 2 }}>Add role</span>
</div>
)}
)
)}
</div>
<div
ref={scrollContainerRef}
style={{
background: "white",
borderTop: `2px solid ${themeColor}`
}}
className="bg-base-100 border-[2px] border-t-primary"
>
<div
style={{
display: "flex",
overflowX: "scroll",
whiteSpace: "nowrap",
padding: "10px"
}}
>
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
@@ -363,17 +345,16 @@ function WidgetComponent({
</div>
)
) : (
<div data-tut={dataTut} className="signerComponent">
<div
style={{
background: themeColor,
padding: "5px"
}}
>
<span className="signedStyle">Fields</span>
<div
data-tut={dataTut}
className={`${
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
} hidden md:block w-[180px] h-full bg-base-100`}
>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>Fields</span>
</div>
<div className="signLayoutContainer1">
<div className="flex flex-col items-center my-3">
<WidgetList
updateWidgets={updateWidgets}
handleDivClick={handleDivClick}
@@ -385,35 +366,33 @@ function WidgetComponent({
)}
{isSignersModal && (
<ModalUi
title={"Roles"}
title={title ? title : "Recipients"}
isOpen={isSignersModal}
handleClose={handleModal}
>
{signersdata.length > 0 ? (
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
handleModal={handleModal}
/>
<div className="max-h-[600px] overflow-auto pb-1">
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
handleModal={handleModal}
sendInOrder={sendInOrder}
setSignersData={setSignersData}
setBlockColor={setBlockColor}
/>
</div>
) : (
<div
style={{
padding: 20,
fontSize: 15,
fontWeight: "500",
textAlign: "center"
}}
>
Please add a role
<div className=" p-[20px] text-[15px] font-medium text-center">
Please add a {title ? title : "recipients"}
</div>
)}
</ModalUi>
@@ -4,9 +4,9 @@ import { getWidgetType } from "../../constant/Utils";
function WidgetList(props) {
return props.updateWidgets.map((item, ind) => {
return (
<div key={ind} style={{ marginBottom: "10px" }}>
<div key={ind} className="mb-[5px]">
<div
className="widgets"
className="select-none"
onClick={() => {
props.addPositionOfSignature &&
props.addPositionOfSignature("onclick", item);
@@ -3,6 +3,10 @@ import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css";
import RegexParser from "regex-parser";
import { textInputWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip";
const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({
@@ -10,11 +14,11 @@ const WidgetNameModal = (props) => {
defaultValue: "",
status: "required",
hint: "",
textvalidate: "text"
textvalidate: ""
});
const [isValid, setIsValid] = useState(true);
const statusArr = ["Required", "Optional"];
const inputOpt = ["email", "number"];
const inputOpt = ["text", "email", "number"];
useEffect(() => {
if (props.defaultdata) {
@@ -45,12 +49,16 @@ const WidgetNameModal = (props) => {
defaultValue: "",
status: "required",
hint: "",
textvalidate: "text"
textvalidate: ""
});
}
};
const handleChange = (e) => {
setFormdata({ ...formdata, [e.target.name]: e.target.value });
if (e) {
setFormdata({ ...formdata, [e.target.name]: e.target.value });
} else {
setFormdata({ ...formdata, textvalidate: "" });
}
};
const handledefaultChange = (e) => {
@@ -70,7 +78,7 @@ const WidgetNameModal = (props) => {
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
case "number":
return "/^\\d+$/";
case textInputWidget:
case "text":
return "/^[a-zA-Zs]+$/";
default:
return type;
@@ -79,7 +87,7 @@ const WidgetNameModal = (props) => {
function handleBlurRegex() {
if (!formdata.textvalidate) {
setFormdata({ ...formdata, textvalidate: "text" });
setFormdata({ ...formdata, textvalidate: "" });
} else {
if (formdata.defaultValue) {
const regexObject = RegexParser(
@@ -98,14 +106,31 @@ const WidgetNameModal = (props) => {
handleClose={props.handleClose && props.handleClose}
title={"Widget info"}
>
<form onSubmit={handleSubmit} style={{ padding: 20 }}>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) &&
!isEnableSubscription && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<form
onSubmit={handleSubmit}
className={`${
props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget
? "pt-0"
: ""
} p-[20px] text-base-content`}
>
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[red]"> *</span>
</label>
<input
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
@@ -115,56 +140,55 @@ const WidgetNameModal = (props) => {
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) && (
<>
<div className="form-section">
<label htmlFor="textvalidate" style={{ fontSize: 13 }}>
Validation
</label>
<div className="mb-[0.75rem]">
<div className="flex items-center gap-1">
<label
htmlFor="textvalidate"
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} text-[13px]`}
>
Validation
</label>
<Tooltip
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
/>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
</div>
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
className={`${
!props.isSubscribe && isEnableSubscription
? "bg-opacity-50 pointer-events-none"
: ""
} flex flex-row gap-[10px] mb-[0.5rem]`}
>
<div style={{ width: "100%", position: "relative" }}>
<div className="w-full relative group">
<input
style={{
padding: "0.5rem 0.75rem",
width: "92%",
borderWidth: "1px",
borderColor: "#d1d5db",
borderTopLeftRadius: "0.375rem",
borderBottomLeftRadius: "0.375rem",
outline: "none",
fontSize: "0.75rem",
position: "relative",
zIndex: 2
}}
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
name="textvalidate"
placeholder="Enter custom expression"
placeholder="Enter custom regular expression"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
// onBlur={() => handleBlurRegex()}
/>
<select
style={{ position: "absolute", left: 0, zIndex: 1 }}
className="addUserInput"
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
name="textvalidate"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
onBlur={() => handleBlurRegex()}
>
<option disabled style={{ fontSize: "13px" }}>
<option
disabled={formdata?.textvalidate}
className="text-[13px]"
>
Select...
</option>
{inputOpt.map((data, ind) => {
return (
<option
style={{ fontSize: "13px" }}
key={ind}
value={data}
>
<option className="text-[13px]" key={ind} value={data}>
{data}
</option>
);
@@ -173,60 +197,46 @@ const WidgetNameModal = (props) => {
</div>
</div>
</div>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
Default value
</label>
<input
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="defaultValue"
value={formdata.defaultValue}
onChange={(e) => handledefaultChange(e)}
onBlur={() =>
isValid === false &&
setFormdata({ ...formdata, defaultValue: "" })
}
autoComplete="off"
onBlur={() => {
if (isValid === false) {
setFormdata({ ...formdata, defaultValue: "" });
setIsValid(true);
}
}}
/>
{isValid === false ? (
<p style={{ color: "Red", fontSize: 8 }}>
invalid default value
</p>
) : (
<p
style={{
color: "transparent",
fontSize: 10,
margin: "3px 8px"
}}
{isValid === false && (
<div
className="warning defaultvalueWarning"
style={{ fontSize: 12 }}
>
.
</p>
<i
className="fas fa-exclamation-circle text-[15px]"
style={{ color: "#fab005" }}
></i>
invalid default value
</div>
)}
</div>
</>
)}
<div className="form-section">
<div
style={{
display: "flex",
flexDirection: "row",
gap: 10,
marginBottom: "0.5rem"
}}
>
<div className="mb-[0.75rem]">
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div
key={ind}
style={{
display: "flex",
flexDirection: "row",
gap: 5,
alignItems: "center"
}}
>
<div key={ind} className="flex flex-row gap-[5px] items-center">
<input
style={{ accentColor: "red", marginRight: "10px" }}
className="mr-[2px] op-radio op-radio-xs"
type="radio"
name="status"
onChange={() =>
@@ -236,9 +246,7 @@ const WidgetNameModal = (props) => {
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div style={{ fontSize: "13px", fontWeight: "500" }}>
{data}
</div>
<div className="text-[13px] font-medium">{data}</div>
</div>
);
})}
@@ -246,35 +254,20 @@ const WidgetNameModal = (props) => {
</div>
{(props.defaultdata?.type === textInputWidget ||
props?.widgetName === textInputWidget) && (
<div className="form-section">
<label htmlFor="hint" style={{ fontSize: 13 }}>
<div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]">
Hint
</label>
<input
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="hint"
value={formdata.hint}
onChange={(e) => handleChange(e)}
/>
</div>
)}
<div
style={{
height: 1,
backgroundColor: "#b7b3b3",
width: "100%",
marginBottom: "16px"
}}
></div>
<button
style={{
color: "white",
padding: "5px 20px",
backgroundColor: "#32a3ac"
}}
type="submit"
className="finishBtn"
>
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary">
Save
</button>
</form>
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../../primitives/Alert";
import Loader from "../../../primitives/Loader";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
@@ -25,9 +26,11 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
if (parentFolderId) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
const res = await FolderQuery.find();
@@ -49,6 +52,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const exsitQuery = new Parse.Query(folderCls);
exsitQuery.equalTo("Name", name);
exsitQuery.equalTo("Type", "Folder");
exsitQuery.notEqualTo("IsArchive", true);
if (parentFolderId) {
exsitQuery.equalTo("Folder", folderPtr);
}
@@ -110,11 +114,8 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
<div id="createFolder" className="relative">
{isLoader && (
<div className="absolute h-full w-full flex justify-center items-center ">
<div
style={{ fontSize: "45px", color: "#3dd3e0" }}
className="loader-37"
></div>
<div className="absolute h-full w-full flex justify-center items-center">
<Loader />
</div>
)}
<h1 className="text-base font-semibold">Create Folder</h1>
@@ -0,0 +1,236 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
const FolderModal = (props) => {
const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isAdd, setIsAdd] = useState(false);
// below useEffect is called when user open popup
useEffect(() => {
if (props.isOpenModal) {
fetchFolder();
}
// eslint-disable-next-line
}, [props.isOpenModal]);
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
const fetchFolder = async (folderPtr) => {
setIsLoader(true);
try {
const FolderQuery = new Parse.Query(props.folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
setIsLoader(false);
}
setIsLoader(false);
}
} catch (error) {
setIsLoader(false);
}
};
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
const handleSelect = (item) => {
setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
if (tabList.length > 0) {
const tab = tabList.some((x) => x.objectId === item.objectId);
if (!tab) {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
} else {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
};
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => {
let url = "OpenSign™ Drive";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
if (props.onSuccess) {
props.onSuccess(clickFolder);
}
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
};
// `handleCancel` is used to clear list of folder, close popup and folderUrl
const handleCancel = () => {
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
};
// `handleCancel` is call when user click on folder name from path/tab in popup
const removeTabListItem = async (e, i) => {
e.preventDefault();
// setEditable(false);
if (!isAdd) {
setIsLoader(true);
let folderPtr;
if (i) {
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
let _len = list.length - 1;
folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: list[_len].objectId
};
setTabList(list);
} else {
setClickFolder({});
setFolderList([]);
setTabList([]);
}
fetchFolder(folderPtr);
}
};
// `handleCreate` is used to open folder creation form in popup
const handleCreate = () => {
setIsAdd(!isAdd);
};
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
const handleAddFolder = (newFolder) => {
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
if (clickFolder && clickFolder.ObjectId) {
fetchFolder({
__type: "Pointer",
className: props.folderCls,
objectId: clickFolder.ObjectId
});
} else {
fetchFolder();
}
handleCreate();
};
return (
<div className="text-xs mt-2 ">
<ModalUi
title={"Select Folder"}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<span
className="cursor-pointer"
title={tab.Name}
onClick={(e) => removeTabListItem(e, i)}
>
{tab.Name}
</span>
{" / "}
</React.Fragment>
))}
<hr />
</div>
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div>
))}
</div>
{isAdd && (
<CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId}
folderCls={props.folderCls}
onSuccess={handleAddFolder}
/>
)}
{isLoader && (
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
</div>
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
</div>
);
};
export default FolderModal;
@@ -45,9 +45,11 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
FolderQuery.notEqualTo("IsArchive", true);
}
const res = await FolderQuery.find();
@@ -93,7 +95,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => {
let url = "Root";
let url = "OpenSign™ Drive";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
@@ -169,32 +171,42 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
{required && <span className="text-red-500 text-[13px]">*</span>}
</label>
</div>
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
<div>
<i
className="far fa-folder-open text-[40px] text-[#33bbff]"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name ? selectFolder.Name : "Root"}
</p>
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
<i
className="fa fa-pencil"
title="Select Folder"
aria-hidden="true"
></i>
</div>
<div className="relative max-w-sm">
<div
onClick={() => SetIsOpen(true)}
className=" cursor-pointer rounded px-[20px] py-[20px] bg-base-100 border-[1px] border-base-200 shadow flex max-w-sm gap-8 items-center"
>
<div>
<i
className="far fa-folder-open text-[40px] text-[#33bbff]"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
<div className="font-semibold ">
<div className="flex items-center gap-2">
<p>
{selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
</p>
<div
className="text-black text-sm"
// onClick={() => SetIsOpen(true)}
>
<i
className="fa fa-pencil cursor-pointer"
title="Select Folder"
aria-hidden="true"
></i>
</div>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p>
</div>
<p className="text-[10px] text-gray-400">
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
</p>
</div>
<div className="absolute top-1 right-1 cursor-pointer">
<div className="absolute top-2 right-1 cursor-pointer">
<Tooltip
message={
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
@@ -207,14 +219,14 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
isOpen={isOpen}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] px-3">
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="Root"
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
Root /{" "}
OpenSign Drive /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
@@ -3,16 +3,6 @@ import Parse from "parse";
import "../../../styles/AddUser.css";
import AsyncSelect from "react-select/async";
const customStyles = {
control: (provided) => ({
...provided,
fontSize: "13px" // Font size for the control
}),
option: (provided) => ({
...provided,
fontSize: "13px" // Font size for the options
})
};
const SelectSigners = (props) => {
const [userList, setUserList] = useState([]);
const [selected, setSelected] = useState();
@@ -53,9 +43,22 @@ const SelectSigners = (props) => {
const contactRes = await contactbook.find();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
// console.log("userList ", res);
setUserList(res);
return await res.map((item) => ({
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
//and filter signers from total signer's list which already present in document's signers list
const compareArrays = (res, signerObj) => {
return res.filter(
(item1) =>
!signerObj.find((item2) => item2.objectId === item1.objectId)
);
};
//get update signer's List if signersdata is present
const updateSignersList =
props?.signersData && compareArrays(res, props?.signersData);
const result = updateSignersList ? updateSignersList : res;
setUserList(result);
return await result.map((item) => ({
label: item.Email,
value: item.objectId
}));
@@ -66,32 +69,44 @@ const SelectSigners = (props) => {
};
return (
<div className="addusercontainer">
<div className="form-wrapper">
<div className="form-section" style={{ marginBottom: 0 }}>
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
<div className="h-full px-[20px] py-[10px] text-base-content">
<div className="w-full mx-auto p-[8px]">
<div className="mb-0">
<label className="text-[14px] font-bold">Choose from contacts</label>
<AsyncSelect
cacheOptions
defaultOptions
value={selected}
loadingMessage={() => "Loading..."}
noOptionsMessage={() => "User not Found"}
noOptionsMessage={() => "Contact not found"}
loadOptions={loadOptions}
onChange={handleOptions}
styles={customStyles}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
</div>
{isError ? (
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
Please select signer
</p>
) : (
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
.
</p>
)}
<p
className={`${
isError ? "text-[red]" : "text-transparent"
} text-[11px] ml-[6px] my-[2px]`}
>
Please select signer
</p>
<div>
<button className="submitbutton" onClick={() => handleAdd()}>
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
Submit
</button>
</div>
@@ -1,14 +1,30 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AddSigner from "../../AddSigner";
import Modal from "react-modal";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
import { createPortal } from "react-dom";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
const AddSignerModal = ({ isOpen, children }) => {
if (!isOpen) {
return null;
}
return createPortal(
<div className="op-modal op-modal-open">
<div className="op-modal-box p-0 min-w-[90%] md:min-w-[500px] max-h-90 overflow-y-auto hide-scrollbar text-sm">
{children}
</div>
</div>,
document.body
);
};
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
@@ -16,7 +32,6 @@ function arrayMove(array, from, to) {
*/
const SignersInput = (props) => {
Modal.setAppElement("body");
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = useState([]);
@@ -104,12 +119,20 @@ const SignersInput = (props) => {
return (
<div className="text-xs mt-2 ">
<label className="block">
<label className="block relative">
Signers
{props.required && <span className="text-red-500 text-[13px]">*</span>}
<span className="absolute ml-1 text-xs z-30">
<Tooltip
id={"signer-tooltip"}
message={
"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."
}
/>
</span>
</label>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<div className="flex gap-x-[5px]">
<div className="w-full">
<Select
onSortEnd={onSortEnd}
distance={4}
@@ -119,6 +142,22 @@ const SignersInput = (props) => {
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
noOptionsMessage={() => "Contact not found"}
unstyled
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
valueContainer: () =>
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
multiValueLabel: () => "mb-[2px]",
menu: () =>
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
menuList: () => "shadow-md rounded-lg overflow-hidden",
option: () =>
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
}}
/>
</div>
<div
@@ -126,66 +165,29 @@ const SignersInput = (props) => {
setIsModel(true);
openModal();
}}
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[38px] min-w-[48px] flex justify-center items-center"
>
<i className="fas fa-plus"></i>
</div>
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
id="modal1"
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
border: "1px transparent",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
style={{ background: "#32a3ac" }}
>
<div className="text-[1.2rem] font-normal">Add Signer</div>
<button
onClick={handleModalCloseClick}
className="text-[1.5rem] cursor-pointer focus:outline-none"
>
&times;
</button>
</div>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
<AddSignerModal isOpen={modalIsOpen}>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
Add Signer
</h3>
<button
onClick={handleModalCloseClick}
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
>
</button>
{isModal && (
<AddSigner
valueKey={"objectId"}
displayKey={"Name"}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</AddSignerModal>
</div>
</div>
);
@@ -0,0 +1,92 @@
import React, { useState, useEffect, useRef } from "react";
import { findContact } from "../../../constant/Utils";
const SuggestionInput = (props) => {
const [inputValue, setInputValue] = useState(props?.value || "");
const [suggestions, setSuggestions] = useState([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const ref = useRef(null);
useEffect(() => {
document.addEventListener("mousedown", Clickout);
return () => {
document.removeEventListener("mousedown", Clickout);
};
}, []);
const Clickout = (event) => {
if (ref.current && !ref.current.contains(event.target)) {
setShowSuggestions(false);
}
};
// create debounce to avoid unnecessay api calls
useEffect(() => {
let timer;
if (inputValue) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
(async () => {
const res = await findContact(inputValue);
if (res?.length > 0) {
setSuggestions(res);
setShowSuggestions(true);
} else {
setSuggestions(res);
setShowSuggestions(false);
}
})();
}, 1000);
}
return () => clearTimeout(timer);
}, [inputValue]);
const handleInputChange = async (e) => {
const value = e.target.value;
setInputValue(value?.toLowerCase());
if (props.onChange) {
props.onChange(value);
}
if (value.trim() === "") {
setSuggestions([]);
setShowSuggestions(false);
return;
}
};
const handleSuggestionClick = (suggestion) => {
setInputValue(suggestion.Email);
setSuggestions([]);
setShowSuggestions(false);
if (props.onChange) {
props.onChange(suggestion);
}
};
return (
<div className="flex flex-col items-center relative text-base-content">
<input
type={props?.type || "text"}
value={inputValue}
onChange={handleInputChange}
placeholder="Enter Email..."
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
required={props.required}
/>
{showSuggestions && (
<ul
ref={ref}
className="absolute z-50 left-0 top-[2.55rem] w-full max-h-[100px] overflow-auto bg-base-200 border border-gray-300 rounded shadow-md"
>
{suggestions.map((suggestion, index) => (
<li
key={index}
className="py-2 px-2 w-full text-sm cursor-pointer hover:bg-base-300"
onClick={() => handleSuggestionClick(suggestion)}
>
{suggestion.Email}
</li>
))}
</ul>
)}
</div>
);
};
export default SuggestionInput;
+6 -2
View File
@@ -3,14 +3,18 @@ import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
return (
<li key={item.title} role="none">
<li key={item.title} role="none" className="my-0.5">
<NavLink
to={
item.pageType
? `/${item.pageType}/${item.objectId}`
: `/${item.objectId}`
}
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer"
className={({ isActive }) =>
`${
isActive ? " bg-base-300 text-base-content" : ""
} flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
onClick={closeSidebar}
tabIndex={isOpen ? 0 : -1}
role="menuitem"
@@ -9,6 +9,7 @@ import dp from "../../assets/images/dp.png";
const Sidebar = ({ isOpen, closeSidebar }) => {
const [menuList, setmenuList] = useState([]);
const [submenuOpen, setSubmenuOpen] = useState(false);
let username = localStorage.getItem("username");
const image = localStorage.getItem("profileImg") || dp;
const tenantname = localStorage.getItem("TenantName");
@@ -33,27 +34,42 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
}
};
const toggleSubmenu = (title) => {
setSubmenuOpen({ [title]: !submenuOpen[title] });
};
const handleMenuItem = () => {
closeSidebar();
setSubmenuOpen({});
};
return (
<aside
className={`bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 md:translate-x-0
${isOpen ? "translate-x-0 block" : "-translate-x-full hidden"}`}
className={`absolute md:relative bg-base-100 h-screen overflow-y-auto transition-all z-[100] hide-scrollbar
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
>
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
<img
className="w-full h-full object-cover"
className="w-full h-full object-contain"
src={image}
alt="Profile"
/>
</div>
<div>
<p className="text-[14px] font-bold">{username}</p>
<p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
<p className="text-[14px] font-bold text-base-content">{username}</p>
<p
className={`text-[12px] text-base-content ${
tenantname ? "mt-2" : ""
}`}
>
{tenantname}
</p>
</div>
</div>
<nav aria-label="OpenSign Sidebar Navigation">
<nav
className="op-menu op-menu-sm"
aria-label="OpenSign Sidebar Navigation"
>
<ul
className="text-sm"
role="menubar"
@@ -65,19 +81,21 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
key={item.title}
item={item}
isOpen={isOpen}
closeSidebar={closeSidebar}
closeSidebar={handleMenuItem}
/>
) : (
<Submenu
key={item.title}
item={item}
closeSidebar={closeSidebar}
toggleSubmenu={toggleSubmenu}
submenuOpen={submenuOpen}
/>
)
)}
</ul>
</nav>
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
<footer className="mt-4 flex justify-center items-center text-[25px] text-base-content gap-3">
<SocialMedia />
</footer>
</aside>
+15 -15
View File
@@ -1,19 +1,13 @@
import React, { useState } from "react";
import React from "react";
import { NavLink } from "react-router-dom";
const Submenu = ({ item, closeSidebar }) => {
const [submenuOpen, setSubmenuOpen] = useState(false);
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const { title, icon, children } = item;
const toggleSubmenu = () => {
setSubmenuOpen(!submenuOpen);
};
return (
<li role="none">
<li role="none" className="my-0.5">
<button
onClick={toggleSubmenu}
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
onClick={() => toggleSubmenu(item.title)}
className="flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none "
aria-expanded={submenuOpen}
aria-haspopup="true"
aria-controls={`submenu-${title}`}
@@ -23,23 +17,29 @@ const Submenu = ({ item, closeSidebar }) => {
<span className="ml-3 lg:ml-4">{title}</span>
<i
className={`${
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
submenuOpen[item.title]
? "fa-solid fa-angle-down"
: "fa-solid fa-angle-right"
}`}
aria-hidden="true"
></i>
</div>
</button>
{submenuOpen && (
{submenuOpen[item.title] && (
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
{children.map((childItem) => (
<li key={childItem.title} role="none">
<li key={childItem.title} role="none" className="my-0.5">
<NavLink
to={
childItem.pageType
? `/${childItem.pageType}/${childItem.objectId}`
: `/${childItem.objectId}`
}
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white cursor-pointer"
className={({ isActive }) =>
`${
isActive ? "bg-base-300 text-base-content" : ""
} flex items-center text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
}
onClick={closeSidebar}
role="menuitem"
tabIndex={submenuOpen ? 0 : -1}
File diff suppressed because it is too large Load Diff
+1 -6
View File
@@ -1,11 +1,6 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
export const rejectBtn =
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
export const submitBtn =
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
export const modalSubmitBtnColor = "#17a2b8";
export const modalCancelBtnColor = "white";
export const themeColor = "#47a3ad";
export const iconColor = "#686968";
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
+44 -62
View File
@@ -1,27 +1,30 @@
import axios from "axios";
const parseAppId = localStorage.getItem("parseAppId");
const serverUrl = localStorage.getItem("baseUrl");
const parseAppId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
export const SaveFileSize = async (size, imageUrl) => {
export const SaveFileSize = async (size, imageUrl, tenantId) => {
//checking server url and save file's size
const tenantPtr = {
__type: "Pointer",
className: "partners_Tenant",
objectId: tenantId
};
const _tenantPtr = JSON.stringify(tenantPtr);
try {
const response = await axios
.get(`${serverUrl}classes/partners_TenantCredits`, {
const res = await axios.get(
`${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
})
.then((result) => {
const res = result.data;
// console.log("res", res);
return res.results;
})
.catch((err) => {
console.log("axois err ", err);
});
}
);
const response = res.data.results;
let data;
// console.log("response", response);
if (response && response.length > 0) {
@@ -30,69 +33,48 @@ export const SaveFileSize = async (size, imageUrl) => {
? response[0].usedStorage + size
: size
};
await axios
.put(
`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
}
)
.then(() => {
// const res = result.data;
// console.log("save res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
} else {
data = {
usedStorage: size
};
await axios
.post(`${serverUrl}classes/partners_TenantCredits`, data, {
await axios.put(
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
})
.then(() => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
}
);
} else {
data = { usedStorage: size, PartnersTenant: tenantPtr };
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
});
}
} catch (e) {
console.log("org app error", e);
} catch (err) {
console.log("err in save usage", err);
}
saveDataFile(size, imageUrl);
saveDataFile(size, imageUrl, tenantPtr);
};
//function for save fileUrl and file size in particular client db class partners_DataFiles
const saveDataFile = async (size, imageUrl) => {
const saveDataFile = async (size, imageUrl, tenantPtr) => {
const data = {
FileUrl: imageUrl,
FileSize: size
FileSize: size,
TenantPtr: tenantPtr
};
// console.log("data save",file, data)
await axios
.post(`${serverUrl}classes/partners_DataFiles`, data, {
try {
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
}
})
.then(() => {
// const res = result.data;
// console.log("res", res);
})
.catch((err) => {
console.log("axois err ", err);
});
} catch (err) {
console.log("err in save usage ", err);
}
};
+8 -9
View File
@@ -2,17 +2,16 @@
@tailwind components;
@tailwind utilities;
body{
background-color: white;
body {
background-color: white;
}
/* Custom CSS to hide scrollbar */
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
display: none;
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+10 -6
View File
@@ -4,6 +4,7 @@ import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { CookiesProvider } from "react-cookie";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import {
@@ -15,6 +16,7 @@ import {
import DragElement from "./components/pdf/DragElement";
import TagManager from "react-gtm-module";
import Parse from "parse";
import './polyfills'
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
@@ -62,10 +64,12 @@ if (process.env.REACT_APP_GTM) {
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
<CookiesProvider defaultSetOptions={{ path: "/" }}>
<Provider store={store}>
<DndProvider options={HTML5toTouch}>
<Preview>{generatePreview}</Preview>
<App />
</DndProvider>
</Provider>
</CookiesProvider>
);
+252 -61
View File
@@ -3,7 +3,7 @@ export default function reportJson(id) {
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
switch (id) {
// draft documents report
case "ByHuevtCFY":
@@ -12,13 +12,24 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "draftDocument"
btnId: "1231",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2142",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
],
helpMsg:
"These are documents you have started but have not finalized for sending."
};
// Need your sign report
case "4Hhwbp482K":
@@ -27,13 +38,17 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnId: "4536",
btnLabel: "sign",
btnColor: "#3ac9d6",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
]
],
helpMsg:
"This is a list of documents that are waiting for your signature"
};
// In progess report
case "1MwEuxLEkF":
@@ -42,13 +57,58 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "View",
btnColor: "#3ac9d6",
textColor: "white",
btnId: "8901",
hoverLabel: "Share",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1588",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1630",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "resend"
},
{
btnId: "1688",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
{
btnId: "1488",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
]
],
helpMsg:
"This is a list of documents you've sent to other parties for signature."
};
// completed documents report
case "kQUoW4hUXz":
@@ -57,13 +117,24 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnId: "1378",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1278",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
],
helpMsg:
"This is a list of documents that have been signed by all parties."
};
// declined documents report
case "UPr2Fm5WY3":
@@ -72,13 +143,24 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnId: "1458",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1358",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
],
helpMsg:
"This is a list of documents that have been declined by one of the signers."
};
// Expired Documents report
case "zNqBHXHsYH":
@@ -87,13 +169,24 @@ export default function reportJson(id) {
heading: head,
actions: [
{
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnId: "1898",
hoverLabel: "View",
btnColor: "op-btn-primary",
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "1998",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
],
helpMsg:
"This is a list of documents that have reached their expiration date."
};
// Recently sent for signatures report show on dashboard
case "d9k3UfYHBc":
@@ -102,11 +195,54 @@ export default function reportJson(id) {
heading: dashboardReportHead,
actions: [
{
btnLabel: "View",
btnColor: "#4bd396",
textColor: "white",
btnId: "1999",
hoverLabel: "Share",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-share",
redirectUrl: "",
action: "share"
},
{
btnId: "1999",
hoverLabel: "View",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
redirectUrl: "pdfRequestFiles",
action: "redirect"
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
subaction: [
{
btnId: "1631",
btnLabel: "Resend",
hoverLabel: "Resend",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "resend"
},
{
btnId: "6788",
btnLabel: "Revoke",
hoverLabel: "Revoke",
btnIcon: "fa-solid fa-file-circle-xmark",
redirectUrl: "",
action: "revoke"
},
{
btnId: "2000",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
]
};
@@ -117,11 +253,13 @@ export default function reportJson(id) {
heading: dashboardReportHead,
actions: [
{
btnLabel: "Sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
btnId: "2001",
btnLabel: "sign",
hoverLabel: "Sign",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-signature",
redirectUrl: "pdfRequestFiles",
action: "redirect"
}
]
};
@@ -132,11 +270,20 @@ export default function reportJson(id) {
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
actions: [
{
btnLabel: "sign",
btnColor: "#4bd396",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "draftDocument"
btnId: "2003",
hoverLabel: "Edit",
btnColor: "op-btn-primary",
btnIcon: "fa-solid fa-pen",
redirectUrl: "draftDocument",
action: "redirect"
},
{
btnId: "2004",
hoverLabel: "Delete",
btnColor: "op-btn-secondary",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
};
@@ -147,35 +294,79 @@ export default function reportJson(id) {
heading: contactbook,
actions: [
{
btnLabel: "",
btnColor: "#f55a42",
textColor: "white",
btnIcon: "fa-solid fa-trash"
btnId: "2204",
hoverLabel: "Delete",
btnColor: "op-btn-outline op-btn-error",
btnIcon: "fa-solid fa-trash",
action: "delete"
}
],
form: "ContactBook"
form: "ContactBook",
helpMsg:
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
};
// template report
case "6TeaPr321t":
return {
reportName: "Templates",
heading: head,
heading: templateReport,
actions: [
{
btnId: "2234",
btnLabel: "Use",
btnColor: "#4bd396",
textColor: "white",
hoverLabel: "Use",
btnColor: "op-btn-primary",
btnIcon: "fa fa-plus",
redirectUrl: "placeHolderSign"
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
},
{
btnLabel: "Edit",
btnColor: "#00c9d5",
textColor: "white",
btnIcon: "fa fa-plus",
redirectUrl: "template"
btnId: "1631",
btnLabel: "Quick send",
hoverLabel: "Quick send",
btnColor: "op-btn-secondary",
btnIcon: "fa-solid fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
},
{
btnId: "2234",
hoverLabel: "option",
btnColor: "",
textColor: "black",
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "2434",
btnLabel: "Edit",
hoverLabel: "Edit",
btnIcon: "fa-solid fa-pen",
redirectUrl: "template",
action: "redirect"
},
{
btnId: "1834",
btnLabel: "Delete",
hoverLabel: "Delete",
btnIcon: "fa fa-trash",
redirectUrl: "",
action: "delete"
}
]
}
]
],
helpMsg:
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
};
default:
return null;
+90
View File
@@ -0,0 +1,90 @@
const dashboardJson = [
{
id: "35KBoSgoAK",
columns: [
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fas fa-signature",
bgColor: "bg-[#f0058e]",
label: "Need your Signature",
description: null,
data: {
id: "e04ee9dc-932d-6b80-0202-703fd154eb74",
queryType: "",
class: "contracts_Document",
query:
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "4Hhwbp482K",
tourSection: "tourcard1",
tourMessage:
"Clicking on this card will take you to the list of documents awaiting your review."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "Card",
icon: "fas fa-sign-out-alt",
bgColor: "bg-[#2ed8b6]",
label: "Out for signatures",
description: null,
data: {
id: "c1935cc0-28d4-3d73-b72a-8c1e3d18a17f",
queryType: "",
class: "contracts_Document",
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF",
tourSection: "tourcard2",
tourMessage:
"Clicking on this card will take you to a list of documents awaiting signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "5Go51Q7T8r",
data: {
tourSection: "tourreport1",
tourMessage:
"This is a list of documents that are waiting for your signature."
}
}
},
{
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
widget: {
type: "report",
reportId: "d9k3UfYHBc",
data: {
tourSection: "tourreport2",
tourMessage:
"This is a list of documents you've sent to other parties for signature."
}
}
},
{
colsize: "col-span-12 md:col-span-12 lg:col-span-12",
widget: {
type: "report",
reportId: "kC5mfynCi4",
data: {
tourSection: "tourreport3",
tourMessage:
"This are documents you have started but have not finalized for sending."
}
}
}
]
}
];
export default dashboardJson;
+79
View File
@@ -0,0 +1,79 @@
const plans = [
{
planName: "OPENSIGN™ FREE",
currency: "",
monthlyPrice: "Free",
yearlyPrice: "Free",
subtitle: "Free Unlimited E-signatures, Forever.",
btnText: "Subscribe",
url: "",
target: "_blank",
benefits: [
"Unlimited envelopes",
"Sign documents yourself",
"Request signatures from others",
"14 field types",
"Automatic e-signatures",
"Completion certificates",
"Send in order",
"Document templates",
"Import from Dropbox",
"Contact book",
"Document expiry support",
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"And much more"
]
},
{
planName: "OPENSIGN™ PROFESSIONAL",
currency: "$",
monthlyPrice: "29.99",
yearlyPrice: "329.99",
subtitle: "Exclusive Access to advanced features.",
btnText: "Subscribe",
url:
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl:
window.location.origin === "https://staging-app.opensignlabs.com"
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self",
benefits: [
"Everything in OpenSign™ free",
"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"
]
},
{
planName: "OPENSIGN™ ENTERPRISE",
currency: "",
monthlyPrice: "Request Price",
yearlyPrice: "Request Price",
subtitle: "Scalable Features with priority support.",
btnText: "Contact us",
url: "https://www.opensignlabs.com/contact-us",
target: "_blank",
benefits: [
"All features",
"Custom domain",
"Custom branding",
"Uptime SLA",
"SSO support",
"Priority support"
]
}
];
export default plans;
-65
View File
@@ -1,65 +0,0 @@
[
{
"planName": "SELF HOSTED",
"currency": "",
"price": "Free",
"subtitle": "Free for life One click install.",
"btnText": "Visit github",
"url": "https://github.com/opensignlabs/opensign",
"target": "_blank",
"benefits": [
"Sign Unlimited Documents",
"S3 Doc Storage",
"Unlimited Guest Signers",
"Unlimited completion certificates",
"Unique Code(OTP) verification for guest signers",
"Expiring docs & reject signature support",
"PDF Template Creation(coming soon)",
"Audit Trails",
"API support",
"Add-ons support(coming soon)",
"Self hosted cloud infrastructure",
"Free for lifetime",
"Community support"
]
},
{
"planName": "THANKS PLAN",
"currency": "$",
"price": "29.99",
"subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)",
"btnText": "Subscribe",
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
"target": "_self",
"benefits": [
"Sign Unlimited Documents",
"Unlimited Secure Doc Storage with OpenSign™ Drive",
"Unlimited Guest Signers",
"Unlimited completion certificates",
"Unique Code(OTP) verification for guest signers",
"Expiring docs & reject signature support",
"PDF Template Creation(coming soon)",
"Audit Trails",
"API support",
"Add-ons support(coming soon)",
"Self hosted cloud infrastructure"
]
},
{
"planName": "ENTERPRISE",
"currency": "",
"price": "Request Price",
"subtitle": "Customization available Priority support.",
"btnText": "Contact us",
"url": "https://www.opensignlabs.com/contact-us",
"target": "_blank",
"benefits": [
"All features",
"Custom domain",
"Whitelabeling",
"Uptime SLA",
"SSO support",
"24/7 support"
]
}
]
+90 -47
View File
@@ -9,6 +9,10 @@ import { useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate, useLocation, Outlet } from "react-router-dom";
import { isEnableSubscription } from "../constant/const";
import { useCookies } from "react-cookie";
import { fetchSubscription } from "../constant/Utils";
import Loader from "../primitives/Loader";
const HomeLayout = () => {
const navigate = useNavigate();
@@ -18,33 +22,80 @@ const HomeLayout = () => {
const arr = useSelector((state) => state.TourSteps);
const [isUserValid, setIsUserValid] = useState(true);
const [isLoader, setIsLoader] = useState(true);
useEffect(() => {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
setIsUserValid(true);
setIsLoader(false);
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
}, []);
// reactour state
const [isCloseBtn, setIsCloseBtn] = useState(true);
const [isTour, setIsTour] = useState(false);
const [tourStatusArr, setTourStatusArr] = useState([]);
const [tourConfigs, setTourConfigs] = useState([]);
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
const tenantId = localStorage.getItem("TenantId");
useEffect(() => {
if (!tenantId) {
setIsUserValid(false);
} else {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
checkIsSubscribed();
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
saveCookies();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tenantId]);
//function to use save data in cookies storage
const saveCookies = () => {
const main_Domain = window.location.origin;
const domainName = window.location.hostname; //app.opensignlabs.com
// Find the index of the first dot in the string
const indexOfFirstDot = domainName.indexOf(".");
// Remove the first dot and get the substring starting from the next character
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
secure: true,
domain: updateDomain
});
setCookie("main_Domain", main_Domain, {
secure: true,
domain: updateDomain
});
};
async function checkIsSubscribed() {
if (isEnableSubscription) {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
setIsUserValid(true);
setIsLoader(false);
} else if (res.billingDate) {
if (new Date(res.billingDate) > new Date()) {
setIsUserValid(true);
setIsLoader(false);
} else {
navigate(`/subscription`);
}
} else {
navigate(`/subscription`);
}
} else {
setIsUserValid(true);
setIsLoader(false);
}
}
const showSidebar = () => {
setIsOpen((value) => !value);
};
@@ -94,6 +145,12 @@ const HomeLayout = () => {
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
{
selector: '[data-tut="tourbutton"]',
content: `To upload documents for self-signing or to request others signatures, simply select the respective buttons.`,
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
...resArr,
{
selector: '[data-tut="reactourLast"]',
@@ -191,35 +248,24 @@ const HomeLayout = () => {
};
return (
<div>
<div className="sticky top-0 z-50">
<Header showSidebar={showSidebar} />
<div className="sticky top-0 z-[101]">
{!isLoader && <Header showSidebar={showSidebar} />}
</div>
{isUserValid ? (
<>
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="flex h-[100vh] justify-center items-center">
<Loader />
</div>
) : (
<>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
<div
id="renderList"
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
>
<div className="bg-base-200 p-3">{<Outlet />}</div>
<div className="z-30">
<Footer />
</div>
@@ -240,13 +286,10 @@ const HomeLayout = () => {
)}
</>
) : (
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
<ModalUi showHeader={false} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-normal">Your session has expired.</p>
<button
onClick={handleLoginBtn}
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
>
<p className="text-xl font-medium">Your session has expired.</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
Login
</button>
</div>
+6 -9
View File
@@ -49,13 +49,13 @@ function ChangePassword() {
return <Navigate to="/" />;
}
return (
<div className="w-full bg-white shadow rounded p-2">
<div className="w-full bg-base-100 text-base-content shadow rounded p-2">
<Title title={"Change Password"} />
<div className="text-xl font-bold border-b-[1px] border-gray-300">
Change Password
</div>
<div className="m-2">
<form onSubmit={handleSubmit}>
<form onSubmit={handleSubmit} className=" flex flex-col gap-y-2">
<div>
<label htmlFor="currentpassword" className="block text-xs ml-1">
Current Password
@@ -65,7 +65,7 @@ function ChangePassword() {
name="currentpassword"
value={currentpassword}
onChange={(e) => setCurrentPassword(e.target.value)}
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm text-xs w-full"
placeholder="Current Password"
required
/>
@@ -79,7 +79,7 @@ function ChangePassword() {
name="newpassword"
value={newpassword}
onChange={(e) => setnewpassword(e.target.value)}
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm text-xs w-full"
placeholder="New Password"
required
/>
@@ -91,17 +91,14 @@ function ChangePassword() {
<input
type="password"
name="confirmpassword"
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm text-xs w-full"
value={confirmpassword}
onChange={(e) => setconfirmpassword(e.target.value)}
placeholder="Confirm Password"
required
/>
</div>
<button
type="submit"
className="bg-[#188ae2] m-2 px-4 py-3 rounded-sm shadow-md text-white text-xs font-semibold uppercase"
>
<button type="submit" className="op-btn op-btn-primary shadow-md m-2">
Change Password
</button>
</form>
+22 -89
View File
@@ -1,18 +1,19 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import "../styles/loader.css";
import GetDashboard from "../components/dashboard/GetDashboard";
import { useNavigate, useParams } from "react-router-dom";
import Title from "../components/Title";
import { save_tourSteps } from "../redux/actions";
import { connect } from "react-redux";
const Dashboard = (props) => {
import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson";
import Loader from "../primitives/Loader";
const Dashboard = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const { id } = useParams();
const [dashboard, setdashboard] = useState([]);
const [classnameArray, setclassnameArray] = useState([]);
const [loading, setloading] = useState(false);
const [defaultQuery, setDefaultQuery] = useState("");
const [dashboard, setdashboard] = useState({});
const [loading, setloading] = useState(true);
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
@@ -22,59 +23,16 @@ const Dashboard = (props) => {
getDashboard(localStorage.getItem("PageLanding"));
}
} else {
navigate("/", { replace: true, state: { from: location } });
navigate("/", { replace: true, state: { from: "" } });
}
// eslint-disable-next-line
}, [id]);
const getDashboard = async (id) => {
setloading(true);
try {
var forms = Parse.Object.extend("w_dashboard");
var query = new Parse.Query(forms);
query.equalTo("objectId", id);
const results = await query.first();
const resultjson = results.toJSON();
let classArray = [];
let DefaultQuery = "";
resultjson.rows.forEach((x) => {
let col_lg = "",
col_md = "",
col_xs = "",
col_sm = "",
col_ = "",
obj = {};
let subItem = [];
let item = "";
x.columns.forEach((y) => {
if (y.widget.type === "customeHtml") {
DefaultQuery = y.widget.data[0].default;
}
if (Number(y["collg"]) > 0) {
col_lg = "col-lg-" + y["collg"];
}
if (Number(y["colmd"]) > 0) {
col_md = "col-md-" + y["colmd"];
}
if (Number(y["colxs"]) > 0) {
col_xs = "col-xs-" + y["colxs"];
}
if (Number(y["colsm"]) > 0) {
col_sm = "col-sm-" + y["colsm"];
}
if (Number(y["col"]) > 0) {
col_ = "col-" + y["col"];
}
item =
col_lg + " " + col_md + " " + col_xs + " " + col_sm + " " + col_;
subItem.push(item);
});
obj = subItem;
classArray.push(obj);
});
setdashboard(resultjson.rows);
// console.log("resultjson.rows ", resultjson.rows);
const arr = resultjson.rows[0].columns
const dashboard = dashboardJson.find((x) => x.id === id);
setdashboard(dashboard);
const dashboardTour = dashboard.columns
.filter((col) => {
if (col.widget.data && col.widget.data.tourSection) {
return col;
@@ -88,14 +46,7 @@ const Dashboard = (props) => {
// style: { backgroundColor: "#abd4d2" },
};
});
props.save_tourSteps(arr);
// console.log("arr ", arr);
setclassnameArray(classArray);
if (localStorage.getItem("DashboardDefaultFilter")) {
setDefaultQuery(localStorage.getItem("DashboardDefaultFilter"));
} else {
setDefaultQuery(DefaultQuery);
}
dispatch(saveTourSteps(dashboardTour));
setloading(false);
} catch (e) {
console.error("Problem", e);
@@ -103,36 +54,18 @@ const Dashboard = (props) => {
}
};
let _dash = (
<GetDashboard
dashboard={dashboard}
classnameArray={classnameArray}
DefaultQuery={defaultQuery}
/>
);
if (loading) {
_dash = (
<div style={{ height: "300px", backgroundColor: "white" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
}
return (
<React.Fragment>
<Title title="Dashboard" />
{_dash}
{loading ? (
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
<Loader />
</div>
) : (
<GetDashboard dashboard={dashboard} />
)}
</React.Fragment>
);
};
export default connect(null, {
save_tourSteps
})(Dashboard);
export default Dashboard;
+52 -43
View File
@@ -1,18 +1,26 @@
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { fetchAppInfo, forgetPassword } from "../redux/actions";
import Title from "../components/Title";
import { NavLink } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import Parse from "parse";
import Alert from "../primitives/Alert";
function ForgotPassword(props) {
import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo } from "../constant/Utils";
function ForgotPassword() {
const dispatch = useDispatch();
const navigate = useNavigate();
const [state, setState] = useState({
email: "",
password: "",
hideNav: ""
});
const [sentStatus, setSentStatus] = useState("");
const [image, setImage] = useState();
const handleChange = (event) => {
const { name, value } = event.target;
setState({ ...state, [name]: value });
@@ -27,14 +35,11 @@ function ForgotPassword(props) {
const handleSubmit = async (event) => {
event.preventDefault();
localStorage.setItem("appLogo", props.appInfo.applogo);
localStorage.setItem("appName", props.appInfo.appname);
localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
localStorage.setItem(
"userSettings",
JSON.stringify(props.appInfo.settings)
);
localStorage.setItem("appLogo", appInfo.applogo);
localStorage.setItem("appName", appInfo.appname);
// localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
// localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
if (state.email) {
const username = state.email;
try {
@@ -50,16 +55,27 @@ function ForgotPassword(props) {
};
useEffect(() => {
props.fetchAppInfo();
dispatch(fetchAppInfo());
saveLogo();
resize();
window.addEventListener("resize", resize);
return () => window.removeEventListener("resize", resize);
// eslint-disable-next-line
}, []);
let image = props.appInfo.applogo;
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
} else {
setImage(appInfo?.applogo || undefined);
}
} else {
setImage(appInfo?.applogo || undefined);
}
};
return (
<div className="bg-white">
<div>
<Title title="Forgot password page" />
{sentStatus === "success" && (
<Alert type="success">
@@ -69,29 +85,31 @@ function ForgotPassword(props) {
{sentStatus === "failed" && (
<Alert type={"danger"}>Please setup email adapter </Alert>
)}
<div>
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block">
{state.hideNav ? (
<img src={image} width="100%" alt="" />
) : (
<img src={image} width="100%" alt="" />
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{image && (
<img
src={image}
className="object-contain h-full"
alt="applogo"
/>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div className="">
<div>
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
<span className="text-[12px] text-[#878787]">
Reset Your Password
</span>
<div className="shadow-md rounded my-4">
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="px-6 py-4">
<label className="block text-xs">Email</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
type="email"
name="email"
className="op-input op-input-bordered op-input-sm w-full"
value={state.email}
onChange={handleChange}
required
@@ -99,19 +117,16 @@ function ForgotPassword(props) {
<hr className="my-2 border-none" />
</div>
</div>
<div className="flex justify-between items-center px-4">
<button
type="submit"
className="rounded-sm bg-[#3ac9d6] text-white px-4 py-3 text-xs font-semibold shadow uppercase"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<NavLink
to="/"
className="rounded-sm bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-3 text-xs font-semibold shadow uppercase"
<button
onClick={() => navigate("/", { replace: true })}
className="op-btn op-btn-secondary"
>
Login
</NavLink>
</button>
</div>
</form>
</div>
@@ -129,10 +144,4 @@ function ForgotPassword(props) {
);
}
const mapStateToProps = (state) => {
return { appInfo: state.appInfo, forgotPassword: state.forgotPassword };
};
export default connect(mapStateToProps, { fetchAppInfo, forgetPassword })(
ForgotPassword
);
export default ForgotPassword;
File diff suppressed because it is too large Load Diff
+96 -43
View File
@@ -1,12 +1,17 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
function GenerateToken() {
const navigation = useNavigate();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [apiToken, SetApiToken] = useState("");
@@ -15,6 +20,7 @@ function GenerateToken() {
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
useEffect(() => {
fetchToken();
@@ -23,6 +29,11 @@ function GenerateToken() {
const fetchToken = async () => {
try {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
"Content-Type": "application/json",
@@ -33,6 +44,7 @@ function GenerateToken() {
if (res) {
SetApiToken(res.data.result.result);
}
setIsLoader(false);
} catch (err) {
SetApiToken();
@@ -81,13 +93,14 @@ function GenerateToken() {
};
const copytoclipboard = (text) => {
navigator.clipboard.writeText(text);
copytoData(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleModal = () => setIsModal(!isModal);
return (
<React.Fragment>
<Title title={"API Token"} />
@@ -97,47 +110,55 @@ function GenerateToken() {
{copied && <Alert type="success">Copied</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="flex justify-center items-center h-screen">
<Loader />
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">API Token</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<span className="w-[40%]">Api Token:</span>{" "}
<span
id="token"
className="w-[60%] md:text-end cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign API{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
}
isSubscribe={true}
/>
</h1>
<ul
className={
isSubscribe || !isEnableSubscription
? "w-full flex flex-col p-2 text-sm "
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
}
>
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5 ">
<span className="">Api Token:</span>{" "}
<span id="token" className="md:text-end py-2 md:py-0">
<span
className="cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken ? apiToken : "_____"}
</span>
<button
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2"
onClick={() => copytoclipboard(apiToken)}
>
<i className="fa-solid fa-copy"></i>
</button>
</span>
</div>
<button
onClick={apiToken ? handleModal : handleSubmit}
className="op-btn op-btn-primary"
>
{apiToken && apiToken}
</span>
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
</li>
</ul>
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
<button
type="button"
onClick={apiToken ? handleModal : handleSubmit}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
<div className="flex items-center justify-center ">
<button
type="button"
onClick={() =>
@@ -145,19 +166,48 @@ function GenerateToken() {
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
className="op-btn op-btn-secondary my-2"
>
View Docs
</button>
</div>
{!isSubscribe && isEnableSubscription && (
<>
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
Upgrade to PRO Plan
</h1>
<ul className={"w-full flex flex-col p-2 text-sm "}>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
<span className="">$29.99/month:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
Sign 100 docs using API. Only 0.15/docs after that.
</span>
</div>
{!isSubscribe && isEnableSubscription && (
<button
type="button"
onClick={() => navigation("/subscription")}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
Upgrade Now
</button>
)}
</li>
</ul>
</>
)}
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
<div className="text-lg font-normal text-base-content">
Are you sure you want to regenerate token it will expire old
token?
</div>
@@ -165,11 +215,14 @@ function GenerateToken() {
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
+220 -175
View File
@@ -1,80 +1,103 @@
import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import "../styles/loginPage.css";
import loader from "../assets/images/loader2.gif";
import axios from "axios";
import { themeColor } from "../constant/const";
import { contractUsers } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import { contractUsers, getAppLogo } from "../constant/Utils";
import logo from "../assets/images/logo.png";
import { appInfo } from "../constant/appinfo";
import Parse from "parse";
import Loader from "../primitives/Loader";
function GuestLogin() {
const { id, userMail, contactBookId, serverUrl } = useParams();
let navigate = useNavigate();
const { id, userMail, contactBookId, base64url } = useParams();
const navigate = useNavigate();
const [email, setEmail] = useState(userMail);
const [OTP, setOTP] = useState("");
const [EnterOTP, setEnterOtp] = useState(false);
const [loading, setLoading] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [appLogo, setAppLogo] = useState("");
const [documentId, setDocumentId] = useState(id);
const [contactId, setContactId] = useState(contactBookId);
const [sendmail, setSendmail] = useState();
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
useEffect(() => {
handleServerUrl();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
//function generate serverUrl and parseAppId from url and save it in local storage
const handleServerUrl = () => {
//split url in array from '&'
localStorage.clear();
const checkSplit = serverUrl.split("&");
const server = checkSplit[0];
const parseId = checkSplit[1];
const appName = checkSplit[2];
const handleServerUrl = async () => {
if (isEnableSubscription) {
const applogo = await getAppLogo();
if (applogo) {
setAppLogo(applogo);
} else {
setAppLogo(logo);
}
} else {
setAppLogo(logo);
}
const newServer = server.replaceAll("%2F", "/");
localStorage.clear();
const parseId = appInfo.appId;
const newServer = `${appInfo.baseUrl}/`;
const appName = appInfo.appname;
localStorage.setItem("baseUrl", newServer);
localStorage.setItem("parseAppId", parseId);
localStorage.setItem("_appName", appName);
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
if (!id) {
//`atob` function is used to decode base64
const decodebase64 = atob(base64url);
//split url in array from '/'
const checkSplit = decodebase64.split("/");
setDocumentId(checkSplit[0]);
setContact((prev) => ({ ...prev, email: checkSplit[1] }));
setEmail(checkSplit[1]);
const contactId = checkSplit?.[2];
setSendmail(checkSplit[3]);
if (!contactId) {
const params = { email: checkSplit[1], docId: checkSplit[0] };
try {
const linkContactRes = await Parse.Cloud.run(
"linkcontacttodoc",
params
);
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes?.contactId);
} catch (err) {
console.log("Err in link ext contact", err);
}
} else {
setContactId(checkSplit[2]);
}
}
setIsLoading(false);
};
const handleChange = (event) => {
const { value } = event.target;
setOTP(value);
};
//send email OTP function
const SendOtp = async (e) => {
const serverUrl =
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
const parseId =
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
if (serverUrl && localStorage) {
setLoading(true);
e.preventDefault();
setEmail(email);
try {
let url = `${serverUrl}functions/SendOTPMailV1/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseId
};
let body = {
email: email.toString()
};
let Otp = await axios.post(url, body, { headers: headers });
if (Otp) {
setLoading(false);
setEnterOtp(true);
}
} catch (error) {
alert("something went wrong!");
const SendOtp = async () => {
setLoading(true);
setEmail(email);
try {
const params = { email: email.toString(), docId: documentId };
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
if (Otp) {
setLoading(false);
setEnterOtp(true);
}
} else {
} catch (error) {
alert("something went wrong!");
}
};
const handleSendOTPBtn = async (e) => {
e.preventDefault();
await SendOtp();
};
//verify OTP send on via email
const VerifyOTP = async (e) => {
e.preventDefault();
@@ -110,7 +133,8 @@ function GuestLogin() {
`Parse/${parseId}/currentUser`,
JSON.stringify(_user)
);
if (contractUserDetails) {
// console.log("contractUserDetails ", contractUserDetails);
if (contractUserDetails && contractUserDetails.length > 0) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
@@ -122,7 +146,13 @@ function GuestLogin() {
//save isGuestSigner true in local to handle login flow header in mobile view
localStorage.setItem("isGuestSigner", true);
setLoading(false);
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
if (sendmail === "false") {
navigate(
`/load/recipientSignPdf/${documentId}/${contactId}?sendmail=${sendmail}`
);
} else {
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
}
}
} catch (error) {
console.log("err ", error);
@@ -131,154 +161,169 @@ function GuestLogin() {
alert("Please Enter OTP!");
}
};
const handleUserData = async (e) => {
e.preventDefault();
const params = { ...contact, docId: documentId };
try {
setLoading(true);
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
// console.log("linkContactRes ", linkContactRes);
setContactId(linkContactRes.contactId);
setEnterOtp(true);
await SendOtp();
} catch (err) {
setLoading(false);
alert("something went wron, please try agian later.");
console.log("Err in link ext contact", err);
}
};
const handleInputChange = (e) => {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
return (
<div style={{ padding: "2rem", background: "white" }}>
<div className="p-[2rem]">
{isLoading ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh",
flexDirection: "column"
}}
>
<img
alt="no img"
src={loader}
style={{ width: "80px", height: "80px" }}
/>
<span style={{ fontSize: "13px", color: "gray" }}>
{isLoading.message}
</span>
<div className="flex flex-col justify-center items-center h-[100vh]">
<Loader />
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
</div>
) : (
<div
style={{
margin: "10px",
border: "0.5px solid #c5c7c9",
padding: "30px",
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"
}}
>
<div className="main_head">
<div className="main-logo">
<img
alt="sign img"
src="https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"
width="100%"
/>
</div>
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
{appLogo && (
<img src={appLogo} className="object-contain h-full" alt="logo" />
)}
</div>
{!EnterOTP ? (
<div className="row">
<div className="col-sm-6 KLO">
<span className="welcomeText">Welcome Back !</span>
<br />
<span className="KNLO">
Verification code is sent to your email
</span>
<div className="card card-box" style={{ borderRadius: "0px" }}>
<div className="card-body">
{contactId ? (
<>
{!EnterOTP ? (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2">
Verification code is sent to your email
</legend>
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
<input
type="email"
name="mobile"
value={email}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled
className="loginInput"
/>
<br />
</div>
<div className="mt-2">
<button
className="op-btn op-btn-primary"
onClick={(e) => handleSendOTPBtn(e)}
disabled={loading}
>
{loading ? "Loading..." : "Send OTP"}
</button>
</div>
</div>
<div className="btnContainer">
{loading ? (
<button
type="button"
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
disabled
>
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
</button>
) : (
<button
className="verifyBtn"
style={{
background: themeColor,
color: "white",
marginLeft: "0px !important"
}}
onClick={(e) => SendOtp(e)}
>
Send OTP
</button>
)}
</div>
</div>
</div>
) : (
<div className="row">
<div className="col-sm-6 KLO">
<span className="welcomeText">Welcome Back !</span>
<br />
<span className="KNLO">You will get a OTP via Email</span>
<div className="card card-box">
<div className="card-body">
<label>Enter Verification Code</label>
) : (
<form
className="w-full md:w-[50%] text-base-content"
onSubmit={VerifyOTP}
>
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2">
You will get a OTP via Email
</legend>
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
<p className="text-sm">Enter Verification Code</p>
<input
type="number"
className="loginInput"
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="OTP"
value={OTP}
onChange={handleChange}
onChange={(e) => setOTP(e.target.value)}
/>
<br />
</div>
</div>
<div>
{loading ? (
<div className="mt-2">
<button
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
type="button"
disabled
className="op-btn op-btn-primary"
type="submit"
disabled={loading}
>
<span
className="spinner-border spinner-border-sm "
role="status"
aria-hidden="true"
></span>
Loading...
{loading ? "Loading..." : "Verify"}
</button>
) : (
<button
type="button"
onClick={(e) => VerifyOTP(e)}
style={{
background: themeColor,
color: "white"
}}
className="verifyBtn"
>
Verify
</button>
)}
</div>
</form>
)}
</>
) : (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
<legend className="text-[12px] text-[#878787] mt-2">
Provide your details
</legend>
<form
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"
onSubmit={handleUserData}
>
<div className="mb-2">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={contact.name}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
required
/>
</div>
</div>
<div className="mb-2">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={contact.email}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
required
disabled
/>
</div>
<div className="mb-2">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
</label>
<input
type="text"
name="phone"
value={contact.phone}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
/>
</div>
<div className="mt-2 flex justify-start">
<button
type="submit"
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Next"}
</button>
</div>
</form>
</div>
)}
</div>
File diff suppressed because it is too large Load Diff
+44 -145
View File
@@ -5,6 +5,9 @@ import "../styles/signature.css";
import { toDataUrl } from "../constant/Utils";
import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { SaveFileSize } from "../constant/saveFileSize";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
const ManageSign = () => {
const appName = appInfo.appname;
const [penColor, setPenColor] = useState("blue");
@@ -13,11 +16,10 @@ const ManageSign = () => {
const [signName, setSignName] = useState("");
const [signature, setSignature] = useState("");
const [warning, setWarning] = useState(false);
const [nameWarning, setNameWarning] = useState(false);
const [isvalue, setIsValue] = useState(false);
const allColor = ["blue", "red", "black"];
const [isLoader, setIsLoader] = useState(true);
const [isSuccess, setIsSuccess] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
const [Initials, setInitials] = useState("");
const [isInitials, setIsInitials] = useState(false);
const [id, setId] = useState("");
@@ -105,9 +107,7 @@ const ManageSign = () => {
e.preventDefault();
const isUrl = image.includes("https");
if (!signName) {
setNameWarning(true);
} else if (!isvalue) {
if (!isvalue) {
setWarning(true);
setTimeout(() => setWarning(false), 1000);
} else {
@@ -161,7 +161,11 @@ const ManageSign = () => {
try {
const parseFile = new Parse.File(file.name, file);
const response = await parseFile.save();
return response?.url();
if (response?.url()) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(file.size, response.url(), tenantId);
return response?.url();
}
} catch (err) {
console.log("sign upload err", err);
setIsLoader(false);
@@ -183,12 +187,20 @@ const ManageSign = () => {
updateSign.set("UserId", userId);
const res = await updateSign.save();
setIsLoader(false);
setIsSuccess(true);
setIsAlert({
type: "success",
message: "Signature saved successfully."
});
setTimeout(() => setIsAlert({}), 2000);
return res;
} catch (err) {
console.log(err);
setIsLoader(false);
alert(`${err.message}`);
setIsAlert({
type: "danger",
message: `${err.message}`
});
setTimeout(() => setIsAlert({}), 2000);
}
} else {
try {
@@ -199,12 +211,20 @@ const ManageSign = () => {
updateSign.set("UserId", userId);
const res = await updateSign.save();
setIsLoader(false);
setIsSuccess(true);
setIsAlert({
type: "success",
message: "Signature saved successfully."
});
setTimeout(() => setIsAlert({}), 2000);
return res;
} catch (err) {
console.log(err);
setIsLoader(false);
alert(`${err.message}`);
setIsAlert({
type: "success",
message: `${err.message}`
});
setTimeout(() => setIsAlert({}), 2000);
}
}
};
@@ -222,37 +242,13 @@ const ManageSign = () => {
setIsValue(true);
};
return (
<div
style={{
backgroundColor: "white",
maxHeight: "500px",
overflow: "auto"
}}
>
<div className="relative h-full bg-base-100 text-base-content flex shadow rounded overflow-auto">
{isLoader && (
<div className="absolute bg-black bg-opacity-75 inset-0 z-50 flex justify-center items-center">
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
</div>
)}
{isSuccess && (
<div
className="alert alert-success successBox"
role="alert"
onAnimationEnd={() => setIsSuccess(false)}
>
Signature saved successfully!
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
<Loader />
</div>
)}
{isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
<div
className="mainDiv"
style={{
@@ -260,91 +256,13 @@ const ManageSign = () => {
paddingRight: "10px"
}}
>
<div style={{ margin: 20 }}>
<div
style={{
fontWeight: "700",
fontSize: 15,
color: "#000",
paddingBottom: 8
}}
>
Manage Signature
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<label
htmlFor="signName"
style={{
fontSize: 12,
position: "relative",
paddingBottom: 5
}}
>
Signer&apos;s Name
{nameWarning && (
<div
className="warning nameWarning"
style={{ fontSize: 12 }}
onAnimationEnd={() => setNameWarning(false)}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please fill input field
</div>
)}
</label>
<input
id="signName"
className="signInputManage"
value={signName}
style={{
border: "1px solid #ccc",
borderRadius: 4,
height: 35,
padding: "6px 12px",
marginBottom: 10,
fontSize: 12
}}
required
onFocus={() => setNameWarning(false)}
onChange={(e) => setSignName(e.target.value)}
/>
<label
htmlFor="imgoverview"
style={{
fontSize: 12,
paddingBottom: 5
}}
>
Signature/Image
</label>
</div>
<div className="signBlock">
<div className="m-[20px]">
<div className="font-[700] text-[15px] pb-[8px]">My Signature</div>
<div className="signBlock mt-4">
<div>
<div style={{ position: "relative" }}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "50%",
paddingLeft: 10
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center",
gap: 10,
marginBottom: 10
}}
>
<div className="relative">
<div className="flex flex-row justify-between w-1/2 pl-[10px]">
<div className="flex flex-row justify-around items-center gap-[10px] mb-[10px]">
<>
<span
onClick={() => handleSignatureBtn()}
@@ -370,24 +288,14 @@ const ManageSign = () => {
/>
</div>
</div>
<div style={{ position: "relative" }}>
<div className="relative">
<div>
{image ? (
<div
style={{
position: "relative",
border: "2px solid #888"
}}
className="signatureCanvas"
>
<div className="signatureCanvas relative border-[2px] border-[#888]">
<img
alt="preview image"
src={image}
style={{
width: "100%",
height: "100%",
objectFit: "contain"
}}
className="w-full h-full object-contain"
/>
</div>
) : (
@@ -572,20 +480,11 @@ const ManageSign = () => {
</div>
</div>
</div>
{/* {!warning && (
<div className="warning signWarning" style={{ fontSize: 12 }}>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please upload signature/Image
</div>
)} */}
</div>
</div>
<div style={{ paddingTop: 10 }}>
<button
className="customBtn successBtn"
className="op-btn op-btn-primary"
onClick={(e) => handleSubmit(e)}
>
save
File diff suppressed because it is too large Load Diff
+3 -5
View File
@@ -4,13 +4,11 @@ import Title from "../components/Title";
const PageNotFound = ({ prefix }) => {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded">
<Title title={"Page Not Found"} />
<div className="text-center">
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
404
</h1>
<p className="text-[30px] lg:text-[50px] text-black">
<h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1>
<p className="text-[30px] lg:text-[50px]">
{prefix ? prefix : "Page"} Not Found
</p>
</div>
File diff suppressed because it is too large Load Diff
+37 -82
View File
@@ -2,14 +2,18 @@ import React, { useState, useEffect } from "react";
import "../styles/pgsignup.css";
import Parse from "parse";
import axios from "axios";
import { fetchAppInfo, showTenantName } from "../redux/actions/index";
import { connect } from "react-redux";
import { useDispatch } from "react-redux";
import Title from "../components/Title";
import { useNavigate, useLocation } from "react-router-dom";
import { appInfo } from "../constant/appinfo";
import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import Loader from "../primitives/Loader";
const PgSignUp = (props) => {
const PgSignUp = () => {
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [formData, setFormData] = useState({
@@ -24,8 +28,7 @@ const PgSignUp = (props) => {
// below useEffect is used to fetch App data and save to redux state
useEffect(() => {
// Parse.User.logOut()
props.fetchAppInfo();
dispatch(fetchAppInfo());
handleSignedUpUser();
// eslint-disable-next-line
}, []);
@@ -39,12 +42,8 @@ const PgSignUp = (props) => {
const url = window.location.href;
let paramString = url.split("?")[1];
let queryString = new URLSearchParams(paramString);
// console.log("url ", queryString);
let obj = { ...formData };
for (let pair of queryString.entries()) {
// console.log("Key is: " + pair[0]);
// console.log("Value is: " + pair[1]);
obj = { ...obj, [pair[0]]: pair[1] };
}
const zohoRes = await axios.post(
@@ -59,34 +58,27 @@ const PgSignUp = (props) => {
}
}
);
// console.log("zohoRes ", zohoRes);
const userSettings = JSON.parse(localStorage.getItem("userSettings"));
const extClass = userSettings[0].extended_class;
// console.log("extClass ", extClass);
const params = { email: userDetails.email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("res", res);
if (res) {
const updateQuery = new Parse.Object(extClass);
updateQuery.id = res.id;
updateQuery.set(
"Next_billing_date",
new Date(zohoRes.data.result.nextBillingDate)
);
updateQuery.set("Plan", zohoRes.data.result.plan);
updateQuery.set("Customer_id", zohoRes.data.result.customer_id);
updateQuery.set(
"Subscription_id",
zohoRes.data.result.subscription_id
);
const updateRes = await updateQuery.save();
if (updateRes) {
const params = { subscription: zohoRes.data.result.subscription };
const baseurl = `${parseBaseUrl}functions/savesubscription`;
try {
const res = await axios.post(baseurl, params, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
}
});
if (res) {
localStorage.removeItem("userDetails");
navigate(
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId
);
setIsLoader(false);
}
} catch (err) {
console.log("err ", err.message);
setIsLoader(false);
}
} catch (err) {
console.log("err ", err);
@@ -106,12 +98,8 @@ const PgSignUp = (props) => {
const url = window.location.href;
let paramString = url.split("?")[1];
let queryString = new URLSearchParams(paramString);
// console.log("url ", queryString);
let obj = { ...formData };
for (let pair of queryString.entries()) {
// console.log("Key is: " + pair[0]);
// console.log("Value is: " + pair[1]);
obj = { ...obj, [pair[0]]: pair[1] };
}
saveUser(obj);
@@ -140,7 +128,9 @@ const PgSignUp = (props) => {
user.set("username", zohoRes.data.result.email);
user.set("password", obj.password);
user.set("email", zohoRes.data.result.email);
user.set("phone", zohoRes.data.result.phone);
if (zohoRes?.data?.result?.phone) {
user.set("phone", zohoRes.data.result.phone);
}
user.set("name", zohoRes.data.result.name);
const res = await user.signUp();
@@ -153,21 +143,10 @@ const PgSignUp = (props) => {
company: zohoRes.data.result.company,
name: zohoRes.data.result.name,
email: zohoRes.data.result.email,
phone: zohoRes.data.result.phone,
phone: zohoRes?.data?.result?.phone || "",
role: obj.role
// "pincode": "",
// "address": "",
// "country": "",
// "state": "",
// "city": ""
},
planDetails: {
plan: zohoRes.data.result.plan || {},
nextBillingDate: zohoRes.data.result.nextBillingDate || "",
customer_id: zohoRes.data.result.customer_id || "",
subscription_id: zohoRes.data.result.subscription_id || ""
}
subscription: zohoRes.data.result.subscription
};
const usersignup = await Parse.Cloud.run("usersignup", params);
// console.log("usersignup ", usersignup);
@@ -243,8 +222,8 @@ const PgSignUp = (props) => {
// Check extended class user role and tenentId
try {
let userRoles = [];
if (props.appInfo.settings) {
let userSettings = props.appInfo.settings;
if (appInfo.settings) {
let userSettings = appInfo.settings;
//Get Current user roles
let url = `${baseUrl}functions/UserGroups`;
@@ -319,8 +298,8 @@ const PgSignUp = (props) => {
}
});
if (tenentInfo.length) {
props.showTenantName(
tenentInfo[0].tenentName || ""
dispatch(
showTenant(tenentInfo[0].tenentName || "")
);
localStorage.setItem(
"TenantName",
@@ -351,15 +330,15 @@ const PgSignUp = (props) => {
tenentName: x.TenantId.TenantName || ""
};
localStorage.setItem(
"TenetId",
"TenantId",
x.TenantId.objectId
);
tenentInfo.push(obj);
}
});
if (tenentInfo.length) {
props.showTenantName(
tenentInfo[0].tenentName || ""
dispatch(
showTenant(tenentInfo[0].tenentName || "")
);
localStorage.setItem(
"TenantName",
@@ -433,21 +412,8 @@ const PgSignUp = (props) => {
<div className="bg-white">
<Title title={"Pgsignup page"} />
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="flex justify-center items-center h-[100vh]">
<Loader />
</div>
) : (
<form id="signup" className="pgsignup-content" onSubmit={handleSubmit}>
@@ -537,15 +503,4 @@ const PgSignUp = (props) => {
);
};
const mapStateToProps = (state) => {
if (Object.keys(state.appInfo).length !== 0) {
return { appInfo: state.appInfo, isloginVisible: true };
} else {
return { appInfo: state.appInfo, isloginVisible: false };
}
};
export default connect(mapStateToProps, {
fetchAppInfo,
showTenantName
})(PgSignUp);
export default PgSignUp;
File diff suppressed because it is too large Load Diff
+133 -74
View File
@@ -1,9 +1,11 @@
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router";
import { NavLink } from "react-router-dom";
import { NavLink, useNavigate } from "react-router-dom";
import checkmark from "../assets/images/checkmark.png";
import plansArr from "../json/plansArr.json";
import plansArr from "../json/plansArr";
import Title from "../components/Title";
import Parse from "parse";
import { openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
const listItemStyle = {
paddingLeft: "20px", // Add padding to create space for the image
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
@@ -16,18 +18,34 @@ const PlanSubscriptions = () => {
const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(false);
const [isLoader, setIsLoader] = useState(true);
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const user = {
name: extUser?.Name,
email: extUser?.Email,
company: extUser?.Company,
phone: extUser?.Phone
};
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
// console.log("userDetails ", userDetails);
const name =
userDetails && userDetails.name ? "first_name=" + userDetails.name : "";
userDetails && userDetails.name
? "first_name=" + encodeURIComponent(userDetails.name)
: "";
const email =
userDetails && userDetails.email ? "&email=" + userDetails.email : "";
userDetails && userDetails.email
? "&email=" + encodeURIComponent(userDetails.email)
: "";
const company =
userDetails && userDetails.company
? "&company_name=" + userDetails.company
? "&company_name=" + encodeURIComponent(userDetails.company)
: "";
const phone =
userDetails && userDetails.phone ? "&mobile=" + userDetails.phone : "";
userDetails && userDetails.phone
? "&mobile=" + encodeURIComponent(userDetails.phone)
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
name +
@@ -35,57 +53,72 @@ const PlanSubscriptions = () => {
company +
phone;
useEffect(() => {
if (localStorage.getItem("accesstoken")) {
setIsLoader(false);
setYearlyVisible(false);
} else {
navigate("/", { replace: true });
}
// if (localStorage.getItem("accesstoken")) {
setIsLoader(false);
// } else {
// navigate("/", { replace: true });
// }
// eslint-disable-next-line
}, []);
const handleFreePlan = async () => {
setIsLoader(true);
try {
const params = { userId: Parse.User.current().id };
const res = await Parse.Cloud.run("freesubscription", params);
if (res.status === "success" && res.result === "already subscribed!") {
setIsLoader(false);
alert("You have already subscribed to plan!");
} else if (res.status === "success") {
setIsLoader(false);
navigate("/");
} else if (res.status === "error") {
setIsLoader(false);
alert(res.result);
}
} catch (err) {
setIsLoader(false);
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
}
};
return (
<>
<Title title={"Subscriptions"} />
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="flex items-center justify-center h-screen">
<Loader />
</div>
) : (
<div
style={{
backgroundColor: "white",
overflowY: "auto",
maxHeight: "100%",
"--theme-color": "#7952b3",
"--plan-width": 30
}}
>
<div
id="monthlyPlans"
className={`${yearlyVisible ? "none" : "block my-2"}`}
>
<div className="flex justify-center w-full my-2">
<ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300">
<div className="overflow-y-auto max-h-full">
<div className="block my-2">
<div className="flex flex-col justify-center items-center w-full">
<div
role="tablist"
className="op-tabs op-tabs-boxed bg-base-100 my-2 shadow-lg transition-all"
>
<a
onClick={() => setYearlyVisible(false)}
role="tab"
className={`${!yearlyVisible ? "op-tab-active" : ""} op-tab`}
>
Monthly
</a>
<a
onClick={() => setYearlyVisible(true)}
role="tab"
className={`${yearlyVisible ? "op-tab-active" : ""} op-tab`}
>
Yearly (10% off)
</a>
</div>
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
{plansArr.map((item) => (
<li
className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]"
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
key={item.planName}
>
<div className="p-2 flex flex-col justify-center items-center">
<div className="p-2 flex flex-col justify-center items-center max-h-[310px]">
<h3 className="text-[#002862] uppercase">
{item.planName}
</h3>
@@ -96,20 +129,20 @@ const PlanSubscriptions = () => {
alt="freeimg"
/>
</div>
<div className="">
<div>
<span className="text-3xl">
{item.currency && <small>{item.currency}</small>}
{item.price}
{yearlyVisible
? item?.yearlyPrice
: item.monthlyPrice}
</span>
<div
className={`${
item.subtitle.length <= 32
? "w-[150px] text-center"
: ""
} text-sm text-center my-2`}
>
<p
<p className="font-semibold pt-2 text-sm">
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
</p>
<div className="max-w-[250px] h-[40px] text-center text-sm my-2">
<div
style={{
textAlign: "center",
backgroundColor: item.subtitlecolor
? item.subtitlecolor
: "white"
@@ -124,29 +157,37 @@ const PlanSubscriptions = () => {
) : (
<span>{item.subtitle}</span>
)}
</p>
</div>
</div>
</div>
<NavLink
to={
item.btnText === "Subscribe"
? item.url + details
: item.url
}
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white"
target={item.target}
>
{item.btnText}
</NavLink>
{item.url ? (
<NavLink
to={
item.btnText === "Subscribe"
? yearlyVisible
? item.yearlyUrl + details
: item.url + details
: item.url
}
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
target={item.target}
>
{item.btnText}
</NavLink>
) : (
<button
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:text-white cursor-pointer"
onClick={() => handleFreePlan()}
>
{item.btnText}
</button>
)}
</div>
<hr className="w-full bg-gray-300 p-[.5px]" />
<hr className="w-full bg-gray-300 h-[0.5px]" />
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
{item.benefits.map((subitem, index) => (
<li style={listItemStyle} key={index} className="m-1">
<span style={{ position: "relative" }}>
{subitem}
</span>
<span className="relative">{subitem}</span>
</li>
))}
</ul>
@@ -154,6 +195,24 @@ const PlanSubscriptions = () => {
))}
</ul>
</div>
<div className="text-sm flex justify-center items-center mt-3 mb-2">
<hr className="border-[2px] border-gray-350 w-[20%]" />
<span className="px-2 text-base-content cursor-default">or</span>
<hr className="border-[2px] border-gray-350 w-[20%]" />
</div>
<div className="flex flex-col justify-center items-center">
<h3 className="text-[#002862] mt-1 mb-2">
Host it yourself for free
</h3>
<div
className="op-btn op-btn-primary w-[200px]"
onClick={() =>
openInNewTab("https://github.com/OpenSignLabs/OpenSign")
}
>
Visit Github
</div>
</div>
</div>
</div>
)}
+58 -21
View File
@@ -6,17 +6,22 @@ import reportJson from "../json/ReportJson";
import { useParams } from "react-router-dom";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Loader from "../primitives/Loader";
const Report = () => {
const { id } = useParams();
const [List, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true);
const [reportName, setReportName] = useState("");
const [reporthelp, setReportHelp] = useState("");
const [actions, setActions] = useState([]);
const [heading, setHeading] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true);
const [form, setForm] = useState("");
const [tourData, setTourData] = useState([]);
const [isDontShow, setIsDontShow] = useState(false);
const abortController = new AbortController();
const docPerPage = 10;
@@ -45,6 +50,9 @@ const Report = () => {
// eslint-disable-next-line
}, [isNextRecord]);
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const getReportData = async (skipUserRecord = 0, limit = 200) => {
// setIsLoader(true);
const json = reportJson(id);
@@ -53,6 +61,7 @@ const Report = () => {
setHeading(json.heading);
setReportName(json.reportName);
setForm(json.form);
setReportHelp(json?.helpMsg);
const currentUser = Parse.User.current().id;
const headers = {
@@ -67,6 +76,42 @@ const Report = () => {
headers: headers,
signal: abortController.signal // is used to cancel fetch query
});
if (id === "6TeaPr321t") {
const tourConfig = [
{
selector: "[data-tut=reactourFirst]",
content: () => (
<TourContentWithBtn
message={
"Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like HR Manager and New Employee. Each time you use the template, you can assign the New Employee role to different incoming staff members, while the HR Manager role remains constant, facilitating a seamless onboarding process for each recruit. "
}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
];
if (res.data.result && res.data.result?.length > 0) {
json.actions.map((data) => {
const newConfig = {
selector: `[data-tut="${data?.selector}"]`,
content: () => (
<TourContentWithBtn
message={data?.message}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
};
tourConfig.push(newConfig);
});
}
setTourData(tourConfig);
}
if (id === "4Hhwbp482K") {
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
let arr = [];
@@ -104,12 +149,14 @@ const Report = () => {
} else {
setIsMoreDocs(false);
}
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
if (!res.data.result.error) {
setIsNextRecord(false);
setList((prevRecord) =>
prevRecord.length > 0
? [...prevRecord, ...res.data.result]
: res.data.result
);
}
}
setIsLoader(false);
} catch (err) {
@@ -127,21 +174,8 @@ const Report = () => {
<>
<Title title={reportName} />
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="h-[100vh] flex justify-center items-center">
<Loader />
</div>
) : (
<>
@@ -156,6 +190,9 @@ const Report = () => {
isMoreDocs={isMoreDocs}
docPerPage={docPerPage}
form={form}
report_help={reporthelp}
tourData={tourData}
isDontShow={isDontShow}
/>
) : (
<PageNotFound prefix={"Report"} />
+384
View File
@@ -0,0 +1,384 @@
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Parse from "parse";
import axios from "axios";
import { appInfo } from "../constant/appinfo";
import { isEnableSubscription } from "../constant/const";
import { fetchSubscription } from "../constant/Utils";
import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
const SSOVerify = () => {
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
const [isModal, setIsModal] = useState(false);
const [message, setMessage] = useState("Verifying SSO...");
const [isLoader, setIsLoader] = useState(false);
const [userDetails, setUserDetails] = useState({
name: "",
email: "",
phone: "",
Destination: "",
Company: ""
});
useEffect(() => {
linkUserWithSSO();
// eslint-disable-next-line
}, []);
// `linkUserWithSSO` is used to sign in or sign up a user using an SSO code and check if the user is present in the extended class
const linkUserWithSSO = async () => {
const param = new URLSearchParams(location.search);
const code = param?.get("code");
const state = param?.get("state");
try {
// The `ssosign` cloud function is used to sign in or sign up a user
const ssosign = await Parse.Cloud.run("ssosign", {
code: code,
email: state
});
localStorage.setItem("accesstoken", ssosign.sessiontoken);
// `checkExtUser` checks if the user is present in the extended class `contracts_Users` and if not, initiates the new user flow
await checkExtUser(ssosign);
} catch (err) {
setMessage("Error: " + err.message);
console.log("err", err.message);
}
};
const checkExtUser = async (ssosign) => {
const params = { email: ssosign?.email };
try {
// `isextenduser` checks if the current user is present in the extended class
const extRes = await Parse.Cloud.run("isextenduser", params);
if (extRes?.isUserExist) {
// `getUserDetails` retrieves the current user's details from the extended class
const extRes = await Parse.Cloud.run("getUserDetails", params);
if (ssosign && ssosign.sessiontoken) {
const LocalUserDetails = {
name: extRes.Name,
email: extRes.email,
phone: extRes?.get("Phone") || "",
company: extRes.get("Company")
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(ssosign.sessiontoken);
}
} else {
setIsModal(true);
setUserDetails((prev) => ({
...prev,
name: ssosign.name,
email: ssosign.email,
phone: ssosign?.phone || ""
}));
}
} catch (err) {
console.log("Err in isextenduser or getuserdetails cloud function", err);
setMessage("Error: " + err.message);
}
};
// `handleSubmitbtn` is used to create a user in the extended class
const handleSubmitbtn = async (e) => {
e.preventDefault();
setIsLoader(true);
let phone;
if (userDetails?.phone) {
phone = validateInput(userDetails?.phone);
} else {
phone = true;
}
if (userDetails.Destination && userDetails.Company && phone) {
const payload = { sessionToken: localStorage.getItem("accesstoken") };
if (payload && payload.sessionToken) {
const params = {
userDetails: {
name: userDetails.name,
email: userDetails.email,
phone: userDetails?.phone || "",
role: "contracts_User",
company: userDetails.Company,
jobTitle: userDetails.Destination
}
};
try {
const userSignUp = await Parse.Cloud.run("usersignup", params);
if (userSignUp && userSignUp.sessionToken) {
const LocalUserDetails = params.userDetails;
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
await thirdpartyLoginfn(userSignUp.sessionToken);
setIsLoader(false);
} else {
alert(userSignUp.message);
setIsLoader(false);
}
} catch (err) {
console.log("error in usersignup", err);
localStorage.removeItem("accesstoken");
alert("Something went wrong.");
setIsLoader(false);
}
} else {
localStorage.removeItem("accesstoken");
alert("Internal server error.");
setIsLoader(false);
}
} else {
alert("Please fill required details correctly.");
setIsLoader(false);
}
};
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
const thirdpartyLoginfn = async (sessionToken) => {
const baseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
try {
const validUser = await Parse.User.become(sessionToken);
if (validUser) {
localStorage.setItem("accesstoken", sessionToken);
const _user = JSON.parse(JSON.stringify(validUser));
localStorage.setItem("UserInformation", JSON.stringify(_user));
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
let userRoles = [];
const userSettings = appInfo.settings;
//Get Current user roles
const url = `${baseUrl}functions/UserGroups`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: _user.sessionToken
};
const body = { appname: appInfo.appname };
const UserGroupsRes = await axios.post(url, JSON.stringify(body), {
headers: headers
});
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || [];
if (userRoles) {
let _currentRole = "";
const excludeRoles = ["contracts_Guest", `contracts_appeditor`];
const filteredRole = userRoles.filter(
(x) => !excludeRoles.includes(x)
);
if (filteredRole?.length > 1) {
console.log("user has two roles");
// _currentRole = filteredRole.filter((x) => x === "contracts_User");
} else {
_currentRole = filteredRole?.[0] || "";
}
if (_currentRole) {
const roleSetting = userSettings?.find(
(setting) => setting.role === _currentRole
);
const redirectUrl =
location?.state?.from ||
`/${roleSetting.pageType}/${roleSetting.pageId}`;
const _role = _currentRole.replace(`${appInfo.appname}_`, "");
localStorage.setItem("_user_role", _role);
// Get TenentID from Extendend Class
localStorage.setItem(
"extended_class",
roleSetting.extended_class
);
try {
const extUser = await Parse.Cloud.run("getUserDetails", {
email: _user.email
});
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", _extUser.Email);
localStorage.setItem("username", _extUser.Name);
localStorage.setItem("scriptId", true);
let tenentInfo = [];
const results = [extUser];
if (results) {
let extendedInfo_stringify = JSON.stringify(results);
localStorage.setItem(
"Extand_Class",
extendedInfo_stringify
);
if (_extUser.TenantId) {
const obj = {
tenentId: _extUser.TenantId.objectId,
tenentName: _extUser.TenantId.TenantName || ""
};
localStorage.setItem("TenantId", obj.tenentId);
tenentInfo.push(obj);
dispatch(showTenant(obj.tenentName || ""));
localStorage.setItem("TenantName", obj.tenentName || "");
}
localStorage.setItem("PageLanding", roleSetting.pageId);
localStorage.setItem("defaultmenuid", roleSetting.menuId);
localStorage.setItem("pageType", roleSetting.pageType);
if (isEnableSubscription) {
const res = await fetchSubscription();
const plan = res.plan;
const billingDate = res.billingDate;
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
localStorage.removeItem("userDetails");
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
navigate(`/subscription`, {
replace: true
});
}
} else {
navigate(redirectUrl);
}
}
}
} catch (err) {
alert("user not exist.");
setMessage("Error: User not exist.");
console.log("err in get extUser", err);
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} else {
alert("Role does not exists.");
setMessage("Error: Role does not exists.");
}
} catch (err) {
console.log("err in usergroups", err);
}
}
} catch (err) {
console.log("Err in become method", err);
}
};
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
const handleCloseModal = () => {
setIsModal(false);
if (Parse?.User?.current()) {
Parse.User.logOut();
}
};
// `validateInput` is used to verify the phone pattern
function validateInput(input) {
if (input) {
const pattern = /^(?!.*\+.*\+)[\d+-]*$/;
return pattern.test(input);
} else {
return true;
}
}
return (
<div>
<div className="w-full h-screen flex flex-col justify-center items-center text-sm md:text-xl ">
{message === "Verifying SSO..." && <Loader />}
<div className="text-base-content">{message}</div>
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<div className="relative">
{isLoader && (
<div className="absolute w-full h-full bg-black bg-opacity-25 flex justify-center items-center">
<Loader />
</div>
)}
<form
className="px-4 py-3 text-base-content"
onSubmit={handleSubmitbtn}
>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</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"
id="Phone"
value={userDetails.phone}
onChange={(e) =>
setUserDetails({
...userDetails,
phone: e.target.value
})
}
disabled={isLoader}
required
/>
<p className="text-[10px] text-[red] ml-2">
{!validateInput(userDetails.phone) && "Invalid phone value"}
</p>
</div>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</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"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title
<span className="text-[13px] text-[red]">*</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"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
disabled={isLoader}
required
/>
</div>
<div className="mt-4">
<button type="submit" className="op-btn op-btn-primary">
Login
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-2"
onClick={handleCloseModal}
>
Cancel
</button>
</div>
</form>
</div>
</ModalUi>
</div>
);
};
export default SSOVerify;
+145 -106
View File
@@ -1,23 +1,30 @@
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import Parse from "parse";
import axios from "axios";
import Title from "../components/Title";
import { fetchAppInfo, showTenantName } from "../redux/actions";
import { useNavigate, NavLink } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize";
import Alert from "../primitives/Alert";
const Signup = (props) => {
import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import Loader from "../primitives/Loader";
const Signup = () => {
const { width } = useWindowSize();
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
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 [image, setImage] = useState();
const [state, setState] = useState({
loading: false,
alertType: "success",
@@ -27,9 +34,8 @@ const Signup = (props) => {
const [lengthValid, setLengthValid] = useState(false);
const [caseDigitValid, setCaseDigitValid] = useState(false);
const [specialCharValid, setSpecialCharValid] = useState(false);
const [isAuthorize, setIsAuthorize] = useState(false);
const togglePasswordVisibility = () => setShowPassword(!showPassword);
const image = props.appInfo.applogo;
const clearStorage = async () => {
if (Parse.User.current()) {
@@ -65,6 +71,19 @@ const Signup = (props) => {
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
};
const handleFreePlan = async (id) => {
try {
const params = { userId: id };
const res = await Parse.Cloud.run("freesubscription", params);
if (res.status === "error") {
alert(res.result);
}
} catch (err) {
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
}
};
const handleSubmit = (event) => {
event.preventDefault();
if (lengthValid && caseDigitValid && specialCharValid) {
@@ -90,7 +109,7 @@ const Signup = (props) => {
res
.then(async (r) => {
if (r) {
let roleData = props.appInfo.settings;
let roleData = appInfo.settings;
if (roleData && roleData.length > 0) {
const params = {
userDetails: {
@@ -99,7 +118,7 @@ const Signup = (props) => {
name: name,
email: email,
phone: phone,
role: props.appInfo.defaultRole
role: appInfo.defaultRole
}
};
try {
@@ -108,7 +127,13 @@ const Signup = (props) => {
params
);
if (usersignup) {
handleNavigation(r.getSessionToken());
const param = new URLSearchParams(location.search);
const isFreeplan =
param?.get("subscription") === "freeplan";
if (isFreeplan) {
await handleFreePlan(r.id);
}
handleNavigation(r.getSessionToken(), isFreeplan);
}
} catch (err) {
alert(err.message);
@@ -153,7 +178,7 @@ const Signup = (props) => {
}
};
const handleNavigation = async (sessionToken) => {
const handleNavigation = async (sessionToken, isFreeplan = false) => {
const baseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
const res = await axios.get(baseUrl + "users/me", {
@@ -181,8 +206,8 @@ const Signup = (props) => {
// Check extended class user role and tenentId
try {
let userRoles = [];
if (props.appInfo.settings) {
let userSettings = props.appInfo.settings;
if (appInfo.settings) {
let userSettings = appInfo.settings;
//Get Current user roles
let url = `${baseUrl}functions/UserGroups`;
@@ -256,8 +281,8 @@ const Signup = (props) => {
}
});
if (tenentInfo.length) {
props.showTenantName(
tenentInfo[0].tenentName || ""
dispatch(
showTenant(tenentInfo[0].tenentName || "")
);
localStorage.setItem(
"TenantName",
@@ -293,15 +318,15 @@ const Signup = (props) => {
tenentName: x.TenantId.TenantName || ""
};
localStorage.setItem(
"TenetId",
"TenantId",
x.TenantId.objectId
);
tenentInfo.push(obj);
}
});
if (tenentInfo.length) {
props.showTenantName(
tenentInfo[0].tenentName || ""
dispatch(
showTenant(tenentInfo[0].tenentName || "")
);
localStorage.setItem(
"TenantName",
@@ -321,8 +346,14 @@ const Signup = (props) => {
element.pageType
);
setState({ loading: false });
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
navigate(`/subscription`, { replace: true });
if (isEnableSubscription) {
if (isFreeplan) {
navigate(
`/${element.pageType}/${element.pageId}`
);
} else {
navigate(`/subscription`, { replace: true });
}
} else {
alert("Registered user successfully");
navigate(
@@ -339,8 +370,14 @@ const Signup = (props) => {
);
localStorage.setItem("pageType", element.pageType);
setState({ loading: false });
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
navigate(`/subscription`, { replace: true });
if (isEnableSubscription) {
if (isFreeplan) {
navigate(
`/${element.pageType}/${element.pageId}`
);
} else {
navigate(`/subscription`, { replace: true });
}
} else {
navigate(
`/${element.pageType}/${element.pageId}`
@@ -433,10 +470,24 @@ const Signup = (props) => {
}
};
useEffect(() => {
props.fetchAppInfo();
dispatch(fetchAppInfo());
saveLogo();
// eslint-disable-next-line
}, []);
const saveLogo = async () => {
if (isEnableSubscription) {
const logo = await getAppLogo();
if (logo) {
setImage(logo);
} else {
setImage(appInfo?.applogo || undefined);
}
} else {
setImage(appInfo?.applogo || undefined);
}
};
const handlePasswordChange = (e) => {
const newPassword = e.target.value;
setPassword(newPassword);
@@ -451,78 +502,58 @@ const Signup = (props) => {
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
};
return (
<div className="bg-white">
<div className="">
{state.loading && (
<div
style={{
position: "fixed",
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0,0,0,0.2)",
top: 0,
left: 0,
zIndex: 2
}}
>
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
<div className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50">
<Loader />
</div>
)}
<Title title={"Signup page"} />
{props.isloginVisible && props.isloginVisible ? (
<div>
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
<div className="w-[250px] h-[66px] inline-block">
<img src={image} width="100%" alt="" />
{appInfo && appInfo.applogo ? (
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
{image && (
<img src={image} className="object-contain h-full" alt="logo" />
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div className="">
<div>
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Create Account !</h2>
<div className="outline outline-1 outline-slate-300/50 shadow-md rounded my-4">
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="px-6 py-4 text-xs">
<label className="block ">
Name{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
Name <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<hr className="my-2 border-none" />
<label>
Email{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
Email <span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
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)}
onChange={(e) =>
setEmail(e.target.value?.toLowerCase())
}
required
/>
<hr className="my-2 border-none" />
<label>
Phone{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
Phone <span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
@@ -530,11 +561,11 @@ const Signup = (props) => {
<hr className="my-2 border-none" />
<label>
Company{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={company}
onChange={(e) => setCompany(e.target.value)}
required
@@ -542,11 +573,11 @@ const Signup = (props) => {
<hr className="my-2 border-none" />
<label>
Job Title{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={jobTitle}
onChange={(e) => setJobTitle(e.target.value)}
required
@@ -554,21 +585,19 @@ const Signup = (props) => {
<hr className="my-2 border-none" />
<label>
Password{" "}
<span style={{ color: "red", fontSize: 13 }}>*</span>
<span className="text-[red] text-[13px]">*</span>
</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="password"
value={password}
onChange={(e) => handlePasswordChange(e)}
required
/>
<span
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
showPassword ? "text-[#007bff]" : "text-black"
}`}
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
onClick={togglePasswordVisibility}
>
{showPassword ? (
@@ -606,34 +635,62 @@ const Signup = (props) => {
{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>
</div>
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
<button
type="submit"
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow uppercase"
className="op-btn op-btn-primary"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Register"}
</button>
<NavLink
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
to="/"
style={width < 768 ? { textAlign: "center" } : {}}
<button
type="button"
className="op-btn op-btn-secondary"
disabled={state.loading}
onClick={() =>
navigate(location.search ? "/" + location.search : "/")
}
>
Login
</NavLink>
</button>
</div>
</form>
</div>
{width >= 768 && (
<div className="self-center">
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
<img src={login_img} alt="bisec" width="100%" />
<img src={login_img} alt="loader" width="100%" />
</div>
</div>
)}
@@ -642,30 +699,12 @@ const Signup = (props) => {
<Alert type={state.alertType}>{state.alertMsg}</Alert>
</div>
) : (
<div
style={{
position: "fixed",
fontSize: "50px",
color: "#3ac9d6",
top: "50%",
left: "45%"
}}
className="loader-37"
></div>
<div className="fixed w-full h-full flex justify-center items-center z-50">
<Loader />
</div>
)}
</div>
);
};
const mapStateToProps = (state) => {
if (Object.keys(state.appInfo).length !== 0) {
return { appInfo: state.appInfo, isloginVisible: true };
} else {
return { appInfo: state.appInfo, isloginVisible: false };
}
};
export default connect(mapStateToProps, {
fetchAppInfo,
showTenantName
})(Signup);
export default Signup;
+301 -178
View File
@@ -1,10 +1,11 @@
import React, { useState, useRef, useEffect } from "react";
import { PDFDocument } from "pdf-lib";
import "../styles/signature.css";
import { themeColor } from "../constant/const";
import Parse from "parse";
import { isEnableSubscription } from "../constant/const";
import Confetti from "react-confetti";
import axios from "axios";
import Loader from "../primitives/LoaderWithMsg";
import loader from "../assets/images/loader2.gif";
import LoaderWithMsg from "../primitives/LoaderWithMsg";
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
@@ -26,7 +27,13 @@ import {
contactBook,
randomId,
getDate,
textInputWidget
textWidget,
getTenantDetails,
checkIsSubscribed,
convertPdfArrayBuffer,
fetchImageBase64,
changeImageWH,
handleSendOTP
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -39,7 +46,8 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Title from "../components/Title";
import ModalUi from "../primitives/ModalUi";
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import VerifyEmail from "../components/pdf/VerifyEmail";
import Loader from "../primitives/Loader";
//For signYourself inProgress section signer can add sign and complete doc sign.
function SignYourSelf() {
const [pdfDetails, setPdfDetails] = useState([]);
@@ -62,12 +70,10 @@ function SignYourSelf() {
const [dragKey, setDragKey] = useState();
const [signKey, setSignKey] = useState();
const [imgWH, setImgWH] = useState({});
const [isCeleb, setIsCeleb] = useState(false);
const [pdfNewWidth, setPdfNewWidth] = useState();
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
const [successEmail, setSuccessEmail] = useState(false);
const imageRef = useRef(null);
const [documentStatus, setDocumentStatus] = useState({ isCompleted: false });
const [myInitial, setMyInitial] = useState("");
const [isInitial, setIsInitial] = useState(false);
const [isUiLoading, setIsUiLoading] = useState(false);
@@ -87,10 +93,11 @@ function SignYourSelf() {
const [containerWH, setContainerWH] = useState({});
const [isPageCopy, setIsPageCopy] = useState(false);
const [selectWidgetId, setSelectWidgetId] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
status: false
});
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
const [isCheckbox, setIsCheckbox] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [pdfLoadFail, setPdfLoadFail] = useState({
@@ -99,6 +106,14 @@ function SignYourSelf() {
});
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isDontShow, setIsDontShow] = useState(false);
const [extUserId, setExtUserId] = useState("");
const [isCompleted, setIsCompleted] = useState(false);
const [isCelebration, setIsCelebration] = useState(false);
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const [activeMailAdapter, setActiveMailAdapter] = useState("");
const [isEmailVerified, setIsEmailVerified] = useState(true);
const [isVerifyModal, setIsVerifyModal] = useState(false);
const [otp, setOtp] = useState("");
const divRef = useRef(null);
const nodeRef = useRef(null);
const [, drop] = useDrop({
@@ -180,19 +195,29 @@ function SignYourSelf() {
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (showComplete) => {
let isCompleted;
//getting document details
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
setPdfDetails(documentData);
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const url = documentData[0] && documentData[0]?.URL;
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError("Error: Something went wrong!");
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError("Error: Something went wrong!");
}
const isCompleted =
documentData[0].IsCompleted && documentData[0].IsCompleted;
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
if (isCompleted) {
const docStatus = {
isCompleted: isCompleted
};
setDocumentStatus(docStatus);
setIsCompleted(true);
setPdfUrl(documentData[0].SignedUrl);
const alreadySign = {
status: true,
@@ -204,8 +229,12 @@ function SignYourSelf() {
setIsUiLoading(false);
setIsSignPad(false);
setIsEmail(true);
setIsCelebration(true);
setXyPostion([]);
setSignBtnPosition([]);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
}
}
} else if (
@@ -255,16 +284,20 @@ function SignYourSelf() {
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
});
const contractUsersRes = await contractUsers(jsonSender.email);
if (contractUsersRes[0] && contractUsersRes.length > 0) {
if (contractUsersRes === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
setContractName("_Users");
setSignerUserId(contractUsersRes[0].objectId);
const tourstatuss =
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0) {
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
(data) => data.signyourself
@@ -272,17 +305,13 @@ function SignYourSelf() {
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].signyourself);
}
} else {
setCheckTourStatus(true);
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (contractUsersRes === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else if (contractUsersRes.length === 0) {
const contractContactBook = await contactBook(jsonSender.objectId);
if (contractContactBook && contractContactBook.length > 0) {
@@ -291,7 +320,8 @@ function SignYourSelf() {
const tourstatuss =
contractContactBook[0].TourStatus &&
contractContactBook[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0) {
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
(data) => data.signyourself
@@ -299,6 +329,8 @@ function SignYourSelf() {
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].signyourself);
}
} else {
setCheckTourStatus(true);
}
} else {
setHandleError("No Data Found!");
@@ -342,8 +374,7 @@ function SignYourSelf() {
return {
name: "checkbox"
};
case textInputWidget:
case textWidget:
return {
name: "text"
};
@@ -514,89 +545,213 @@ function SignYourSelf() {
setSignKey(key);
};
//`handleResend` function is used to resend otp for email verification
const handleResend = async (e) => {
e.preventDefault();
setOtpLoader(true);
await handleSendOTP(Parse.User.current().getEmail());
setOtpLoader(false);
alert("OTP sent on you email");
};
//`handleVerifyEmail` function is used to verify email with otp
const handleVerifyEmail = async (e) => {
e.preventDefault();
setOtpLoader(true);
try {
const resEmail = await Parse.Cloud.run("verifyemail", {
otp: otp,
email: Parse.User.current().getEmail()
});
if (resEmail?.message === "Email is verified.") {
setIsEmailVerified(true);
} else if (resEmail?.message === "Email is already verified.") {
setIsEmailVerified(true);
}
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
// handleRecipientSign();
} catch (error) {
alert(error.message);
} finally {
setOtpLoader(false);
}
};
//`handleVerifyBtn` function is used to send otp on user mail
const handleVerifyBtn = async () => {
setIsVerifyModal(true);
await handleSendOTP(Parse.User.current().getEmail());
};
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
async function embedWidgetsData() {
let showAlert = false;
for (let i = 0; i < xyPostion?.length; i++) {
const requiredWidgets = xyPostion[i].pos.filter(
(position) => position.type !== "checkbox"
);
if (requiredWidgets && requiredWidgets?.length > 0) {
let checkSigned;
for (let i = 0; i < requiredWidgets?.length; i++) {
checkSigned = requiredWidgets[i]?.options?.response;
if (!checkSigned) {
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
let checkDefaultSigned = requiredWidgets[i]?.options?.defaultValue;
if (!checkSignUrl) {
if (!checkDefaultSigned) {
if (!showAlert) {
showAlert = true;
//check current user email is verified or not
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!");
}
}
if (isEmailVerified) {
let showAlert = false,
isSignatureExist = false;
try {
for (let i = 0; i < xyPostion?.length; i++) {
const requiredWidgets = xyPostion[i].pos.filter(
(position) => position.type !== "checkbox"
);
if (requiredWidgets && requiredWidgets?.length > 0) {
let checkSigned;
for (let i = 0; i < requiredWidgets?.length; i++) {
checkSigned = requiredWidgets[i]?.options?.response;
if (!checkSigned) {
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
let checkDefaultSigned =
requiredWidgets[i]?.options?.defaultValue;
if (!checkSignUrl) {
if (!checkDefaultSigned) {
if (!showAlert) {
showAlert = true;
}
}
}
}
}
}
//condition to check exist signature widget or not
if (!isSignatureExist) {
isSignatureExist = xyPostion[i].pos.some(
(data) => data?.type === "signature"
);
}
}
if (xyPostion.length === 0 || !isSignatureExist) {
setIsAlert({
header: "Fields required",
isShow: true,
alertMessage:
"Please ensure there's at least one signature widget added"
});
return;
} else if (showAlert) {
setIsAlert({
isShow: true,
alertMessage:
"Please ensure all field is accurately filled and meets all requirements."
});
return;
} else {
setIsUiLoading(true);
const existingPdfBytes = pdfArrayBuffer;
// Load a PDFDocument from the existing PDF bytes
try {
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const isSignYourSelfFlow = true;
const extUserPtr = pdfDetails[0].ExtUserPtr;
const HeaderDocId = extUserPtr?.HeaderDocId;
//embed document's object id to all pages in pdf document
if (!HeaderDocId) {
await embedDocId(pdfDoc, documentId, allPages);
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
xyPostion,
pdfDoc,
pdfOriginalWidth,
isSignYourSelfFlow,
containerWH
);
// console.log("pdf", pdfBytes);
//function for call to embed signature in pdf and get digital signature pdf
await signPdfFun(pdfBytes, documentId);
} catch (err) {
setIsUiLoading(false);
if (err && err.message.includes("is encrypted.")) {
setIsAlert({
isShow: true,
alertMessage: `Currently encrypted pdf files are not supported.`
});
} else {
console.log("err in signing", err);
setIsAlert({
isShow: true,
alertMessage: `Something went wrong.`
});
}
}
}
} catch (err) {
console.log("err in embedselfsign ", err);
setIsUiLoading(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong, please try again later."
});
}
}
if (xyPostion.length === 0) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
return;
} else if (showAlert) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
return;
} else {
setIsCeleb(true);
setTimeout(() => {
setIsCeleb(false);
}, 3000);
setIsUiLoading(true);
const url = pdfDetails[0] && pdfDetails[0].URL;
const existingPdfBytes = await fetch(url).then((res) =>
res.arrayBuffer()
);
// Load a PDFDocument from the existing PDF bytes
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true
});
const flag = true;
const extUserPtr = pdfDetails[0].ExtUserPtr;
const HeaderDocId = extUserPtr?.HeaderDocId;
//embed document's object id to all pages in pdf document
if (!HeaderDocId) {
await embedDocId(pdfDoc, documentId, allPages);
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
xyPostion,
pdfDoc,
pdfOriginalWidth,
flag,
containerWH
);
// console.log('pdf',pdfBytes)
//function for call to embed signature in pdf and get digital signature pdf
await signPdfFun(pdfBytes, documentId);
}
}
//function for get digital signature
const signPdfFun = async (base64Url, documentId) => {
let isCustomCompletionMail = false;
const getIsSubscribe = await checkIsSubscribed();
const tenantDetails = await getTenantDetails(jsonSender.objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert("User does not exist");
} else {
if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject &&
(!isEnableSubscription || getIsSubscribe)
) {
isCustomCompletionMail = true;
}
}
// below for loop is used to get first signature of user to send if to signpdf
// for adding it in completion certificate
let getSignature;
for (let item of xyPostion) {
if (!getSignature) {
const typeExist = item.pos.some((data) => data?.type);
if (typeExist) {
getSignature = item.pos.find((data) => data?.type === "signature");
} else {
getSignature = item.pos.find((data) => !data.isStamp);
}
}
}
let base64Sign = getSignature.SignUrl;
//check https type signature (default signature exist) then convert in base64
const isUrl = base64Sign.includes("https");
if (isUrl) {
try {
base64Sign = await fetchImageBase64(base64Sign);
} catch (e) {
console.log("error", e);
}
}
//change image width and height to 104/44 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
let singleSign = {
pdfFile: base64Url,
docId: documentId
docId: documentId,
isCustomCompletionMail: isCustomCompletionMail,
mailProvider: activeMailAdapter,
signature: suffixbase64
};
await axios
@@ -604,7 +759,8 @@ function SignYourSelf() {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
// sessionToken: localStorage.getItem("accesstoken")
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
})
.then((Listdata) => {
@@ -705,7 +861,7 @@ function SignYourSelf() {
};
//function for save button to save signature or image url
const saveSign = (isDefaultSign, width, height) => {
const saveSign = (type, isDefaultSign, width, height) => {
const isTypeText = width && height ? true : false;
const signatureImg = isDefaultSign
? isDefaultSign === "initials"
@@ -728,6 +884,7 @@ function SignYourSelf() {
}
}
const getUpdatePosition = onSaveSign(
type,
xyPostion,
index,
signKey,
@@ -764,6 +921,7 @@ function SignYourSelf() {
//function for delete signature block
const handleDeleteSign = (key) => {
setCurrWidgetsDetails({});
const updateResizeData = [];
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
@@ -934,38 +1092,41 @@ function SignYourSelf() {
<DndProvider backend={HTML5Backend}>
<Title title={"Self Sign"} />
{isLoading.isLoad ? (
<Loader isLoading={isLoading} />
<LoaderWithMsg isLoading={isLoading} />
) : handleError ? (
<HandleError handleError={handleError} />
) : (
<div>
{isUiLoading && (
<div
style={{
position: "absolute",
height: "100vh",
width: "100%",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignItems: "center",
zIndex: "999",
backgroundColor: "#e6f2f2",
opacity: 0.8
}}
>
<img
alt="no img"
src={loader}
style={{ width: "100px", height: "100px" }}
/>
<span style={{ fontSize: "13px", fontWeight: "bold" }}>
<div className="absolute h-[100vh] w-full z-[999] flex flex-col justify-center items-start bg-[#e6f2f2] bg-opacity-80">
<Loader />
<span className="font-bold text-[13px]">
This might take some time
</span>
</div>
)}
{isCelebration && (
<div className="relative z-[999]">
<Confetti width={window.innerWidth} height={window.innerHeight} />
</div>
)}
<div className="signatureContainer" ref={divRef}>
<div
className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"
ref={divRef}
>
{!isEmailVerified && (
<VerifyEmail
isVerifyModal={isVerifyModal}
setIsVerifyModal={setIsVerifyModal}
handleVerifyEmail={handleVerifyEmail}
setOtp={setOtp}
otp={otp}
otpLoader={otpLoader}
handleVerifyBtn={handleVerifyBtn}
handleResend={handleResend}
/>
)}
{/* this component used for UI interaction and show their functionality */}
{pdfLoadFail && !checkTourStatus && (
<Tour
@@ -980,8 +1141,9 @@ function SignYourSelf() {
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
pdfUrl={pdfUrl}
signPdfUrl={pdfDetails[0] && pdfDetails[0].URL}
signPdfUrl={
pdfDetails[0] && (pdfDetails[0].SignedUrl || pdfDetails[0].URL)
}
allPages={allPages}
setAllPages={setAllPages}
setPageNumber={setPageNumber}
@@ -997,9 +1159,8 @@ function SignYourSelf() {
}}
>
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlert.isShow}
title={"Alert"}
title={isAlert?.header || "Alert"}
handleClose={() => {
setIsAlert({
isShow: false,
@@ -1007,55 +1168,24 @@ function SignYourSelf() {
});
}}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="p-[20px] h-full">
<p>{isAlert.alertMessage}</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<button
onClick={() => {
setIsAlert({
isShow: false,
alertMessage: ""
});
}}
type="button"
className="finishBtn cancelBtn"
>
Ok
</button>
</div>
</ModalUi>
{/* this modal is used show this document is already sign */}
<ModalUi
isOpen={showAlreadySignDoc.status}
title={"Sign Documents"}
title={"Document signed"}
handleClose={() => {
setShowAlreadySignDoc({ status: false });
}}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="p-[20px] h-full">
<p>{showAlreadySignDoc.mssg}</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
className="finishBtn cancelBtn"
className="op-btn op-btn-ghost shadow-md"
onClick={() => setShowAlreadySignDoc({ status: false })}
>
Close
@@ -1102,17 +1232,20 @@ function SignYourSelf() {
setIsInitial={setIsInitial}
setIsStamp={setIsStamp}
widgetType={widgetType}
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
{/*render email component to send email after finish signature on document */}
<EmailComponent
isEmail={isEmail}
pdfUrl={pdfUrl}
isCeleb={isCeleb}
setIsEmail={setIsEmail}
pdfName={pdfDetails[0] && pdfDetails[0].Name}
setSuccessEmail={setSuccessEmail}
sender={jsonSender}
setIsAlert={setIsAlert}
extUserId={extUserId}
activeMailAdapter={activeMailAdapter}
/>
{/* pdf header which contain funish back button */}
<Header
@@ -1120,7 +1253,6 @@ function SignYourSelf() {
allPages={allPages}
changePage={changePage}
pdfUrl={pdfUrl}
documentStatus={documentStatus}
embedWidgetsData={embedWidgetsData}
pdfDetails={pdfDetails}
isShowHeader={true}
@@ -1128,6 +1260,7 @@ function SignYourSelf() {
alreadySign={pdfUrl ? true : false}
isSignYourself={true}
setIsEmail={setIsEmail}
isCompleted={isCompleted}
/>
<div data-tut="reactourSecond">
@@ -1178,9 +1311,9 @@ function SignYourSelf() {
maxHeight: window.innerHeight - 70 + "px",
backgroundColor: "white"
}}
className="autoSignScroll"
className="overflow-y-auto hide-scrollbar"
>
{!documentStatus.isCompleted ? (
{!isCompleted ? (
<div>
<WidgetComponent
dataTut="reactourFirst"
@@ -1190,7 +1323,6 @@ function SignYourSelf() {
handleDivClick={handleDivClick}
handleMouseLeave={handleMouseLeave}
isDragSign={isDragSign}
themeColor={themeColor}
dragStamp={dragStamp}
dragRef={dragRef}
isDragStamp={isDragStamp}
@@ -1211,31 +1343,22 @@ function SignYourSelf() {
</div>
)}
<ModalUi
headerColor={"#dc3545"}
isOpen={validateAlert}
title={"Validation alert"}
handleClose={() => {
setValidateAlert(false);
}}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="p-[20px] h-full">
<p>
The input does not meet the criteria set by the regular expression.
</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => setValidateAlert(false)}
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-ghost shadow-md"
>
Close
</button>
+127 -98
View File
@@ -3,13 +3,13 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
import { useParams, useNavigate } from "react-router-dom";
import axios from "axios";
import "../styles/signature.css";
import { themeColor } from "../constant/const";
import { isEnableSubscription } from "../constant/const";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useDrag, useDrop } from "react-dnd";
import WidgetComponent from "../components/pdf/WidgetComponent";
import Tour from "reactour";
import Loader from "../primitives/LoaderWithMsg";
import LoaderWithMsg from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError";
import SignerListPlace from "../components/pdf/SignerListPlace";
import Header from "../components/pdf/PdfHeader";
@@ -23,8 +23,8 @@ import {
defaultWidthHeight,
addWidgetOptions,
textInputWidget,
textWidget,
radioButtonWidget
radioButtonWidget,
fetchSubscription
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -36,6 +36,7 @@ import AddRoleModal from "../components/pdf/AddRoleModal";
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import Parse from "parse";
const TemplatePlaceholder = () => {
const navigate = useNavigate();
const { templateId } = useParams();
@@ -53,6 +54,7 @@ const TemplatePlaceholder = () => {
const [isSelectListId, setIsSelectId] = useState();
const [isSendAlert, setIsSendAlert] = useState(false);
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
@@ -78,6 +80,7 @@ const TemplatePlaceholder = () => {
const [showDropdown, setShowDropdown] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [isRadio, setIsRadio] = useState(false);
const [blockColor, setBlockColor] = useState("");
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isNameModal, setIsNameModal] = useState(false);
const [pdfLoadFail, setPdfLoadFail] = useState({
@@ -143,6 +146,7 @@ const TemplatePlaceholder = () => {
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
const [isCheckbox, setIsCheckbox] = useState(false);
const [widgetName, setWidgetName] = useState(false);
const [isAddRole, setIsAddRole] = useState(false);
const senderUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
@@ -168,7 +172,23 @@ const TemplatePlaceholder = () => {
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current]);
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
return true;
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
navigate(`/subscription`);
}
} else {
navigate(`/subscription`);
}
}
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
const fetchTemplate = async () => {
try {
@@ -190,6 +210,9 @@ const TemplatePlaceholder = () => {
: [];
if (documentData && documentData.length > 0) {
if (isEnableSubscription) {
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
}
setPdfDetails(documentData);
setIsSigners(true);
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
@@ -224,6 +247,7 @@ const TemplatePlaceholder = () => {
});
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
} else {
const updatedSigners = documentData[0].Signers.map((x, index) => ({
...x,
@@ -232,6 +256,7 @@ const TemplatePlaceholder = () => {
}));
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
}
} else {
setRoleName("User 1");
@@ -250,6 +275,7 @@ const TemplatePlaceholder = () => {
setUniqueId(updatedSigners[0].Id);
setSignersData(updatedSigners);
setIsSelectId(0);
setBlockColor(updatedSigners[0].blockColor);
}
}
} else if (
@@ -284,10 +310,10 @@ const TemplatePlaceholder = () => {
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRecipients = tourstatus.filter(
(data) => data.templatetour
(data) => data.templateTour
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].templatetour);
setCheckTourStatus(checkTourRecipients[0].templateTour);
}
}
const loadObj = {
@@ -447,11 +473,6 @@ const TemplatePlaceholder = () => {
setIsCheckbox(true);
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
} else if (
dragTypeValue !== textWidget &&
dragTypeValue !== "signature"
) {
setIsNameModal(true);
}
setCurrWidgetsDetails({});
setWidgetType(dragTypeValue);
@@ -462,9 +483,20 @@ const TemplatePlaceholder = () => {
} else {
setIsReceipent(false);
}
} else {
setIsAddRole(true);
}
};
const tourAddRole = [
{
selector: '[data-tut="reactourAddbtn"]',
content: "You need to add a role before you can add fields for it. ",
position: "top",
style: { fontSize: "13px" }
}
];
//function for get pdf page details
const pageDetails = async (pdf) => {
const load = {
@@ -673,35 +705,29 @@ const TemplatePlaceholder = () => {
Name: pdfDetails[0]?.Name || "",
Note: pdfDetails[0]?.Note || "",
Description: pdfDetails[0]?.Description || "",
SendinOrder: pdfDetails[0]?.SendinOrder || false
SendinOrder: pdfDetails[0]?.SendinOrder || false,
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
NextReminderDate: pdfDetails[0]?.NextReminderDate
};
const updateTemplate = new Parse.Object("contracts_Template");
updateTemplate.id = templateId;
for (const key in data) {
updateTemplate.set(key, data[key]);
}
await updateTemplate.save(null, {
sessionToken: localStorage.getItem("accesstoken")
});
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}_Template/${templateId}`,
data,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
)
.then(() => {
setIsCreateDocModal(true);
setIsMailSend(true);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
})
.catch((err) => {
console.log("axois err ", err);
});
setIsCreateDocModal(true);
setIsMailSend(true);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} catch (e) {
setIsLoading(false);
alert("Something went wrong, please try again later.");
console.log("error", e);
}
} else {
@@ -743,7 +769,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`Drag a widget placeholder onto the PDF to choose your desired signing location.`}
message={`Drag or click on a field to add it to the document.`}
isChecked={handleDontShow}
/>
),
@@ -765,7 +791,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourFour"]',
content: () => (
<TourContentWithBtn
message={`Clicking "Save" button will save the template and will ask you if you want to create a new document using this template.`}
message={`Clicking Save will store the current template. After saving, youll be prompted to create a new document from this template if you wish.`}
isChecked={handleDontShow}
/>
),
@@ -783,15 +809,15 @@ const TemplatePlaceholder = () => {
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const templatetourIndex = tourStatus.findIndex(
(obj) => obj["templatetour"] === false || obj["templatetour"] === true
(obj) => obj["templateTour"] === false || obj["templateTour"] === true
);
if (templatetourIndex !== -1) {
updatedTourStatus[templatetourIndex] = { templatetour: true };
updatedTourStatus[templatetourIndex] = { templateTour: true };
} else {
updatedTourStatus.push({ templatetour: true });
updatedTourStatus.push({ templateTour: true });
}
} else {
updatedTourStatus = [{ templatetour: true }];
updatedTourStatus = [{ templateTour: true }];
}
await axios
.put(
@@ -860,6 +886,7 @@ const TemplatePlaceholder = () => {
setIsModalRole(false);
setRoleName("");
setUniqueId(Id);
setBlockColor(color[index]);
setIsSelectId(index);
setIsMailSend(false);
};
@@ -876,9 +903,11 @@ const TemplatePlaceholder = () => {
if (index === signersdata.length - 1) {
setUniqueId(updateSigner[updateSigner.length - 1]?.Id || "");
setIsSelectId(index - 1 || 0);
setBlockColor(color[index - 1 || 0]);
} else {
setUniqueId(updateSigner[index]?.Id || "");
setIsSelectId(index);
setBlockColor(color[index]);
}
setSignerPos(updatePlaceholderUser);
@@ -1098,8 +1127,6 @@ const TemplatePlaceholder = () => {
inputype = options.includes(defaultdata.textvalidate)
? defaultdata.textvalidate
: "regex";
} else {
inputype = "text";
}
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
@@ -1124,11 +1151,14 @@ const TemplatePlaceholder = () => {
status: defaultdata?.status || "required",
hint: defaultdata?.hint || "",
defaultValue: defaultdata?.defaultValue || "",
validation: {
type: inputype,
pattern:
inputype === "regex" ? defaultdata.textvalidate : ""
}
validation:
(isSubscribe || !isEnableSubscription) && inputype
? {
type: inputype,
pattern:
inputype === "regex" ? defaultdata.textvalidate : ""
}
: {}
}
};
} else {
@@ -1177,11 +1207,14 @@ const TemplatePlaceholder = () => {
<Title title={"Template"} />
<DndProvider backend={HTML5Backend}>
{isLoading.isLoad ? (
<Loader isLoading={isLoading} />
<LoaderWithMsg isLoading={isLoading} />
) : handleError ? (
<HandleError handleError={handleError} />
) : (
<div className="signatureContainer" ref={divRef}>
<div
className="op-card overflow-hidden flex flex-row justify-between bg-base-300 relative"
ref={divRef}
>
{/* this component used for UI interaction and show their functionality */}
{!checkTourStatus && (
//this tour component used in your html component where you want to put
@@ -1196,6 +1229,15 @@ const TemplatePlaceholder = () => {
closeWithMask={false}
/>
)}
{isAddRole && (
<Tour
onRequestClose={() => setIsAddRole(false)}
steps={tourAddRole}
isOpen={isAddRole}
rounded={5}
closeWithMask={false}
/>
)}
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdfDetails[0].URL}
@@ -1215,23 +1257,21 @@ const TemplatePlaceholder = () => {
>
{/* this modal is used show alert set placeholder for all signers before send mail */}
<ModalUi
headerColor={"#dc3545"}
isOpen={isSendAlert}
title={"Fields required"}
handleClose={() => setIsSendAlert(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add field for all recipients.</p>
<div className="h-full p-[20px]">
<p>Please add at least one signature field for all roles.</p>
</div>
</ModalUi>
<ModalUi
headerColor={"#dc3545"}
isOpen={!IsReceipent}
title={"Roles"}
handleClose={() => setIsReceipent(true)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add a role</p>
<div className="h-full p-[20px] text-center font-medium">
<p>Please add roles first</p>
</div>
</ModalUi>
{/* this modal is used show send mail message and after send mail success message */}
@@ -1240,32 +1280,18 @@ const TemplatePlaceholder = () => {
title={"Create Document"}
handleClose={() => setIsCreateDocModal(false)}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="h-full p-[20px]">
<p>
Do you want to create a document using the template you just
created ?
</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
{currentEmail.length > 0 && (
<>
<button
onClick={() => {
handleCreateDocModal();
}}
style={{
background: themeColor,
color: "white"
}}
onClick={() => handleCreateDocModal()}
type="button"
className="finishBtn"
className="op-btn op-btn-primary"
>
Yes
</button>
@@ -1274,7 +1300,7 @@ const TemplatePlaceholder = () => {
setIsCreateDocModal(false);
}}
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-secondary ml-2"
>
No
</button>
@@ -1282,32 +1308,21 @@ const TemplatePlaceholder = () => {
)}
</div>
</ModalUi>
{isCreateDoc && <Loader isLoading={isLoading} />}
{isCreateDoc && <LoaderWithMsg isLoading={isLoading} />}
<ModalUi
headerColor={"#dc3545"}
isOpen={isShowEmail}
title={"signers alert"}
handleClose={() => {
setIsShowEmail(false);
}}
>
<div style={{ height: "100%", padding: 20 }}>
<div className="h-full p-[20px]">
<p>Please select signer for add placeholder!</p>
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
></div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => {
setIsShowEmail(false);
}}
onClick={() => setIsShowEmail(false)}
type="button"
className="finishBtn cancelBtn"
className="op-btn op-btn-primary"
>
Ok
</button>
@@ -1322,6 +1337,7 @@ const TemplatePlaceholder = () => {
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
/>
<DropdownWidgetOption
type="checkbox"
@@ -1332,6 +1348,7 @@ const TemplatePlaceholder = () => {
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
/>
<DropdownWidgetOption
type="dropdown"
@@ -1342,6 +1359,7 @@ const TemplatePlaceholder = () => {
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
/>
<PlaceholderCopy
isPageCopy={isPageCopy}
@@ -1354,7 +1372,7 @@ const TemplatePlaceholder = () => {
// signerObjId={signerObjId}
Id={uniqueId}
/>
{/* pdf header which contain funish back button */}
{/* pdf header which contain finish, back button */}
<Header
completeBtnTitle={"Save"}
isPlaceholder={true}
@@ -1449,16 +1467,21 @@ const TemplatePlaceholder = () => {
title={"Roles"}
initial={true}
isTemplateFlow={true}
sendInOrder={pdfDetails[0].SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
/>
</div>
) : (
<div>
<div className="signerComponent">
<div className="hidden md:block w-[180px] h-full bg-base-100">
<div
style={{ maxHeight: window.innerHeight - 70 + "px" }}
className="autoSignScroll"
className="overflow-y-auto hide-scrollbar"
>
<SignerListPlace
isMailSend={isMailSend}
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
@@ -1472,10 +1495,14 @@ const TemplatePlaceholder = () => {
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
title={"Roles"}
sendInOrder={pdfDetails[0]?.SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
/>
<div data-tut="reactourSecond">
<WidgetComponent
pdfUrl={isMailSend}
isMailSend={isMailSend}
dragSignature={dragSignature}
signRef={signRef}
handleDivClick={handleDivClick}
@@ -1514,6 +1541,7 @@ const TemplatePlaceholder = () => {
isAddUser={isAddUser}
uniqueId={uniqueId}
closePopup={closePopup}
signersData={signersdata}
/>
</div>
<ModalUi
@@ -1532,6 +1560,7 @@ const TemplatePlaceholder = () => {
isOpen={isNameModal}
handleClose={handleNameModal}
handleData={handleWidgetdefaultdata}
isSubscribe={isSubscribe}
/>
</div>
);
+375 -110
View File
@@ -1,4 +1,8 @@
import React, { useState, useEffect } from "react";
import React, {
useState,
useEffect
// useRef
} from "react";
import { Navigate, useNavigate } from "react-router-dom";
import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
@@ -6,10 +10,18 @@ import dp from "../assets/images/dp.png";
import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
import Upgrade from "../primitives/Upgrade";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
function UserProfile() {
const navigate = useNavigate();
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [editmode, setEditMode] = useState(false);
@@ -19,54 +31,125 @@ function UserProfile() {
const [isLoader, setIsLoader] = useState(false);
const [percentage, setpercentage] = useState(0);
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
// const [publicUserName, setPublicUserName] = useState(
// extendUser && extendUser?.[0]?.UserName
// );
// const previousPublicUserName = useRef(publicUserName);
const [company, setCompany] = useState(
extendUser && extendUser?.[0]?.Company
);
const [jobTitle, setJobTitle] = useState(
extendUser && extendUser?.[0]?.JobTitle
);
const [isVerifyModal, setIsVerifyModal] = useState(false);
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
// const [userNameError, setUserNameError] = useState("");
useEffect(() => {
getUserDetail();
}, []);
const getUserDetail = async () => {
setIsLoader(true);
const extClass = localStorage.getItem("Extand_Class");
const jsonSender = JSON.parse(extClass);
const HeaderDocId = jsonSender[0]?.HeaderDocId;
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
if (HeaderDocId) {
setIsDisableDocId(HeaderDocId);
}
}, []);
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
let isEmailVerified = currentUser?.emailVerified || false;
if (isEmailVerified) {
setIsEmailVerified(isEmailVerified);
setIsLoader(false);
} else {
try {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(currentUser.objectId, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
isEmailVerified = user?.get("emailVerified");
setIsEmailVerified(isEmailVerified);
setIsLoader(false);
}
} catch (e) {
alert("something went wrong!");
}
}
};
//function to check public username already exist
// const handleCheckPublicUserName = async () => {
// try {
// const res = await Parse.Cloud.run("getpublicusername", {
// username: publicUserName
// });
// if (res) {
// setIsLoader(false);
// setUserNameError("user name already exist");
// setTimeout(() => {
// setUserNameError("");
// }, 3000);
// return res;
// }
// } catch (e) {
// console.log("error in getpublicusername cloud function");
// }
// };
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
let phn = Phone;
try {
const tour = Parse.Object.extend("_User");
const query = new Parse.Query(tour);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn);
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res.phone);
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
await updateExtUser({ Name: res.name, Phone: res.phone });
alert("Profile updated successfully.");
setEditMode(false);
navigate("/dashboard/35KBoSgoAK");
let phn = Phone,
res = "";
//condition to call cloud function when user change publicUserName
// if (previousPublicUserName.current !== publicUserName) {
// res = await handleCheckPublicUserName();
// }
if (!res) {
setIsLoader(true);
try {
const userQuery = Parse.Object.extend("_User");
const query = new Parse.Query(userQuery);
await query.get(UserProfile.objectId).then((object) => {
object.set("name", name);
object.set("ProfilePic", Image);
object.set("phone", phn || "");
object.save().then(
async (response) => {
if (response) {
let res = response.toJSON();
let rr = JSON.stringify(res);
localStorage.setItem("UserInformation", rr);
SetName(res.name);
SetPhone(res?.phone || "");
setImage(res.ProfilePic);
localStorage.setItem("username", res.name);
localStorage.setItem("profileImg", res.ProfilePic);
await updateExtUser({
Name: res.name,
Phone: res?.phone || ""
});
alert("Profile updated successfully.");
setEditMode(false);
setIsLoader(false);
//navigate("/dashboard/35KBoSgoAK");
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
}
},
(error) => {
alert("Something went wrong.");
console.error("Error while updating tour", error);
setIsLoader(false);
}
);
});
} catch (error) {
console.log("err", error);
);
});
} catch (error) {
console.log("err", error);
}
}
};
@@ -75,12 +158,15 @@ function UserProfile() {
const extClass = localStorage.getItem("extended_class");
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId;
const body = {
Phone: obj.Phone,
Phone: obj?.Phone || "",
Name: obj.Name,
HeaderDocId: isDisableDocId
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company
// UserName: publicUserName || ""
};
await axios.put(
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
body,
@@ -98,7 +184,6 @@ function UserProfile() {
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
// console.log("updateRes ", updateRes);
};
@@ -133,9 +218,9 @@ function UserProfile() {
// console.log("File URL:", response.url());
if (response.url()) {
setImage(response.url());
localStorage.setItem("profileImg", response.url());
setpercentage(0);
SaveFileSize(size, response.url());
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
@@ -153,30 +238,84 @@ function UserProfile() {
const handleDisableDocId = () => {
setIsDisableDocId((prevChecked) => !prevChecked);
};
//`handleVerifyBtn` function is used to send otp on user mail
const handleVerifyBtn = async () => {
setIsVerifyModal(true);
await handleSendOTP(Parse.User.current().getEmail());
};
const handleCloseVerifyModal = async () => {
setIsVerifyModal(false);
};
//`handleVerifyEmail` function is used to verify email with otp
const handleVerifyEmail = async (e) => {
e.preventDefault();
setOtpLoader(true);
try {
const resEmail = await Parse.Cloud.run("verifyemail", {
otp: otp,
email: Parse.User.current().getEmail()
});
if (resEmail?.message === "Email is verified.") {
setIsEmailVerified(true);
} else if (resEmail?.message === "Email is already verified.") {
setIsEmailVerified(true);
}
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
} catch (error) {
alert(error.message);
} finally {
setOtpLoader(false);
}
};
//function to use resend otp for email verification
const handleResend = async (e) => {
e.preventDefault();
setOtpLoader(true);
await handleSendOTP();
setOtpLoader(false);
alert("OTP sent on you email");
};
//function to handle onchange username and restrict 6-characters username for free users
// const handleOnchangeUserName = (e) => {
// const value = e.target.value;
// if (value.length > 6 && !isSubscribe) {
// setUserNameError("Please upgrade to allow more than 6 characters.");
// setTimeout(() => {
// setUserNameError("");
// }, 2000);
// } else {
// setPublicUserName(e.target.value);
// }
// };
const handleCancel = () => {
setEditMode(false);
SetName(localStorage.getItem("username"));
SetPhone(UserProfile && UserProfile.phone);
setImage(localStorage.getItem("profileImg"));
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
setCompany(extendUser && extendUser?.[0]?.Company);
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
};
return (
<React.Fragment>
<Title title={"Profile"} />
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="h-[100vh] flex justify-center items-center">
<Loader />
</div>
) : (
<div className="flex justify-center items-center w-full">
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
<div className="flex justify-center items-center w-full relative">
{/* {userNameError && (
<div
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
>
{userNameError}
</div>
)} */}
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded-xl w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
<img
@@ -188,7 +327,7 @@ function UserProfile() {
{editmode && (
<input
type="file"
className="max-w-[270px] text-sm py-1 px-2 mt-4 border-[1px] border-[#15b4e9] text-black rounded"
className="op-file-input op-file-input-bordered op-file-input-sm max-w-[270px] mt-4 text-sm"
accept="image/png, image/gif, image/jpeg"
onChange={(e) => {
let files = e.target.files;
@@ -214,7 +353,7 @@ function UserProfile() {
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Name:</span>{" "}
@@ -222,7 +361,7 @@ function UserProfile() {
<input
type="text"
value={name}
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
onChange={(e) => SetName(e.target.value)}
/>
) : (
@@ -231,14 +370,14 @@ function UserProfile() {
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1" : "py-2"
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Phone:</span>{" "}
{editmode ? (
<input
type="text"
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
onChange={(e) => SetPhone(e.target.value)}
value={Phone}
/>
@@ -250,68 +389,194 @@ function UserProfile() {
<span className="font-semibold">Email:</span>{" "}
<span>{UserProfile && UserProfile.email}</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Company:</span>{" "}
{editmode ? (
<input
type="text"
value={company}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
onChange={(e) => setCompany(e.target.value)}
/>
) : (
<span>{extendUser?.[0].Company}</span>
)}
</li>
<li
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Job title:</span>{" "}
{editmode ? (
<input
type="text"
value={jobTitle}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
onChange={(e) => setJobTitle(e.target.value)}
/>
) : (
<span>{extendUser?.[0]?.JobTitle}</span>
)}
</li>
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Is Email verified:</span>{" "}
<span>
{UserProfile && UserProfile.emailVerified
? "Verified"
: "Not verified"}
{isEmailVerified ? (
"Verified"
) : (
<span>
Not verified(
<span
onClick={() => handleVerifyBtn()}
className="hover:underline text-blue-600 cursor-pointer"
>
verify
</span>
)
</span>
)}
</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Disable DocumentId :</span>{" "}
<span>
<label className="relative inline-flex items-center cursor-pointer mb-0">
{/* {isEnableSubscription && (
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">
Public profile :{" "}
<Tooltip
message={`this is your public URL. Copy or share it
with the signer, and you will be able to see
all your publicly set templates.`}
/>
</span>
<div className="flex text-xs items-center">
<span>opensign-me.vercel.app/</span>
{editmode ? (
<input
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="op-input op-input-bordered op-input-xs"
/>
)}
</div>
</li>
)} */}
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span
className={
isSubscribe || !isEnableSubscription
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Disable DocumentId :{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
}
/>
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
className={`${
isSubscribe || !isEnableSubscription
? `${editmode ? "cursor-pointer" : ""}`
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
disabled={editmode ? false : true}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
checked={isDisableDocId}
onChange={handleDisableDocId}
type="checkbox"
value=""
className="sr-only peer"
/>
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</span>
</div>
{!isEnableSubscription && (
<PremiumAlertHeader
message={
"Disable documentId is free in beta, this feature will incur a fee later."
}
/>
)}
</li>
</ul>
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
{editmode ? (
<button
type="button"
onClick={handleSubmit}
className="rounded shadow focus:outline-none border-[2px] border-[#15b4e9] bg-white text-[#15b4e9] px-4 py-2 mr-4"
>
Save
</button>
) : (
<button
type="button"
onClick={() => {
setEditMode(true);
}}
className="rounded shadow focus:outline-none text-white bg-[#e7505a] px-4 py-2 mr-4"
>
Edit
</button>
)}
<div className="flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4">
<button
type="button"
onClick={() => {
if (editmode) {
setEditMode(false);
} else {
navigate("/changepassword");
}
}}
className={`rounded shadow focus:outline-none text-white bg-[#3598dc] ${
editmode ? "px-4 py-2 " : "p-2"
onClick={(e) =>
editmode ? handleSubmit(e) : setEditMode(true)
}
className="op-btn op-btn-primary"
>
{editmode ? "Save" : "Edit"}
</button>
<button
type="button"
onClick={() =>
editmode ? handleCancel() : navigate("/changepassword")
}
className={`op-btn ${
editmode ? "op-btn-ghost" : "op-btn-secondary"
}`}
>
{editmode ? "Cancel" : "Change Password"}
</button>
</div>
</div>
{isVerifyModal && (
<ModalUi
isOpen
title={"OTP verification"}
handleClose={handleCloseVerifyModal}
>
{otpLoader ? (
<div className="h-[150px] flex justify-center items-center">
<Loader />
</div>
) : (
<form onSubmit={(e) => handleVerifyEmail(e)}>
<div className="px-6 py-3 text-base-content">
<label className="mb-2">Enter OTP</label>
<input
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter OTP received over email"
value={otp}
onChange={(e) => setOtp(e.target.value)}
/>
</div>
<div className="px-6 mb-3">
<button type="submit" className="op-btn op-btn-primary">
Verify
</button>
<button
className="op-btn op-btn-secondary ml-2"
onClick={(e) => handleResend(e)}
>
Resend
</button>
</div>
</form>
)}
</ModalUi>
)}
</div>
)}
</React.Fragment>
+131 -76
View File
@@ -1,13 +1,18 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
import Parse from "parse";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
function Webhook() {
const navigation = useNavigate();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [webhook, setWebhook] = useState();
@@ -15,6 +20,8 @@ function Webhook() {
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [error, setError] = useState("");
useEffect(() => {
fetchWebhook();
@@ -23,6 +30,10 @@ function Webhook() {
const fetchWebhook = async () => {
const email = Parse.User.current().getEmail();
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
}
const params = { email: email };
try {
const extRes = await Parse.Cloud.run("getUserDetails", params);
@@ -38,41 +49,48 @@ function Webhook() {
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
setIsModal(false);
try {
const params = { url: webhook };
const url = parseBaseUrl + "functions/savewebhook";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, params, { headers: headers }).then((res) => {
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating webhook");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
if (webhook && webhook.startsWith("https://")) {
setIsLoader(true);
setIsModal(false);
try {
const params = { url: webhook };
const url = parseBaseUrl + "functions/savewebhook";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, params, { headers: headers }).then((res) => {
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating webhook");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
console.log("err", error);
console.log("err", error);
}
} else {
setError("The webhook url should always start with https://");
setTimeout(() => {
setError("");
}, 1500);
}
};
@@ -80,46 +98,50 @@ function Webhook() {
return (
<React.Fragment>
<Title title={"Webhook"} />
{isGenerate && <Alert type="success">Webhook added successfully!</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isGenerate && <Alert type="success">Webhook added successfully.</Alert>}
{isErr && <Alert type="danger">Something went wrong.</Alert>}
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="h-[100vh] flex justify-center items-center">
<Loader />
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">Webhook</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
{!isEnableSubscription && <PremiumAlertHeader />}
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign Webhook{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
isSubscribe={true}
/>
</h1>
<ul
className={
isSubscribe || !isEnableSubscription
? "w-full flex flex-col p-2 text-sm "
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
}
>
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<span className="w-[40%]">Webhook:</span>{" "}
<span id="token" className="w-[60%] md:text-end cursor-pointer">
{webhook && webhook}
</span>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
<span className="">Webhook:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
{webhook ? webhook : "_____"}
</span>
</div>
<button
type="button"
onClick={handleModal}
className="op-btn op-btn-primary "
>
{webhook ? "Update Webhook" : "Add Webhook"}
</button>
</li>
</ul>
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
<button
type="button"
onClick={handleModal}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
{webhook ? "Update Webhook" : "Add Webhook"}
</button>
<div className="flex items-center justify-center ">
<button
type="button"
onClick={() =>
@@ -127,36 +149,69 @@ function Webhook() {
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
className="op-btn op-btn-secondary my-2"
>
View Docs
</button>
</div>
{!isSubscribe && isEnableSubscription && (
<>
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
Upgrade to PRO Plan
</h1>
<ul className={"w-full flex flex-col p-2 text-sm "}>
<li
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
<span className="">$29.99/month:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
First 100 documents included, then just $0.15 per
document.
</span>
</div>
{!isSubscribe && isEnableSubscription && (
<button
type="button"
onClick={() => navigation("/subscription")}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
Upgrade Now
</button>
)}
</li>
</ul>
</>
)}
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
{error && <Alert type="danger">{error}</Alert>}
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
<div className="text-lg font-normal text-base-content">
<label className="text-sm ml-2">Webhook</label>
<input
value={webhook}
onChange={(e) => setWebhook(e.target.value)}
placeholder="Enter webhook url"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<hr className="bg-[#ccc] mt-3" />
<div className="flex items-center mt-3 gap-2">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
+16
View File
@@ -0,0 +1,16 @@
// below code is used to handle undefined Promise.withResolvers for safari
if (typeof Promise.withResolvers === "undefined") {
Promise.withResolvers = function () {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
};
}
// Usage:
// const { promise, resolve, reject } = Promise.withResolvers()
// console.log(promise, resolve, reject) // Promise { <pending> } [Function (anonymous)] [Function (anonymous)]
// ... Do something async and then call resolve or reject!
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import "../styles/AddUser.css";
const AddUser = (props) => {
import Loader from "./Loader";
const AddContact = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
@@ -21,7 +22,7 @@ const AddUser = (props) => {
);
if (savedUserDetails && addYourself) {
setName(savedUserDetails.name);
setPhone(savedUserDetails.phone);
setPhone(savedUserDetails?.phone || "");
setEmail(savedUserDetails.email);
}
}, [addYourself]);
@@ -50,15 +51,17 @@ const AddUser = (props) => {
try {
const contactQuery = new Parse.Object("contracts_Contactbook");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
if (phone) {
contactQuery.set("Phone", phone);
}
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
if (localStorage.getItem("TenetId")) {
if (localStorage.getItem("TenantId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenetId")
objectId: localStorage.getItem("TenantId")
});
}
@@ -68,8 +71,10 @@ const AddUser = (props) => {
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
const user = await _user.save();
if (user) {
@@ -191,40 +196,36 @@ const AddUser = (props) => {
};
return (
<div className="addusercontainer">
<div className="h-full px-[20px] py-[10px]">
{isLoader && (
<div className="loaderdiv">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<div className="form-wrapper">
<div style={{ fontSize: 14, fontWeight: "700" }}>Add User</div>
<div className="w-full mx-auto p-[8px]">
<div className="text-[14px] font-[700]">Add User</div>
{isUserExist && (
<div className="form-section">
<div className="mb-[0.75rem] flex items-center mt-1">
<input
type="checkbox"
id="addYourself"
checked={addYourself}
onChange={handleAddYourselfChange}
className="form-checkbox"
className="op-checkbox op-checkbox-sm"
/>
<label htmlFor="addYourself" className="checkbox-label ">
<label
htmlFor="addYourself"
className="ml-[0.5rem] text-base-content mb-0"
>
Add Yourself
</label>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
<form className="text-base-content" onSubmit={handleSubmit}>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
type="text"
@@ -233,48 +234,46 @@ const AddUser = (props) => {
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="form-section">
<label htmlFor="email" style={{ fontSize: 13 }}>
<div className="mb-[0.75rem]">
<label htmlFor="email" className="text-[13px]">
Email
<span style={{ color: "red", fontSize: 13 }}> *</span>
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
required
disabled={addYourself}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
/>
</div>
<div className="form-section">
<label htmlFor="phone" style={{ fontSize: 13 }}>
<div className="mb-[0.75rem]">
<label htmlFor="phone" className="text-[13px]">
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
disabled={addYourself}
className="addUserInput"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="buttoncontainer">
<button type="submit" className="submitbutton">
<div className="mt-6 flex justify-start gap-2">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
className="resetbutton"
className="op-btn op-btn-secondary"
>
Reset
</button>
@@ -285,4 +284,4 @@ const AddUser = (props) => {
);
};
export default AddUser;
export default AddContact;
+12 -12
View File
@@ -5,25 +5,25 @@ const Alert = ({ children, type }) => {
function theme(color) {
switch (color) {
case "success":
return "border-[#c3e6cb] bg-[#d4edda] text-[#155724]";
return "op-alert-success";
case "info":
return "border-[#adcdeb] bg-[#c1daf0] text-[#153756]";
return "op-alert-info";
case "danger":
return "border-[#f0a8a8] bg-[#f4bebe] text-[#c42121]";
return "op-alert-error";
case "warning":
return "op-alert-warning";
default:
return "border-[#d6d6d6] bg-[#d9d9d9] text-[#575757]";
return "";
}
}
return (
<>
{children && (
<div
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] z-50`}
>
{children}
children && (
<div className="z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm">
<div className={`op-alert ${textcolor}`}>
<span>{children}</span>
</div>
)}
</>
</div>
)
);
};
File diff suppressed because it is too large Load Diff
+2 -9
View File
@@ -2,15 +2,8 @@ import React from "react";
function HandleError({ handleError }) {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh"
}}
>
<span style={{ fontSize: "20px", color: "gray" }}>{handleError}</span>
<div className="flex justify-center items-center h-[100vh]">
<span className="text-[20px] text-[gray]">{handleError}</span>
</div>
);
}
+18
View File
@@ -0,0 +1,18 @@
import React, { Suspense } from "react";
import Loader from "./Loader";
const LazyPage = ({ Page }) => {
return (
<Suspense
fallback={
<div className="flex justify-center items-center h-[100vh]">
<Loader />
</div>
}
>
<Page />
</Suspense>
);
};
export default LazyPage;
+8 -28
View File
@@ -1,45 +1,25 @@
import React from "react";
import SelectSigners from "../components/shared/fields/SelectSigners";
import AddUser from "./AddUser";
import AddContact from "./AddContact";
import ModalUi from "./ModalUi";
const LinkUserModal = (props) => {
return (
<ModalUi
title={"Add/Choose Signer"}
isOpen={props.isAddUser[props.uniqueId]}
isOpen={
props?.isAddSigner ||
(props?.isAddUser && props?.isAddUser[props?.uniqueId])
}
handleClose={props.closePopup}
>
<SelectSigners
details={props.handleAddUser}
closePopup={props.closePopup}
signersData={props?.signersData}
/>
<div
style={{
display: "flex",
alignItems: "center",
gap: 5,
margin: "0 30%",
color: "#808080"
}}
>
<span
style={{
height: 1,
width: "100%",
backgroundColor: "#ccc"
}}
></span>
<span>or</span>
<span
style={{
height: 1,
width: "100%",
backgroundColor: "#ccc"
}}
></span>
</div>
<AddUser details={props.handleAddUser} closePopup={props.closePopup} />
<div className="op-divider text-base-content mx-[25%] my-1">or</div>
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
</ModalUi>
);
};
+9
View File
@@ -0,0 +1,9 @@
import React from "react";
const Loader = () => {
return (
<div className="op-loading op-loading-infinity w-[5rem] text-accent"></div>
);
};
export default Loader;

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