Compare commits

...
Author SHA1 Message Date
AmolandGitHub 7ef22eb220 Update uploadFiletoS3.js 2024-12-04 21:47:28 +05:30
AmolandGitHub 2237358d8a Update getSignedUrl.js 2024-12-04 21:46:32 +05:30
AmolandGitHub 0275762e20 Merge pull request #1492 from OpenSignLabs/fix_uncaught 2024-11-28 18:32:56 +05:30
prafull-opensignlabs 618b4fa2fb fix: handle uncaught server error 2024-11-28 18:20:48 +05:30
AndrewandGitHub 37b41fca93 Merge pull request #1491 from OpenSignLabs/prafull-opensignlabs-patch-3 2024-11-28 16:07:53 +05:30
prafull-opensignlabsandGitHub 470ed8970a fix: get owner subscription is expired in guest login flow 2024-11-28 16:02:39 +05:30
AmolandGitHub bb80324c35 Update CONTRIBUTING.md 2024-11-22 11:08:08 +05:30
AmolandGitHub a460b62233 Create LICENSE 2024-11-21 18:37:36 +05:30
AmolandGitHub 412901b839 Update LICENSE 2024-11-21 18:36:46 +05:30
prafull-opensignlabsandGitHub 9b322913f6 Merge pull request #1475 from OpenSignLabs/raktima-main-patch-1
fix: resolved text widget data embedding issue after sending the document
2024-11-20 16:30:36 +05:30
RaktimaandGitHub 248bb71ceb Update PlaceHolderSign.js 2024-11-20 16:13:53 +05:30
RaktimaNXG f62b241270 fix: resolved text widget data embedding issue after sending the document 2024-11-20 13:38:25 +05:30
prafull-opensignlabsandGitHub d95da53757 Merge pull request #1457 from OpenSignLabs/staging
fix: otp auth must be disable in certificate if OTP verification is off
2024-11-13 18:46:48 +05:30
prafull-opensignlabsandGitHub 66323f9347 Merge pull request #1456 from OpenSignLabs/uncompatible_pdf
fix: otp auth must be disable in certificate if OTP verification is off
2024-11-13 16:22:16 +05:30
prafull-opensignlabs c65f17a216 fix: otp auth must be disable in certificate if OTP verfication is off 2024-11-13 16:20:17 +05:30
prafull-opensignlabsandGitHub 277cc34e65 Merge pull request #1455 from OpenSignLabs/staging
fix: incompatible pdf error while signing pdf
2024-11-13 14:18:50 +05:30
prafull-opensignlabsandGitHub 9bf93fb231 Merge pull request #1454 from OpenSignLabs/uncompatible_pdf
fix: incompatible pdf error while signing pdf
2024-11-13 14:13:43 +05:30
prafull-opensignlabs f418e02270 fix: uncompatible pdf error while signing pdf 2024-11-13 14:11:11 +05:30
prafull-opensignlabsandGitHub 1ca33c0b76 Merge pull request #1453 from OpenSignLabs/staging
fix: server get crash in self-hosting
2024-11-13 10:11:42 +05:30
prafull-opensignlabsandGitHub 403254a5c6 Merge pull request #1451 from OpenSignLabs/validation
fix: server get crash in self-hosting
2024-11-13 10:09:14 +05:30
prafull-opensignlabs c893d96ad3 fix: server get crash in self-hosting 2024-11-13 00:33:46 +05:30
AndrewandGitHub d8131a6199 Merge pull request #1450 from OpenSignLabs/staging 2024-11-12 17:11:09 +05:30
AndrewandGitHub 3971118af8 Merge pull request #1449 from OpenSignLabs/validation 2024-11-12 17:04:04 +05:30
prafull-opensignlabs 4db0d93f41 refactor: update news from header 2024-11-12 16:24:50 +05:30
AndrewandGitHub affbfc5061 Merge pull request #1448 from OpenSignLabs/validation
refactor: change parameter from size to fontsize
2024-11-12 15:18:21 +05:30
prafull-opensignlabs 886ede2007 refactor: change parameter from size to fontsize 2024-11-12 15:17:34 +05:30
prafull-opensignlabsandGitHub a3936c66aa Merge pull request #1447 from OpenSignLabs/validation
fix: resolve spacing issue for text-type widgets in mobile view using draft template
2024-11-12 10:11:14 +05:30
prafull-opensignlabs d98453ffc2 fix: resolve spacing issue for text-type widgets in mobile view using draft template 2024-11-12 10:08:13 +05:30
AndrewandGitHub 7424912455 Merge pull request #1446 from OpenSignLabs/validation 2024-11-11 21:33:35 +05:30
prafull-opensignlabs e095495081 fix: handle invalid email validation in quick send 2024-11-11 21:24:56 +05:30
AndrewandGitHub eb4ce5544f Merge pull request #1445 from OpenSignLabs/validation 2024-11-11 20:29:33 +05:30
prafull-opensignlabs 8eef17834d fix: correct email validation in forms 2024-11-11 20:11:38 +05:30
prafull-opensignlabsandGitHub 40ed5e2353 Merge pull request #1443 from OpenSignLabs/raktima-patch-11
fix: resolve spacing issue for text-type widgets in mobile view using template flow
2024-11-11 16:54:31 +05:30
RaktimaNXG 2aa4c8c361 fix: resolve spacing issue for text-type widgets in mobile view using template flow 2024-11-11 15:50:17 +05:30
ZathielandGitHub e5b120fa36 Merge pull request #1437 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/tailwindcss-3.4.14
build(deps-dev): bump tailwindcss from 3.4.13 to 3.4.14 in /apps/OpenSign
2024-11-11 02:09:07 +01:00
ZathielandGitHub 1e026f8ebd Merge pull request #1438 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/i18next-23.16.5
build(deps): bump i18next from 23.16.2 to 23.16.5 in /apps/OpenSign
2024-11-11 02:08:40 +01:00
ZathielandGitHub e5c7da0847 Merge pull request #1439 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/daisyui-4.12.14
build(deps-dev): bump daisyui from 4.12.13 to 4.12.14 in /apps/OpenSign
2024-11-11 02:06:46 +01:00
dependabot[bot]andGitHub 2e67002768 build(deps-dev): bump tailwindcss in /apps/OpenSign
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.13 to 3.4.14.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.14/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.13...v3.4.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 01:06:23 +00:00
ZathielandGitHub 9c03649d48 Merge pull request #1441 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/lottiefiles/dotlottie-react-0.9.3
build(deps): bump @lottiefiles/dotlottie-react from 0.9.1 to 0.9.3 in /apps/OpenSign
2024-11-11 02:05:57 +01:00
ZathielandGitHub df436b3b20 Merge pull request #1442 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/npm_and_yarn-1b75c58f89
build(deps): bump the npm_and_yarn group in /apps/OpenSign with 2 updates
2024-11-11 02:04:07 +01:00
ZathielandGitHub 34acf4170e Merge pull request #1440 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/react-select-5.8.3
build(deps): bump react-select from 5.8.1 to 5.8.3 in /apps/OpenSign
2024-11-11 02:02:58 +01:00
dependabot[bot]andGitHub f9a13c880a build(deps): bump the npm_and_yarn group
Bumps the npm_and_yarn group in /apps/OpenSign with 2 updates: [postcss](https://github.com/postcss/postcss) and [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware).


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

Updates `http-proxy-middleware` from 2.0.6 to 2.0.7
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: http-proxy-middleware
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 01:01:45 +00:00
ZathielandGitHub df7cd45be6 Merge pull request #1434 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/eslint-9.14.0
build(deps-dev): bump eslint from 9.13.0 to 9.14.0 in /apps/OpenSignServer
2024-11-11 01:58:59 +01:00
dependabot[bot]andGitHub b22d70c833 build(deps): bump @lottiefiles/dotlottie-react in /apps/OpenSign
Bumps [@lottiefiles/dotlottie-react](https://github.com/LottieFiles/dotlottie-web/tree/HEAD/packages/react) from 0.9.1 to 0.9.3.
- [Release notes](https://github.com/LottieFiles/dotlottie-web/releases)
- [Changelog](https://github.com/LottieFiles/dotlottie-web/blob/main/packages/react/CHANGELOG.md)
- [Commits](https://github.com/LottieFiles/dotlottie-web/commits/HEAD/packages/react)

---
updated-dependencies:
- dependency-name: "@lottiefiles/dotlottie-react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:58:58 +00:00
dependabot[bot]andGitHub 1ef8e8aa6a build(deps): bump react-select from 5.8.1 to 5.8.3 in /apps/OpenSign
Bumps [react-select](https://github.com/JedWatson/react-select) from 5.8.1 to 5.8.3.
- [Release notes](https://github.com/JedWatson/react-select/releases)
- [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.8.1...react-select@5.8.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:58:47 +00:00
dependabot[bot]andGitHub 2aa58b7eb3 build(deps-dev): bump daisyui from 4.12.13 to 4.12.14 in /apps/OpenSign
Bumps [daisyui](https://github.com/saadeghi/daisyui) from 4.12.13 to 4.12.14.
- [Release notes](https://github.com/saadeghi/daisyui/releases)
- [Changelog](https://github.com/saadeghi/daisyui/blob/master/CHANGELOG.md)
- [Commits](https://github.com/saadeghi/daisyui/compare/v4.12.13...v4.12.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:58:36 +00:00
dependabot[bot]andGitHub 9fb61509c4 build(deps): bump i18next from 23.16.2 to 23.16.5 in /apps/OpenSign
Bumps [i18next](https://github.com/i18next/i18next) from 23.16.2 to 23.16.5.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v23.16.2...v23.16.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:58:25 +00:00
ZathielandGitHub b99dc875ed Merge pull request #1432 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/aws-sdk/s3-request-presigner-3.688.0
build(deps): bump @aws-sdk/s3-request-presigner from 3.685.0 to 3.688.0 in /apps/OpenSignServer
2024-11-11 01:58:01 +01:00
dependabot[bot]andGitHub 7da31f00c3 build(deps-dev): bump eslint in /apps/OpenSignServer
Bumps [eslint](https://github.com/eslint/eslint) from 9.13.0 to 9.14.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.13.0...v9.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:57:28 +00:00
ZathielandGitHub 894ccd1244 Merge pull request #1433 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/nodemailer-6.9.16
build(deps): bump nodemailer from 6.9.15 to 6.9.16 in /apps/OpenSignServer
2024-11-11 01:56:36 +01:00
ZathielandGitHub 8d90e6e808 Merge pull request #1435 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/babel/eslint-parser-7.25.9
build(deps-dev): bump @babel/eslint-parser from 7.25.8 to 7.25.9 in /apps/OpenSignServer
2024-11-11 01:55:18 +01:00
dependabot[bot]andGitHub 79771f725e 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.25.8 to 7.25.9.
- [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.25.9/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-11-11 00:53:16 +00:00
ZathielandGitHub 786f56941a Merge pull request #1436 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/mongodb-runner-5.7.1
build(deps-dev): bump mongodb-runner from 5.7.0 to 5.7.1 in /apps/OpenSignServer
2024-11-11 01:51:59 +01:00
dependabot[bot]andGitHub 8fae910d46 build(deps-dev): bump mongodb-runner in /apps/OpenSignServer
Bumps [mongodb-runner](https://github.com/mongodb-js/devtools-shared) from 5.7.0 to 5.7.1.
- [Commits](https://github.com/mongodb-js/devtools-shared/compare/mongodb-runner@5.7.0...mongodb-runner@5.7.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:15:02 +00:00
dependabot[bot]andGitHub 8b5920bba3 build(deps): bump nodemailer in /apps/OpenSignServer
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.15 to 6.9.16.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.15...v6.9.16)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:14:33 +00:00
dependabot[bot]andGitHub 03f1451e64 build(deps): bump @aws-sdk/s3-request-presigner in /apps/OpenSignServer
Bumps [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner) from 3.685.0 to 3.688.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.688.0/packages/s3-request-presigner)

---
updated-dependencies:
- dependency-name: "@aws-sdk/s3-request-presigner"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-11 00:14:24 +00:00
AndrewandGitHub 1d2a975b5f Merge pull request #1431 from OpenSignLabs/validation 2024-11-08 21:12:00 +05:30
prafull-opensignlabs 3db32979dc refacotr: add translation message for warning 2024-11-08 21:05:39 +05:30
AndrewandGitHub 110431dae2 Merge pull request #1430 from OpenSignLabs/validation 2024-11-08 20:10:03 +05:30
prafull-opensignlabs ade228f756 fix: user does not exist error while signing 2024-11-08 20:08:44 +05:30
AndrewandGitHub 9b18a66f22 Merge pull request #1429 from OpenSignLabs/validation
fix: signature types issues in public template while creating through drafttemplate
2024-11-08 18:39:33 +05:30
prafull-opensignlabs c88de90a24 fix: signature types issues in public template while creating through drafttemplate 2024-11-08 18:33:29 +05:30
RaktimaandGitHub 737d66d97d Merge pull request #1428 from OpenSignLabs/validation
fix: signature type not visible in signpad for public template
2024-11-08 12:47:13 +05:30
prafull-opensignlabs b77b57d3fd fix: signature type not visible in signpad for public template 2024-11-08 12:34:21 +05:30
AndrewandGitHub 240c7766c1 Merge pull request #1427 from OpenSignLabs/validation 2024-11-07 21:53:16 +05:30
prafull-opensignlabs 508b2e43d3 refactor: add restriction foe default signature type 2024-11-07 21:51:39 +05:30
prafull-opensignlabsandGitHub d6129e21e9 Merge pull request #1426 from OpenSignLabs/validation
fix: issue of signature types
2024-11-07 19:02:07 +05:30
prafull-opensignlabs a11a2444f1 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-11-07 18:59:10 +05:30
prafull-opensignlabs db1fb1c0e1 fix: issue of signature types 2024-11-07 18:56:42 +05:30
prafull-opensignlabsandGitHub 7f326b19aa Merge pull request #1425 from OpenSignLabs/raktima-patch-11
refactor code
2024-11-07 17:53:05 +05:30
RaktimaNXG 2a1d7d2502 refactor code 2024-11-07 17:18:03 +05:30
prafull-opensignlabsandGitHub 01edfce6e7 Merge pull request #1424 from OpenSignLabs/raktima-patch-11
fix: text type widgets space issue in mobile view
2024-11-07 16:40:13 +05:30
RaktimaNXG 9176b8f99c fix: increase text widgets space 2024-11-07 16:25:54 +05:30
RaktimaNXG 0ff2e57424 fix: text widgets issue in mobile view 2024-11-07 15:37:56 +05:30
prafull-opensignlabsandGitHub 567966866c Merge pull request #1422 from OpenSignLabs/validation
feat: add functionality to enable/disable signature types
2024-11-07 14:15:27 +05:30
prafull-opensignlabs e03f07a8ef fix: incompatible pdf issue and signturetype issues 2024-11-07 14:12:15 +05:30
prafull-opensignlabs 9915a700bb fix: incompatible pdf issue and signturetype issues 2024-11-07 14:12:01 +05:30
prafull-opensignlabsandGitHub f0a406a397 Merge pull request #1423 from OpenSignLabs/raktima-patch-10
fix: profile and side bar menu togglling issue in mobile view
2024-11-07 13:22:49 +05:30
prafull-opensignlabs 851ea24be4 feat: validation for signature type 2024-11-06 18:27:24 +05:30
RaktimaNXG 50c4eff790 fix: profile and side panel menu toggle problem in mobile view 2024-11-06 17:49:05 +05:30
prafull-opensignlabsandGitHub e69e6998c9 Merge pull request #1420 from OpenSignLabs/raktima-patch-10
fix: signer list selection issue in request sign flow
2024-11-06 14:36:29 +05:30
RaktimaNXG c87873cfb6 fix: signer list selection issue in request sign flow 2024-11-06 11:46:39 +05:30
prafull-opensignlabsandGitHub b09becf3eb Merge pull request #1419 from OpenSignLabs/validation
fix: user does not exist error while creating document
2024-11-06 11:41:58 +05:30
prafull-opensignlabs 0673242f41 fix: user does not exist error while creating document 2024-11-06 11:17:14 +05:30
prafull-opensignlabsandGitHub 1776931df5 Merge pull request #1378 from OpenSignLabs/raktima-patch-8
fix: form advanced options panel currently opens or closes when clicking outside the panel.
2024-11-06 10:09:52 +05:30
prafull-opensignlabsandGitHub a2c0921345 Merge pull request #1417 from OpenSignLabs/validation
fix: something went wrong issue while signing document
2024-11-05 16:20:00 +05:30
prafull-opensignlabs ad0a974c14 fix: something went wrong issue while signing document 2024-11-05 15:49:39 +05:30
ZathielandGitHub 2d4a7d336c Merge pull request #1404 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/aws-sdk/s3-request-presigner-3.685.0
build(deps): bump @aws-sdk/s3-request-presigner from 3.670.0 to 3.685.0 in /apps/OpenSignServer
2024-11-04 23:16:01 +01:00
dependabot[bot]andGitHub 8d2df87de3 build(deps): bump @aws-sdk/s3-request-presigner in /apps/OpenSignServer
Bumps [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner) from 3.670.0 to 3.685.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.685.0/packages/s3-request-presigner)

---
updated-dependencies:
- dependency-name: "@aws-sdk/s3-request-presigner"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 22:15:27 +00:00
ZathielandGitHub 873e7e2068 Merge pull request #1390 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/babel/runtime-corejs2-7.26.0
build(deps-dev): bump @babel/runtime-corejs2 from 7.25.7 to 7.26.0 in /apps/OpenSign
2024-11-04 23:15:10 +01:00
dependabot[bot]andGitHub 3fcb1d0138 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.25.7 to 7.26.0.
- [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.26.0/packages/babel-runtime-corejs2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 22:13:24 +00:00
ZathielandGitHub ce41f9a0ce Merge pull request #1406 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/aws-sdk/client-s3-3.685.0
build(deps): bump @aws-sdk/client-s3 from 3.675.0 to 3.685.0 in /apps/OpenSignServer
2024-11-04 23:13:23 +01:00
ZathielandGitHub 34d8ed19bf Merge pull request #1408 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/babel/core-7.26.0
build(deps-dev): bump @babel/core from 7.25.8 to 7.26.0 in /apps/OpenSign
2024-11-04 23:12:08 +01:00
ZathielandGitHub ea9f34f0b3 Merge pull request #1387 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/parse/s3-files-adapter-3.0.0
build(deps): bump @parse/s3-files-adapter from 2.2.0 to 3.0.0 in /apps/OpenSignServer
2024-11-04 23:11:57 +01:00
dependabot[bot]andGitHub 4e9cbd23f2 build(deps-dev): bump @babel/core in /apps/OpenSign
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.25.8 to 7.26.0.
- [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.26.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 22:11:41 +00:00
ZathielandGitHub bbd65c01e9 Merge pull request #1359 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/parse-server-api-mail-adapter-4.1.0
build(deps): bump parse-server-api-mail-adapter from 4.0.0 to 4.1.0 in /apps/OpenSignServer
2024-11-04 23:10:55 +01:00
ZathielandGitHub 5dc30b7702 Merge pull request #1407 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/react-cookie-7.2.2
build(deps): bump react-cookie from 7.2.1 to 7.2.2 in /apps/OpenSign
2024-11-04 23:10:09 +01:00
ZathielandGitHub eb3b31bd59 Merge pull request #1409 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/babel/preset-env-7.26.0
build(deps-dev): bump @babel/preset-env from 7.25.8 to 7.26.0 in /apps/OpenSign
2024-11-04 23:09:26 +01:00
dependabot[bot]andGitHub 57597dfef0 build(deps-dev): bump @babel/preset-env in /apps/OpenSign
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.25.8 to 7.26.0.
- [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.26.0/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 22:08:46 +00:00
ZathielandGitHub 8cb7a610c7 Merge pull request #1405 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/mongoose-8.8.0
build(deps): bump mongoose from 8.7.3 to 8.8.0 in /apps/OpenSignServer
2024-11-04 23:08:24 +01:00
ZathielandGitHub 2c464978b4 Merge pull request #1410 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/babel/preset-react-7.25.9
build(deps-dev): bump @babel/preset-react from 7.25.7 to 7.25.9 in /apps/OpenSign
2024-11-04 23:06:39 +01:00
AmolandGitHub 8d0bf6abe8 docs: update feature comparison 2024-11-04 23:03:18 +05:30
AmolandGitHub 82c724a768 docs: update feature comparison 2024-11-04 21:33:31 +05:30
prafull-opensignlabsandGitHub 6146fe5f86 Merge pull request #1413 from OpenSignLabs/raktima-patch-9
fix: text size issue after complete signature in mobile view
2024-11-04 18:15:30 +05:30
RaktimaNXG 24ce6eae3b fix: remove extra space from bottom after embed text widgets in mobile view 2024-11-04 18:09:38 +05:30
RaktimaNXG 8db934a2c2 fix: increase space from bottom in text widgets 2024-11-04 17:31:20 +05:30
AndrewandGitHub dc90d412ca Merge pull request #1415 from OpenSignLabs/prafull-opensignlabs-patch-2 2024-11-04 17:29:26 +05:30
prafull-opensignlabsandGitHub b8415c8995 fix: invalid type object save in signers field 2024-11-04 17:26:36 +05:30
RaktimaNXG 0d86f39ba3 refactor code 2024-11-04 16:48:02 +05:30
prafull-opensignlabsandGitHub 0bab033673 Merge pull request #1414 from OpenSignLabs/validation
fix: ensure webhook is triggered when document is created via quick send
2024-11-04 16:26:48 +05:30
prafull-opensignlabs 72a0ce3f51 fix: ensure webhook is triggered when document is created via quick send 2024-11-04 16:22:41 +05:30
RaktimaNXG c3a14b82b3 refactor code 2024-11-04 16:07:49 +05:30
RaktimaNXG b3252e276b fix: remove blank space from text widgets,fix text size conflict issue 2024-11-04 15:43:45 +05:30
prafull-opensignlabsandGitHub 10c63a66f6 Merge pull request #1412 from OpenSignLabs/validation
fix: invalid template id error while opening opening public template
2024-11-04 15:22:51 +05:30
prafull-opensignlabs f44205b90d fix: invalid template id error in while opening opening public template 2024-11-04 15:20:03 +05:30
dependabot[bot]andGitHub cd087d410f build(deps-dev): bump @babel/preset-react in /apps/OpenSign
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.25.7 to 7.25.9.
- [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.25.9/packages/babel-preset-react)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 00:40:58 +00:00
dependabot[bot]andGitHub c5812ba938 build(deps): bump react-cookie from 7.2.1 to 7.2.2 in /apps/OpenSign
Bumps [react-cookie](https://github.com/bendotcodes/cookies) from 7.2.1 to 7.2.2.
- [Release notes](https://github.com/bendotcodes/cookies/releases)
- [Changelog](https://github.com/bendotcodes/cookies/blob/main/LEGACY-CHANGELOG.md)
- [Commits](https://github.com/bendotcodes/cookies/compare/v7.2.1...v7.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 00:40:19 +00:00
dependabot[bot]andGitHub 2e4c7f7690 build(deps): bump @aws-sdk/client-s3 in /apps/OpenSignServer
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.675.0 to 3.685.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.685.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 00:17:30 +00:00
dependabot[bot]andGitHub a7389efb00 build(deps): bump mongoose from 8.7.3 to 8.8.0 in /apps/OpenSignServer
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.7.3 to 8.8.0.
- [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.7.3...8.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-04 00:16:54 +00:00
AmolandGitHub 532062687c Merge pull request #1402 from OpenSignLabs/validation
fix: make documentation url copy and clickable in draft template
2024-10-31 17:29:00 +05:30
prafull-opensignlabs d3a603d115 fix: make documentation url copy and clickable in draft ui 2024-10-31 17:02:49 +05:30
AmolandGitHub 26fece78c5 Merge pull request #1401 from OpenSignLabs/validation
fix: drattemplate ui changes, signers not visible in list, alert message
2024-10-31 13:13:00 +05:30
prafull-opensignlabs b8fb626591 fix: drattemplate ui changes, signers not visible in list, alert message 2024-10-31 12:58:43 +05:30
RaktimaNXG 732786067f fix: text size issue after complete signature in mobile view 2024-10-30 13:56:26 +05:30
AndrewandGitHub 45c0cd48c7 Merge pull request #1399 from OpenSignLabs/validation 2024-10-29 19:11:50 +05:30
prafull-opensignlabs 50d7982dd5 feat: add support for non-email usernames in SMTP configuration 2024-10-29 19:05:11 +05:30
AmolandGitHub ff085b1284 Merge pull request #1398 from OpenSignLabs/validation
fix: template variable is not define in draft template
2024-10-29 18:23:01 +05:30
prafull-opensignlabs c3564b1f8f fix: template variable is not define in draft template api 2024-10-29 17:35:33 +05:30
AmolandGitHub 32eba21e7a Merge pull request #1396 from OpenSignLabs/validation
feat: draft template api
2024-10-29 15:42:53 +05:30
prafull-opensignlabs 9952afcc38 feat: draft template api 2024-10-29 15:41:35 +05:30
AndrewandGitHub b3feccab0d Merge pull request #1392 from OpenSignLabs/validation 2024-10-28 17:42:29 +05:30
prafull-opensignlabs 86ebd4903b fix: document creator didn't receive completion mail 2024-10-28 17:15:11 +05:30
ZathielandGitHub 0533bb9c30 Merge pull request #1356 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/eslint-9.13.0 2024-10-28 08:12:07 +01:00
ZathielandGitHub 185e90494c Merge pull request #1357 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/react-datepicker-7.5.0 2024-10-28 08:10:02 +01:00
ZathielandGitHub 3f77b497ce Merge pull request #1386 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/mongoose-8.7.3 2024-10-28 08:09:14 +01:00
ZathielandGitHub 87a8333cdf Merge pull request #1389 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/serve-14.2.4 2024-10-28 08:08:20 +01:00
dependabot[bot]andGitHub 3d12261aa7 build(deps): bump serve from 14.2.3 to 14.2.4 in /apps/OpenSign
Bumps [serve](https://github.com/vercel/serve) from 14.2.3 to 14.2.4.
- [Release notes](https://github.com/vercel/serve/releases)
- [Commits](https://github.com/vercel/serve/compare/14.2.3...14.2.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 00:43:50 +00:00
dependabot[bot]andGitHub 19c6cbbcd3 build(deps): bump @parse/s3-files-adapter in /apps/OpenSignServer
Bumps [@parse/s3-files-adapter](https://github.com/parse-community/parse-server-s3-adapter) from 2.2.0 to 3.0.0.
- [Release notes](https://github.com/parse-community/parse-server-s3-adapter/releases)
- [Changelog](https://github.com/parse-community/parse-server-s3-adapter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/parse-community/parse-server-s3-adapter/compare/2.2.0...3.0.0)

---
updated-dependencies:
- dependency-name: "@parse/s3-files-adapter"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 00:35:57 +00:00
dependabot[bot]andGitHub 39c191db7c build(deps): bump mongoose from 8.7.2 to 8.7.3 in /apps/OpenSignServer
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.7.2 to 8.7.3.
- [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.7.2...8.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-28 00:35:48 +00:00
RaktimaNXG 58c36266e0 fix: form advanced options panel currently opens or closes when clicking outside the panel. 2024-10-23 11:19:02 +05:30
RaktimaandGitHub 0ebdfffdeb Merge pull request #1374 from OpenSignLabs/validation
fix: widgets get removed from doc when signers try to sign at a time
2024-10-22 19:54:03 +05:30
prafull-opensignlabs ed840f3048 fix: widgets get removed from doc when signers try to sign at a time 2024-10-22 19:45:41 +05:30
AmolandGitHub 1e13b509df Merge pull request #1372 from OpenSignLabs/validation
fix: report UI
2024-10-22 17:37:32 +05:30
prafull-opensignlabs d76a9a4628 fix: report UI 2024-10-22 17:36:01 +05:30
AndrewandGitHub f70e20498e Merge pull request #1367 from OpenSignLabs/raktima-patch-7 2024-10-21 17:28:21 +05:30
RaktimaNXG 1715b39629 refactor code 2024-10-21 17:20:28 +05:30
RaktimaNXG 3c63db8c9c refactor code 2024-10-21 14:24:58 +05:30
RaktimaNXG 7af78c95dd fix: resolve the copy widgets glitch and provide a warning tour for text widgets left empty. 2024-10-21 14:07:31 +05:30
ZathielandGitHub 1c4a377641 Merge pull request #1360 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/i18next-23.16.2 2024-10-21 07:42:57 +02:00
dependabot[bot]andGitHub bf56937585 build(deps-dev): bump eslint from 9.12.0 to 9.13.0 in /apps/OpenSign
Bumps [eslint](https://github.com/eslint/eslint) from 9.12.0 to 9.13.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.12.0...v9.13.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 05:41:38 +00:00
dependabot[bot]andGitHub e97442b0c8 build(deps): bump i18next from 23.16.0 to 23.16.2 in /apps/OpenSign
Bumps [i18next](https://github.com/i18next/i18next) from 23.16.0 to 23.16.2.
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v23.16.0...v23.16.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 05:41:36 +00:00
ZathielandGitHub d029ce5380 Merge pull request #1361 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/react-syntax-highlighter-15.6.1 2024-10-21 07:41:10 +02:00
ZathielandGitHub 20049921ad Merge pull request #1362 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/aws-sdk/client-s3-3.675.0 2024-10-21 07:40:29 +02:00
ZathielandGitHub 05a0db75db Merge pull request #1363 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/reduxjs/toolkit-2.3.0 2024-10-21 07:39:27 +02:00
ZathielandGitHub a7b8100a18 Merge pull request #1364 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/mongoose-8.7.2 2024-10-21 07:38:01 +02:00
ZathielandGitHub 112369b283 Merge pull request #1365 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/eslint-9.13.0 2024-10-21 07:37:15 +02:00
dependabot[bot]andGitHub f9b915b728 build(deps-dev): bump eslint in /apps/OpenSignServer
Bumps [eslint](https://github.com/eslint/eslint) from 9.12.0 to 9.13.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.12.0...v9.13.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:53 +00:00
dependabot[bot]andGitHub 8acdbc928e build(deps): bump mongoose from 8.7.1 to 8.7.2 in /apps/OpenSignServer
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.7.1 to 8.7.2.
- [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.7.1...8.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:43 +00:00
dependabot[bot]andGitHub 9665e14e6d build(deps): bump @reduxjs/toolkit from 2.2.8 to 2.3.0 in /apps/OpenSign
Bumps [@reduxjs/toolkit](https://github.com/reduxjs/redux-toolkit) from 2.2.8 to 2.3.0.
- [Release notes](https://github.com/reduxjs/redux-toolkit/releases)
- [Commits](https://github.com/reduxjs/redux-toolkit/compare/v2.2.8...v2.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:41 +00:00
dependabot[bot]andGitHub b45e05cc9e build(deps): bump @aws-sdk/client-s3 in /apps/OpenSignServer
Bumps [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) from 3.670.0 to 3.675.0.
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.675.0/clients/client-s3)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:33 +00:00
dependabot[bot]andGitHub 116f4d94ea build(deps): bump react-syntax-highlighter in /apps/OpenSign
Bumps [react-syntax-highlighter](https://github.com/react-syntax-highlighter/react-syntax-highlighter) from 15.5.0 to 15.6.1.
- [Release notes](https://github.com/react-syntax-highlighter/react-syntax-highlighter/releases)
- [Changelog](https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/CHANGELOG.MD)
- [Commits](https://github.com/react-syntax-highlighter/react-syntax-highlighter/compare/15.5.0...v15.6.1)

---
updated-dependencies:
- dependency-name: react-syntax-highlighter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:23 +00:00
dependabot[bot]andGitHub f18b5b49c5 build(deps): bump parse-server-api-mail-adapter in /apps/OpenSignServer
Bumps [parse-server-api-mail-adapter](https://github.com/parse-community/parse-server-api-mail-adapter) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/parse-community/parse-server-api-mail-adapter/releases)
- [Changelog](https://github.com/parse-community/parse-server-api-mail-adapter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/parse-community/parse-server-api-mail-adapter/compare/4.0.0...4.1.0)

---
updated-dependencies:
- dependency-name: parse-server-api-mail-adapter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:14 +00:00
dependabot[bot]andGitHub 09a4db7c03 build(deps): bump react-datepicker from 7.4.0 to 7.5.0 in /apps/OpenSign
Bumps [react-datepicker](https://github.com/Hacker0x01/react-datepicker) from 7.4.0 to 7.5.0.
- [Release notes](https://github.com/Hacker0x01/react-datepicker/releases)
- [Commits](https://github.com/Hacker0x01/react-datepicker/compare/v7.4.0...v7.5.0)

---
updated-dependencies:
- dependency-name: react-datepicker
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-21 00:31:00 +00:00
prafull-opensignlabsandGitHub 2616a5776b Merge pull request #1350 from OpenSignLabs/validation
fix: show invalid password error if pfx password is invalid
2024-10-18 17:21:59 +05:30
prafull-opensignlabs 36cfc4a867 fix: show invalid password error if pfx password is invalid 2024-10-17 10:55:43 +05:30
AndrewandGitHub 631f118afb Merge pull request #1347 from OpenSignLabs/validation
fix: document loading err and report UI
2024-10-16 20:26:22 +05:30
prafull-opensignlabs b43e3a61e6 refactor: remove unnecessary code 2024-10-16 16:22:06 +05:30
prafull-opensignlabs 04100adece fix: add space below in last row from reports 2024-10-16 14:48:34 +05:30
prafull-opensignlabs a103f93925 fix: add space after last record in reports 2024-10-16 13:02:24 +05:30
prafull-opensignlabs 8467340fb5 fix: report UI 2024-10-16 12:13:19 +05:30
prafull-opensignlabs 3a8e41de27 fix: duplicate id warning 2024-10-15 18:57:15 +05:30
prafull-opensignlabs 97c2f183ca fix: document loading err and report UI 2024-10-15 18:56:50 +05:30
84 changed files with 7372 additions and 3428 deletions
+2 -2
View File
@@ -77,6 +77,6 @@ Engage with the community on our [Gitter Channel](https://gitter.im/OpenSignLabs
## License
By contributing, you agree that your contributions will be licensed under its [MIT License](LICENSE).
By contributing, you agree that your contributions will be licensed under its [AGPL-V3 License](LICENSE).
---
---
+6 -1
View File
@@ -1,4 +1,9 @@
GNU AFFERO GENERAL PUBLIC LICENSE
* All content that resides under the "apps/OpenSignServer/cloud/customRoute" directory of this repository, if that directory exists, is licensed under the license defined in "apps/OpenSignServer/cloud/customRoute".
* All third party components incorporated into the OpenSign Software are licensed under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is available under the "GNU AFFERO GENERAL PUBLIC LICENSE" as defined below -
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+606 -620
View File
File diff suppressed because it is too large Load Diff
+16 -16
View File
@@ -4,14 +4,14 @@
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.9.1",
"@lottiefiles/dotlottie-react": "^0.9.3",
"@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.4",
"@react-pdf/renderer": "^4.0.0",
"@reduxjs/toolkit": "^2.2.8",
"@reduxjs/toolkit": "^2.3.0",
"axios": "^1.7.7",
"file-saver": "^2.0.5",
"i18next": "^23.16.0",
"i18next": "^23.16.5",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.6.2",
"jszip": "^3.10.1",
@@ -25,8 +25,8 @@
"react": "^18.2.0",
"react-bootstrap": "^2.10.5",
"react-confetti": "^6.1.0",
"react-cookie": "^7.2.1",
"react-datepicker": "^7.4.0",
"react-cookie": "^7.2.2",
"react-datepicker": "^7.5.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dnd-multi-backend": "^8.0.3",
@@ -43,16 +43,16 @@
"react-router-dom": "^6.27.0",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.1",
"react-select": "^5.8.3",
"react-signature-canvas": "^1.0.6",
"react-syntax-highlighter": "^15.5.0",
"react-syntax-highlighter": "^15.6.1",
"react-tooltip": "^5.28.0",
"react-web-share": "^2.0.2",
"reactour": "^1.19.4",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"regex-parser": "^2.3.0",
"serve": "^14.2.3",
"serve": "^14.2.4",
"styled-components": "^5.3.0",
"web-vitals": "^4.2.3",
"ws": "^8.18.0"
@@ -95,28 +95,28 @@
}
},
"devDependencies": {
"@babel/core": "^7.25.8",
"@babel/preset-env": "^7.25.8",
"@babel/preset-react": "^7.25.7",
"@babel/runtime-corejs2": "^7.25.7",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9",
"@babel/runtime-corejs2": "^7.26.0",
"autoprefixer": "^10.4.20",
"babel-loader": "^9.2.1",
"clean-webpack-plugin": "^4.0.0",
"commitizen": "^4.3.1",
"concurrently": "^9.0.1",
"css-loader": "^7.1.2",
"daisyui": "^4.12.13",
"daisyui": "^4.12.14",
"dotenv": "^16.4.5",
"dotenv-webpack": "^8.1.0",
"eslint": "^9.12.0",
"eslint": "^9.13.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.1",
"lint-staged": "^15.2.10",
"mini-css-extract-plugin": "^2.9.1",
"postcss": "^8.4.47",
"postcss": "^8.4.48",
"prettier": "^3.3.3",
"pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.13",
"tailwindcss": "^3.4.14",
"terser-webpack-plugin": "^5.3.10",
"webpack-cli": "^5.1.4"
},
@@ -1,5 +1,5 @@
{
"header-news": "New feature: Public profiles are now live! Easily create and share public templates",
"header-news": "New feature: Teams plan users can now integrate their own AWS S3 buckets for file storage",
"header-news-btn": "Setup Now",
"create-account": "Create Account",
"login": "Login",
@@ -94,6 +94,7 @@
"My Signature": "My Signature",
"API Token": "API Token",
"Webhook": "Webhook",
"Preferences": "Preferences",
"Teams": "Teams",
"Team-update-successfully": "Team Updated successfully.",
"Users": "Users"
@@ -291,7 +292,7 @@
"subject": "Subject",
"body": "Body",
"add-contact": "Add Contact",
"add-signer-alert": "Contact already exist!",
"add-signer-alert": "Contact already exist! Please select it from Signers dropdown",
"record-delete-alert": "Record deleted successfully!",
"record-revoke-alert": "Record revoked successfully!",
"mail-sent-alert": "Mail sent successfully.",
@@ -445,7 +446,8 @@
"cutomize-email": "Cutomize Email",
"upgrade-to-customize-email": "Upgrade to customize Email",
"sign-url": "Sign url",
"Mails Sent": "Mails Sent",
"mails-sent": "Mails sent",
"mail-not-delivered": "Mail not delivered",
"document-alert": "Document Alert",
"owner-subscription-expired": "Owner's subscription has expired.",
"subscription-expired": "Subscription Expired",
@@ -679,9 +681,21 @@
"p3":"Tip: If your signers are familiar with OpenSign and frequently use it, you may want to disable the guided tour for a more seamless experience. Choose the option that best fits your document signing needs."
},
"form-title-1":"Document Flow Setting",
"form-title-2":"Security Setting",
"form-title-2":"Security Settings",
"public-tour-message":"The template needs to be public before you can generate a shareable link.",
"add-user-template": "You need to add a role before you can add fields for it.",
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>"
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>",
"text-field-tour":"Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
"attach-signer-tour":"You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
"allowed-signature-types": "Allowed signature types",
"at-least-one-signature-type": "At least one signature type should be enabled.",
"expect-default-one-more-signature-type": "Please enable one additional signature type apart from the default.",
"add-signer-note": "Note - This contact will be added to your contact list",
"allowed-signature-types-help": {
"p1": "This signature preference controls which signing options are available to your signers. Only the signature types you select will appear in the signature widget when a document is signed. For example, if you choose only the 'Draw' option in preferences, your signer will see only the 'Draw' option in the signature widget, while the other three options will not be available.",
"l1": "Create a handwritten signature directly on the screen using your finger or a stylus.",
"l2": "Enter your name and choose from various font styles to create a signature.",
"l3": "Upload an existing digital version of your signature.",
"l4": "Use a pre-set signature style saved under 'My Signature' in your settings."
}
}
@@ -1,5 +1,5 @@
{
"header-news": "Nueva funcionalidad: ¡Los perfiles públicos ahora están disponibles! Crea y comparte plantillas públicas fácilmente",
"header-news": "Nueva característica: los usuarios del plan Teams ahora pueden integrar sus propios depósitos de AWS S3 para el almacenamiento de archivos",
"header-news-btn": "Configurar ahora",
"create-account": "Crear cuenta",
"login": "Iniciar sesión",
@@ -94,6 +94,7 @@
"My Signature": "Mi firma",
"API Token": "Token API",
"Webhook": "Webhook",
"Preferences": "Preferencias",
"Teams": "Equipos",
"Team-update-successfully": "Equipo actualizado correctamente.",
"Users": "Usuarios"
@@ -291,7 +292,7 @@
"subject": "Asunto",
"body": "Cuerpo",
"add-contact": "Agregar contacto",
"add-signer-alert": "¡El contacto ya existe!",
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
"record-delete-alert": "¡Entrada eliminada exitosamente!",
"record-revoke-alert": "¡Entrada revocada exitosamente!",
"mail-sent-alert": "Correo enviado exitosamente.",
@@ -445,7 +446,8 @@
"cutomize-email": "Personalizar correo",
"upgrade-to-customize-email": "Mejora para personalizar el correo",
"sign-url": "URL de firma",
"Mails Sent": "Correos enviados",
"mails-sent": "Correos enviados",
"mail-not-delivered": "correo no entregado",
"document-alert": "Alerta de documento",
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
"subscription-expired": "Suscripción expirada",
@@ -682,6 +684,18 @@
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
},
"form-title-1": "Configuración del flujo de documentos",
"form-title-2": "Configuración de seguridad"
"form-title-2": "Configuración de seguridad",
"text-field-tour":"Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
"attach-signer-tour" :"Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
"allowed-signature-types": "Tipos de firma permitidos",
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
"allowed-signature-types-help": {
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
"l3": "Cargue una versión digital existente de su firma.",
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
}
}
@@ -1,5 +1,5 @@
{
"header-news": "Nouvelle fonctionnalité : les profils publics sont désormais en ligne ! Créez et partagez facilement des modèles publics",
"header-news": "Nouvelle fonctionnalité : les utilisateurs du forfait Teams peuvent désormais intégrer leurs propres compartiments AWS S3 pour le stockage de fichiers",
"header-news-btn": "Configurer maintenant",
"create-account": "Créer un compte",
"login": "Se Connecter",
@@ -94,6 +94,7 @@
"My Signature": "Ma signature",
"API Token": "Jeton API",
"Webhook": "Webhook",
"Preferences": "Préférences",
"Teams": "Équipe",
"Team-update-successfully": "Équipe mise à jour avec succès.",
"Users": "Utilisateurs"
@@ -290,7 +291,7 @@
"subject": "Sujet",
"body": "Corps",
"add-contact": "Ajouter le contact",
"add-signer-alert": "Le contact existe déjà !",
"add-signer-alert": "Le contact existe déjà ! Veuillez le sélectionner dans la liste déroulante 'Signataires'",
"record-delete-alert": "Enregistrement supprimé avec succès !",
"record-revoke-alert": " Enregistrement révoqué avec succès ! ",
"mail-sent-alert": "Courriel envoyé avec succès.",
@@ -444,7 +445,8 @@
"cutomize-email": "Personnaliser l'e-mail",
"upgrade-to-customize-email": "Mise à niveau pour personnaliser l'e-mail",
"sign-url": "Signer l'URL",
"Mails Sent": "Mails envoyés avec succès !",
"mails-sent": "Mails envoyés avec succès !",
"mail-not-delivered": "Courrier non distribué",
"document-alert": "Alerte document",
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
"subscription-expired": "Abonnement expiré",
@@ -681,7 +683,18 @@
"form-title-2": "Paramètre de sécurité",
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
"add-user-template":"Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
"pdf-uncompatible":"Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>"
"pdf-uncompatible":"Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>",
"text-field-tour" :"Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
"attach-signer-tour" :"Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
"allowed-signature-types": "Types de signature autorisés",
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
"expect-default-one-signature-type": "Veuillez activer un type de signature supplémentaire en plus de celui par défaut.",
"add-signer-note": "Remarque - Ce contact sera ajouté à votre liste de contacts",
"allowed-signature-types-help": {
"p1": "Cette préférence de signature contrôle les options de signature disponibles pour vos signataires. Seuls les types de signature que vous sélectionnez apparaîtront dans le widget de signature lorsqu'un document est signé. Par exemple, si vous choisissez uniquement l'option 'Draw' dans les préférences, votre signataire ne verra que l'option 'Draw' dans le widget de signature, tandis que les trois autres options ne seront pas disponibles.",
"l1": "Créez une signature manuscrite directement sur l'écran à l'aide de votre doigt ou d'un stylet.",
"l2": "Entrez votre nom et choisissez parmi différents styles de police pour créer une signature.",
"l3": "Téléchargez une version numérique existante de votre signature.",
"l4": "Utilisez un style de signature prédéfini enregistré sous 'Ma signature' dans vos paramètres."
}
}
+10
View File
@@ -35,6 +35,8 @@ const GenerateToken = lazy(() => import("./pages/GenerateToken"));
const Webhook = lazy(() => import("./pages/Webhook"));
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
const DraftTemplate = lazy(() => import("./pages/DraftTemplate"));
const Preferences = lazy(() => import("./pages/Preferences"));
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
const AppLoader = () => {
@@ -198,8 +200,16 @@ function App() {
</>
)}
<Route path="/users" element={<UserList />} />
<Route
path="/preferences"
element={<LazyPage Page={Preferences} />}
/>
</Route>
<Route path="/sso" element={<SSOVerify />} />
<Route
path="/drafttemplate/:jwttoken"
element={<DraftTemplate />}
/>
<Route path="*" element={<PageNotFound />} />
</Routes>
</BrowserRouter>
+124 -118
View File
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const";
const AddSigner = (props) => {
const { t } = useTranslation();
@@ -47,139 +48,143 @@ const AddSigner = (props) => {
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
try {
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 (!emailRegex.test(email)) {
alert("Please enter a valid email address.");
} else {
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
try {
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) {
_user.set("phone", phone);
contactQuery.set("Phone", phone);
}
const user = await _user.save();
if (user) {
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_Guest");
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.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
contactQuery.setACL(acl);
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 currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
const res = await contactQuery.save();
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);
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
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 currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
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 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("");
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(t("add-signer-alert"));
setIsLoader(false);
}
} else {
alert(t("add-signer-alert"));
} catch (err) {
console.log("err in fetch contact", err);
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
} catch (err) {
console.log("err in fetch contact", err);
} else {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
} else {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
};
@@ -206,6 +211,7 @@ const AddSigner = (props) => {
</div>
)}
<div className="w-full mx-auto p-2 text-base-content">
<p className="mb-3">{t("add-signer-note")}</p>
{isUserExist && (
<div className="mb-3 flex items-center">
<input
+155 -132
View File
@@ -3,7 +3,7 @@ import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { copytoData, fetchSubscriptionInfo } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import { emailRegex, isEnableSubscription } from "../constant/const";
import { useTranslation } from "react-i18next";
function generatePassword(length) {
const characters =
@@ -115,149 +115,172 @@ const AddUser = (props) => {
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
setIsFormLoader(false);
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
if (!emailRegex.test(formdata.email)) {
alert("Please enter a valid email address.");
} else {
try {
const extUser = new Parse.Object("contracts_Users");
extUser.set("Name", formdata.name);
if (formdata.phone) {
extUser.set("Phone", formdata.phone);
}
extUser.set("Email", formdata.email);
extUser.set("UserRole", `contracts_${formdata.role}`);
if (formdata?.team) {
extUser.set("TeamIds", [
{
__type: "Pointer",
className: "contracts_Teams",
objectId: formdata.team
}
]);
}
if (localUser && localUser.OrganizationId) {
extUser.set("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: localUser.OrganizationId.objectId
});
}
if (localUser && localUser.Company) {
extUser.set("Company", localUser.Company);
}
if (localStorage.getItem("TenantId")) {
extUser.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
setIsFormLoader(false);
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
} else {
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", formdata.name);
_user.set("username", formdata.email);
_user.set("email", formdata.email);
_user.set("password", formdata.password);
const extUser = new Parse.Object("contracts_Users");
extUser.set("Name", formdata.name);
if (formdata.phone) {
_user.set("phone", formdata.phone);
extUser.set("Phone", formdata.phone);
}
extUser.set("Email", formdata.email);
extUser.set("UserRole", `contracts_${formdata.role}`);
if (formdata?.team) {
extUser.set("TeamIds", [
{
__type: "Pointer",
className: "contracts_Teams",
objectId: formdata.team
}
]);
}
if (localUser && localUser.OrganizationId) {
extUser.set("OrganizationId", {
__type: "Pointer",
className: "contracts_Organizations",
objectId: localUser.OrganizationId.objectId
});
}
if (localUser && localUser.Company) {
extUser.set("Company", localUser.Company);
}
const user = await _user.save();
if (user) {
const currentUser = Parse.User.current();
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
if (localStorage.getItem("TenantId")) {
extUser.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenantId")
});
}
extUser.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
extUser.setACL(acl);
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.closePopup) {
props.closePopup();
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", formdata.name);
_user.set("username", formdata.email);
_user.set("email", formdata.email);
_user.set("password", formdata.password);
if (formdata.phone) {
_user.set("phone", formdata.phone);
}
if (props.handleUserData) {
if (formdata?.team) {
const team = teamList.find((x) => x.objectId === formdata.team);
parseData.TeamIds = parseData.TeamIds.map((y) =>
y.objectId === team.objectId ? team : y
);
const user = await _user.save();
if (user) {
const currentUser = Parse.User.current();
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
extUser.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
extUser.setACL(acl);
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
if (formdata?.team) {
const team = teamList.find(
(x) => x.objectId === formdata.team
);
parseData.TeamIds = parseData.TeamIds.map((y) =>
y.objectId === team.objectId ? team : y
);
}
props.handleUserData(parseData);
}
props.handleUserData(parseData);
}
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
setIsFormLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: formdata.email };
const userRes = await Parse.Cloud.run("getUserId", params);
const currentUser = Parse.User.current();
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
extUser.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);
extUser.setACL(acl);
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
if (formdata?.team) {
const team = teamList.find(
(x) => x.objectId === formdata.team
);
parseData.TeamIds = parseData.TeamIds.map((y) =>
y.objectId === team.objectId ? team : y
);
}
props.handleUserData(parseData);
}
setIsFormLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
}
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: formdata.email };
const userRes = await Parse.Cloud.run("getUserId", params);
const currentUser = Parse.User.current();
extUser.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
extUser.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);
extUser.setACL(acl);
const res = await extUser.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
if (formdata?.team) {
const team = teamList.find((x) => x.objectId === formdata.team);
parseData.TeamIds = parseData.TeamIds.map((y) =>
y.objectId === team.objectId ? team : y
);
}
props.handleUserData(parseData);
}
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
}
console.log("err", err);
setIsFormLoader(false);
props.setIsAlert({
type: "danger",
msg: t("something-went-wrong-mssg")
});
} finally {
setTimeout(
() => props.setIsAlert({ type: "success", msg: "" }),
1500
);
}
} catch (err) {
console.log("err", err);
setIsFormLoader(false);
props.setIsAlert({
type: "danger",
msg: t("something-went-wrong-mssg")
});
} finally {
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
}
}
};
+100 -61
View File
@@ -5,7 +5,7 @@ import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const";
import { emailRegex, isEnableSubscription } from "../constant/const";
import { fetchSubscription } from "../constant/Utils";
import { useNavigate } from "react-router-dom";
const BulkSendUi = (props) => {
@@ -69,7 +69,21 @@ const BulkSendUi = (props) => {
if (subscription?.plan === "freeplan") {
setIsFreePlan(true);
}
const resCredits = await Parse.Cloud.run("allowedcredits");
const token = props.jwttoken
? { jwttoken: props.jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const axiosres = await axios.post(
`${localStorage.getItem("baseUrl")}functions/allowedcredits`,
{},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token
}
}
);
const resCredits = axiosres.data && axiosres.data.result;
if (resCredits) {
const allowedcredits = resCredits?.allowedcredits || 0;
const addoncredits = resCredits?.addoncredits || 0;
@@ -81,7 +95,7 @@ const BulkSendUi = (props) => {
}
setAmount((obj) => ({ ...obj, totalcredits: totalcredits }));
}
const getPlaceholder = props.item?.Placeholders;
const getPlaceholder = props?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
@@ -97,7 +111,7 @@ const BulkSendUi = (props) => {
} else {
setIsBulkAvailable(true);
setAdmin((obj) => ({ ...obj, isAdmin: true }));
const getPlaceholder = props.item?.Placeholders;
const getPlaceholder = props?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
@@ -188,75 +202,99 @@ const BulkSendUi = (props) => {
const updatedForms = forms.filter((_, i) => i !== index);
setForms(updatedForms);
};
function validateEmails(data) {
for (const item of data) {
for (const field of item.fields) {
if (!emailRegex.test(field.email)) {
alert(`Invalid email found: ${field.email}`);
return false;
}
}
}
return true;
}
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) => {
//checking if user enter email which already exist as a signer then add user in a signers array
let existSigner = [];
form.fields.map((data) => {
if (data.signer) {
existSigner.push(data.signer);
}
});
// Map through the copied Placeholders array to update email values
const updatedPlaceholders = Placeholders.map((placeholder) => {
// Find the field in the current form that matches the placeholder Id
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) {
if (signer) {
return {
...placeholder,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
} else {
return {
...placeholder,
email: field.email,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
if (validateEmails(forms)) {
// Create a copy of Placeholders array from props.item
let Placeholders = [...props.Placeholders];
// Initialize an empty array to store updated documents
let Documents = [];
// Loop through each form
forms.forEach((form) => {
//checking if user enter email which already exist as a signer then add user in a signers array
let existSigner = [];
form.fields.map((data) => {
if (data.signer) {
existSigner.push(data.signer);
}
}
// 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
if (existSigner?.length > 0) {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders,
Signers: props.item.Signers
? [...props.item.Signers, ...existSigner]
: [...existSigner]
});
} else {
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
}
});
await batchQuery(Documents);
// 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) {
if (signer) {
return {
...placeholder,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
} else {
return {
...placeholder,
email: field.email,
signerObjId: field?.signer?.objectId || "",
signerPtr: signer
};
}
}
// If no matching field is found, keep the placeholder as is
return placeholder;
});
// Push a new document object with updated Placeholders into the Documents array
if (existSigner?.length > 0) {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders,
Signers: props.item.Signers
? [...props.item.Signers, ...existSigner]
: [...existSigner]
});
} else {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders,
SignatureType: props.signatureType
});
}
});
await batchQuery(Documents);
} else {
setIsSubmit(false);
}
};
const batchQuery = async (Documents) => {
const serverUrl = localStorage.getItem("baseUrl");
const functionsUrl = `${serverUrl}functions/batchdocuments`;
const token = props.jwttoken
? { jwttoken: props.jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const functionsUrl = `${localStorage.getItem(
"baseUrl"
)}functions/batchdocuments`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
...token
};
const params = { Documents: JSON.stringify(Documents) };
try {
@@ -362,6 +400,7 @@ const BulkSendUi = (props) => {
fieldIndex
)
}
jwttoken={props?.jwttoken}
/>
</div>
))}
+11 -4
View File
@@ -16,7 +16,7 @@ import { useTranslation } from "react-i18next";
import ModalUi from "../primitives/ModalUi";
import QuotaCard from "../primitives/QuotaCard";
const Header = ({ showSidebar }) => {
const Header = ({ showSidebar, setIsMenu }) => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const { width } = useWindowSize();
@@ -40,7 +40,7 @@ const Header = ({ showSidebar }) => {
const toggleDropdown = () => {
setIsOpen(!isOpen);
if (width <= 768) {
showSidebar();
setIsMenu(false);
}
};
useEffect(() => {
@@ -140,15 +140,22 @@ const Header = ({ showSidebar }) => {
);
setShowNotification(false);
};
const handleSetupNowNavigation = () => {
const url = isStaging
? "https://staging-console.opensignlabs.com/fileadapter"
: "https://console.opensignlabs.com/fileadapter";
openInNewTab(url);
};
return (
<div>
{isEnableSubscription && showNotification && (
<div className="flex justify-between items-center shadow py-1 bg-[#CAE4FA] p-1">
<div className="flex justify-between items-center shadow py-1 bg-[#CAE4FA] p-1">
<div className="text-center text-[14px] ml-auto">
{t("header-news")}
<span
className="cursor-pointer font-medium underline text-blue-800"
onClick={() => navigate("/profile")}
onClick={() => handleSetupNowNavigation()}
>
{" " + t("header-news-btn") + "."}
</span>
@@ -1,16 +1,16 @@
import React from "react";
import { useTranslation } from "react-i18next";
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
function DefaultSignature(props) {
const { t } = useTranslation();
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
setDefaultSignAlert({
if (props?.xyPostion.length > 0) {
props?.setDefaultSignAlert({
isShow: true,
alertMessage: t("default-sign-alert")
});
} else {
setDefaultSignAlert({
props?.setDefaultSignAlert({
isShow: true,
alertMessage: t("please-select-position!")
});
@@ -22,22 +22,30 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
{t("signature")}
</div>
<div className="flex flex-col items-center mt-[10px] font-semibold">
<div className="flex flex-col items-center mt-[10px] font-semibold relative">
<p className="text-base-content">{t("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}
src={props?.defaultSignImg}
/>
</div>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
onClick={() => confirmToaddDefaultSign()}
disabled={!props?.isDefault}
>
{t("auto-sign-all")}
</button>
{!props.isDefault && (
<div className="absolute bg-black/70 text-white w-full h-full flex items-center justify-center text-[11px] cursor-default">
<span className="-rotate-45">
Document creator has disabled this option
</span>
</div>
)}
</div>
</div>
);
@@ -339,9 +339,9 @@ function DropdownWidgetOption(props) {
value={
props.fontSize ||
props.currWidgetsDetails?.options?.fontSize ||
"12"
12
}
onChange={(e) => props.setFontSize(e.target.value)}
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
>
{fontsizeArr.map((size, ind) => {
return (
@@ -5,11 +5,8 @@ import { isEnableSubscription } from "../../constant/const";
import { useTranslation } from "react-i18next";
import { Tooltip } from "react-tooltip";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
const EditTemplate = ({ template, onSuccess, jwttoken }) => {
const { t } = useTranslation();
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: template?.Name || "",
Note: template?.Note || "",
@@ -29,17 +26,13 @@ const EditTemplate = ({ template, onSuccess }) => {
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const subscribe = await checkIsSubscribed();
const subscribe = await checkIsSubscribed(jwttoken);
setIsSubscribe(subscribe.isValid);
}
};
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
// const handleFolder = (data) => {
// console.log("handleFolder ", data)
// setFolder(data);
// };
// Define a function to handle form submission
const handleSubmit = async (e) => {
@@ -85,7 +78,7 @@ const EditTemplate = ({ template, onSuccess }) => {
</div>
<div className="mb-[0.35rem]">
<label htmlFor="name" className="text-[13px]">
{t("name")}
{t("Title")}
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
@@ -158,7 +151,6 @@ const EditTemplate = ({ template, onSuccess }) => {
}
>
{t("auto-reminder")}
{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
+12 -8
View File
@@ -40,7 +40,8 @@ function Header({
handleRotationFun,
isDisableRotate,
templateId,
setIsDownloadModal
setIsDownloadModal,
disabledBackBtn
}) {
const { t } = useTranslation();
const filterPrefill =
@@ -48,6 +49,7 @@ function Header({
const isMobile = window.innerWidth < 767;
const [isDownloading, setIsDownloading] = useState("");
const isGuestSigner = localStorage.getItem("isGuestSigner");
const enabledBackBtn = disabledBackBtn === true ? false : true;
//function for show decline alert
const handleDeclinePdfAlert = async () => {
@@ -332,13 +334,15 @@ function Header({
<i className="fa-light fa-gear fa-lg"></i>
</button>
)}
<button
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
{t("back")}
</button>
{enabledBackBtn && (
<button
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
{t("back")}
</button>
)}
<button
disabled={isMailSend && true}
data-tut="headerArea"
+11 -20
View File
@@ -356,18 +356,13 @@ function Placeholder(props) {
if (props.data && props?.pos?.type !== textWidget) {
props.setUniqueId(props?.data?.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
);
const checkIndex = props.xyPostion
.filter((data) => data.Role !== "prefill")
.findIndex((data) => data.Id === props.data.Id);
props.setIsSelectId(checkIndex || 0);
} else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId);
props.setUniqueId(props?.data?.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
);
props.setIsSelectId(checkIndex || 0);
}
//checking widget's type and open widget copy modal for required widgets
@@ -417,7 +412,7 @@ function Placeholder(props) {
data?.format,
null,
null,
props.fontSize || props.pos?.options?.fontSize || "12",
props.fontSize || props.pos?.options?.fontSize || 12,
props.fontColor || props.pos?.options?.fontColor || "black"
);
setSelectDate({ date: date, format: data?.format });
@@ -450,17 +445,13 @@ function Placeholder(props) {
handleOnClickSettingIcon();
}}
className="fa-light fa-gear icon"
style={{
color: "#188ae2",
right: "29px",
top: "-19px"
}}
style={{ color: "#188ae2", right: "29px", top: "-19px" }}
></i>
) : (
/* condition to add setting icon for placeholder & template flow for all widgets except signature and date */
((!props?.pos?.type && props.pos.isStamp) ||
(props?.pos?.type &&
!["date", "signature"].includes(props.pos.type) &&
!["date"].includes(props.pos.type) &&
!props.isSignYourself)) && (
<i
onClick={(e) => {
@@ -474,7 +465,7 @@ function Placeholder(props) {
className="fa-light fa-gear icon"
style={{
color: "#188ae2",
right: props?.pos?.type === textWidget ? "32px" : "47px",
right: props?.pos?.type === textWidget ? "32px" : "51px",
top: "-19px"
}}
></i>
@@ -656,7 +647,7 @@ function Placeholder(props) {
const fontSize = (size || 12) * containerScale * props.scale;
//isMinHeight to set text box minimum height
if (isMinHeight) {
return fontSize * 1.5 + "px";
return fontSize * 1.2 + "px";
} else {
return fontSize + "px";
}
@@ -843,7 +834,7 @@ function Placeholder(props) {
)}
{isTabAndMobile ? (
<div
className="flex items-stretch"
className="flex items-stretch justify-center"
style={{
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
@@ -953,8 +944,8 @@ function Placeholder(props) {
<span>{t("font-size")} :</span>
<select
className="ml-[3px] md:ml:[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={props.fontSize || clickonWidget.options?.fontSize || "12"}
onChange={(e) => props.setFontSize(e.target.value)}
value={props.fontSize || clickonWidget.options?.fontSize || 12}
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
>
{fontsizeArr.map((size, ind) => {
return (
@@ -361,6 +361,7 @@ function RenderPdf({
fontColor={fontColor}
setFontColor={setFontColor}
isResize={isResize}
unSignedWidgetId={unSignedWidgetId}
/>
</React.Fragment>
);
@@ -555,6 +556,7 @@ function RenderPdf({
fontColor={fontColor}
setFontColor={setFontColor}
isResize={isResize}
unSignedWidgetId={unSignedWidgetId}
/>
</React.Fragment>
);
@@ -9,15 +9,13 @@ function SelectLanguage(props) {
{ value: "es", text: "Española" },
{ value: "fr", text: "Français" }
];
const [lang, setLang] = useState(i18next.language || "en");
const defaultLanguage = i18next.language || "en";
const [lang, setLang] = useState(defaultLanguage);
// This function put query that helps to change the language
const handleChangeLang = (e) => {
setLang(e.target.value);
i18n.changeLanguage(e.target.value);
props?.updateExtUser &&
props.updateExtUser({
language: e.target.value
});
props?.updateExtUser && props.updateExtUser({ language: e.target.value });
};
return (
<div
@@ -32,9 +30,7 @@ function SelectLanguage(props) {
!props.isProfile ? " md:w-[15%] w-[50%]" : "w-[180px]"
} op-select op-select-bordered bg-white op-select-sm `}
>
<option disabled selected>
select
</option>
<option disabled>select</option>
{languages.map((item) => {
return (
<option key={item.value} value={item.value}>
+419 -359
View File
@@ -1,39 +1,20 @@
import React, { useRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import SignatureCanvas from "react-signature-canvas";
function SignPad({
isSignPad,
isStamp,
setIsImageSelect,
onSaveSign,
setIsSignPad,
setImage,
isImageSelect,
imageRef,
onImageChange,
onSaveImage,
image,
defaultSign,
setSignature,
myInitial,
isInitial,
setIsInitial,
setIsStamp,
widgetType,
currWidgetsDetails,
setCurrWidgetsDetails
}) {
function SignPad(props) {
const { t } = useTranslation();
const [penColor, setPenColor] = useState("blue");
const allColor = ["blue", "red", "black"];
const canvasRef = useRef(null);
const [isDefaultSign, setIsDefaultSign] = useState(false);
const [isTab, setIsTab] = useState("draw");
const [isTab, setIsTab] = useState("");
const [isSignImg, setIsSignImg] = useState("");
const [signValue, setSignValue] = useState("");
const [textWidth, setTextWidth] = useState(0);
const [textHeight, setTextHeight] = useState(0);
const [signatureType, setSignatureType] = useState("draw");
const [signatureType, setSignatureType] = useState("");
const [isSignTypes, setIsSignTypes] = useState(true);
const fontOptions = [
{ value: "Fasthand" },
{ value: "Dancing Script" },
@@ -53,24 +34,58 @@ function SignPad({
const jsonSender = JSON.parse(senderUser);
const currentUserName = jsonSender && jsonSender?.name;
useEffect(() => {
handleTab();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.signatureTypes]);
function handleTab() {
const signtypes = props?.signatureTypes || [];
const index = signtypes?.findIndex((x) => x.enabled === true);
if (index !== -1) {
setIsSignTypes(true);
const tab = props?.signatureTypes[index].name;
if (tab === "draw") {
setIsTab("draw");
setSignatureType("draw");
} else if (tab === "upload") {
props?.setIsImageSelect(true);
setIsTab("uploadImage");
} else if (tab === "typed") {
setIsTab("type");
} else if (tab === "default") {
setIsTab("mysignature");
} else {
setIsTab(true);
}
} else {
setIsSignTypes(false);
}
}
function isTabEnabled(tabName) {
const isEnabled = props?.signatureTypes.find(
(x) => x.name === tabName
)?.enabled;
return isEnabled;
}
//function for clear signature image
const handleClear = () => {
setCurrWidgetsDetails({});
props?.setCurrWidgetsDetails({});
if (isTab === "draw") {
if (canvasRef.current) {
canvasRef.current.clear();
} else if (isStamp) {
setImage("");
} else if (props?.isStamp) {
props?.setImage("");
}
setIsSignImg("");
} else if (isTab === "uploadImage") {
setImage("");
props?.setImage("");
}
// setIsInitial(false);
};
//function for set signature url
const handleSignatureChange = (data) => {
setSignature(data);
props?.setSignature(data);
setIsSignImg(data);
};
//save button component
@@ -88,58 +103,57 @@ function SignPad({
)}
<button
onClick={() => {
setCurrWidgetsDetails({});
if (!image) {
props?.setCurrWidgetsDetails({});
if (!props?.image) {
if (isTab === "mysignature") {
setIsSignImg("");
if (isInitial) {
onSaveSign(signatureType, "initials");
if (props?.isInitial) {
props?.onSaveSign(signatureType, "initials");
} else {
onSaveSign(null, "default");
props?.onSaveSign(null, "default");
}
} else {
if (isTab === "type") {
setIsSignImg("");
onSaveSign(
props?.onSaveSign(
null,
false,
!isInitial && textWidth > 150 ? 150 : textWidth,
!isInitial && textHeight > 35 ? 35 : textHeight
!props?.isInitial && textWidth > 150 ? 150 : textWidth,
!props?.isInitial && textHeight > 35 ? 35 : textHeight
);
} else {
setIsSignImg("");
canvasRef.current.clear();
onSaveSign(signatureType);
props?.onSaveSign(signatureType);
}
}
setPenColor("blue");
} else {
setIsSignImg("");
onSaveImage(signatureType);
props?.onSaveImage(signatureType);
}
setIsSignPad(false);
setIsInitial(false);
setIsImageSelect(false);
props?.setIsSignPad(false);
props?.setIsInitial(false);
props?.setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("draw");
props?.setImage();
handleTab();
setSignValue("");
setIsStamp(false);
props?.setIsStamp(false);
}}
type="button"
className={`${
isSignImg || image || isDefaultSign || textWidth
isSignImg || props?.image || isDefaultSign || textWidth
? ""
: "pointer-events-none"
} op-btn op-btn-primary shadow-lg`}
disabled={
(isTab === "draw" && isSignImg) ||
(isTab === "image" && image) ||
(isTab === "image" && props?.image) ||
(isTab === "mysignature" && isDefaultSign) ||
(isTab === "type" && signValue)
? false
: image
: props?.image
? false
: true
}
@@ -151,12 +165,12 @@ function SignPad({
};
//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;
if (props?.currWidgetsDetails && canvasRef.current) {
const isWidgetType = props?.currWidgetsDetails?.type;
const signatureType = props?.currWidgetsDetails?.signatureType;
const url = props?.currWidgetsDetails?.SignUrl;
//checking widget type and draw type signature url
if (isInitial) {
if (props?.isInitial) {
if (isWidgetType === "initials" && signatureType === "draw" && url) {
canvasRef.current.fromDataURL(url);
}
@@ -171,12 +185,12 @@ function SignPad({
const trimmedName = currentUserName && currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
const userName = props?.isInitial ? firstCharacter : currentUserName;
setSignValue(userName || "");
setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignPad]);
}, [props.isSignPad]);
useEffect(() => {
const loadFont = async () => {
try {
@@ -204,7 +218,7 @@ function SignPad({
? signValue?.trim()
: currentUserName?.trim();
const firstCharacter = trimmedName?.charAt(0);
const userName = isInitial ? firstCharacter : signValue;
const userName = props?.isInitial ? firstCharacter : signValue;
setSignValue(userName);
convertToImg(fontSelect, userName);
}
@@ -233,7 +247,7 @@ function SignPad({
// Draw the text content on the canvas
const ctx = canvasElement.getContext("2d");
const pixelRatio = window.devicePixelRatio || 1;
const addExtraWidth = isInitial ? 10 : 50;
const addExtraWidth = props?.isInitial ? 10 : 50;
const width = span.offsetWidth + addExtraWidth;
const height = span.offsetHeight;
setTextWidth(width);
@@ -255,7 +269,7 @@ function SignPad({
document.body.removeChild(span);
// Convert the canvas to image data
const dataUrl = canvasElement.toDataURL("image/png");
setSignature(dataUrl);
props?.setSignature(dataUrl);
};
const PenColorComponent = (props) => {
@@ -297,319 +311,365 @@ function SignPad({
</div>
);
};
// `handleCancelBtn` function trigger when user click on cross button
const handleCancelBtn = () => {
setPenColor("blue");
props?.setIsSignPad(false);
props?.setIsInitial(false);
props?.setIsImageSelect(false);
setIsDefaultSign(false);
props?.setImage();
handleTab();
setSignValue("");
props?.setIsStamp(false);
};
return (
<div>
{isSignPad && (
{props?.isSignPad && (
<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 className="text-base-content font-bold text-lg">
{widgetType === "image" ||
currWidgetsDetails?.type === "image"
? t("upload-image")
: t("upload-stamp-image")}
</span>
) : (
<>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("draw");
setImage();
if (isSignImg) {
setSignature(isSignImg);
}
}}
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("draw")}
{isSignTypes ? (
<>
<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">
{props?.isStamp ? (
<span className="text-base-content font-bold text-lg">
{props?.widgetType === "image" ||
props?.currWidgetsDetails?.type === "image"
? t("upload-image")
: t("upload-stamp-image")}
</span>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("upload-image")}
</span>
</div>
<div>
<span
onClick={() => {
setIsDefaultSign(false);
setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
setImage();
}}
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("type")}
</span>
</div>
{!isInitial && defaultSign ? (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-signature")}
</span>
</div>
) : (
isInitial &&
myInitial && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-initials")}
</span>
</div>
)
<>
{isTabEnabled("draw") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(false);
setIsTab("draw");
props?.setImage();
if (isSignImg) {
props?.setSignature(isSignImg);
}
}}
className={`${
isTab === "draw"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("draw")}
</span>
</div>
)}
{isTabEnabled("upload") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(true);
setIsTab("uploadImage");
setSignatureType("");
}}
className={`${
isTab === "uploadImage"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("upload-image")}
</span>
</div>
)}
{isTabEnabled("typed") && (
<div>
<span
onClick={() => {
setIsDefaultSign(false);
props?.setIsImageSelect(false);
setIsTab("type");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "type"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("type")}
</span>
</div>
)}
{!props?.isInitial &&
props?.defaultSign &&
isTabEnabled("default") ? (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
props?.setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-signature")}
</span>
</div>
) : (
props?.isInitial &&
props?.myInitial &&
isTabEnabled("default") && (
<div>
<span
onClick={() => {
setIsDefaultSign(true);
props?.setIsImageSelect(true);
setIsTab("mysignature");
setSignatureType("");
props?.setImage();
}}
className={`${
isTab === "mysignature"
? "op-link-primary"
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
{t("my-initials")}
</span>
</div>
)
)}
</>
)}
</>
)}
</div>
</div>
</div>
<div
className="text-[1.5rem] cursor-pointer"
onClick={() => {
setPenColor("blue");
setIsSignPad(false);
setIsInitial(false);
setIsImageSelect(false);
setIsDefaultSign(false);
setImage();
setIsTab("draw");
setSignatureType("draw");
setSignValue("");
setIsStamp(false);
}}
>
&times;
</div>
</div>
<div className="p-[20px] h-full">
{isDefaultSign ? (
<>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
}
>
<img
alt="stamp img"
className="w-full h-full object-contain bg-white"
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
) : isImageSelect || isStamp ? (
!image ? (
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
}
onClick={() => imageRef.current.click()}
>
<input
type="file"
onChange={onImageChange}
className="filetype"
accept="image/png,image/jpeg"
ref={imageRef}
hidden
/>
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
<div className="text-[10px]">{t("upload")}</div>
</div>
</div>
) : (
<>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
marginBottom: 6,
overflow: "hidden"
}}
className={
isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
}
>
<img
alt="print img"
ref={imageRef}
src={image.src}
draggable="false"
className=" object-contain h-full w-full"
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div className="flex justify-between items-center">
<span className="mr-[5px] text-[12px]">
{isInitial ? t("initial-teb") : t("signature-tab")}:
</span>
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
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) => {
setSignValue(e.target.value);
convertToImg(fontSelect, e.target.value);
}}
/>
</div>
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
{fontOptions.map((font, ind) => {
return (
<div
key={ind}
style={{
cursor: "pointer",
fontFamily: font.value,
backgroundColor:
fontSelect === font.value && "rgb(206 225 247)"
}}
onClick={() => {
setFontSelect(font.value);
convertToImg(font.value, signValue);
}}
>
<div
style={{
padding: "5px 10px 5px 10px",
fontSize: "20px",
color: penColor
}}
>
{signValue ? signValue : "Your signature"}
</div>
</div>
);
})}
</div>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
<div
className="text-[1.5rem] cursor-pointer"
onClick={handleCancelBtn}
>
&times;
</div>
</div>
) : (
<>
<div className="flex justify-center">
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
className: isInitial
? "intialSignatureCanvas"
: "signatureCanvas",
style: {
border: "1.6px solid #007bff",
borderRadius: "2px"
}
}}
// backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(canvasRef.current?.toDataURL())
}
dotSize={1}
/>
<div className="p-[20px] h-full">
{isDefaultSign ? (
<>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
}
>
<img
alt="stamp img"
className="w-full h-full object-contain bg-white"
draggable="false"
src={
props?.isInitial
? props?.myInitial
: props?.defaultSign
}
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
) : props?.isImageSelect || props?.isStamp ? (
!props?.image ? (
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
marginBottom: 6,
cursor: "pointer"
}}
className={
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
}
onClick={() => props?.imageRef.current.click()}
>
<input
type="file"
onChange={props?.onImageChange}
className="filetype"
accept="image/png,image/jpeg"
ref={props?.imageRef}
hidden
/>
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
<div className="text-[10px]">{t("upload")}</div>
</div>
</div>
) : (
<>
<div className="flex justify-center">
<div
style={{
border: "1.3px solid #007bff",
borderRadius: "2px",
marginBottom: 6,
overflow: "hidden"
}}
className={
props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas"
}
>
<img
alt="print img"
ref={props?.imageRef}
src={props?.image.src}
draggable="false"
className=" object-contain h-full w-full"
/>
</div>
</div>
<div className="flex justify-end">
<SaveBtn />
</div>
</>
)
) : isTab === "type" ? (
<div>
<div className="flex justify-between items-center">
<span className="mr-[5px] text-[12px]">
{props?.isInitial
? t("initial-teb")
: t("signature-tab")}
:
</span>
<input
maxLength={props?.isInitial ? 3 : 30}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
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) => {
setSignValue(e.target.value);
convertToImg(fontSelect, e.target.value);
}}
/>
</div>
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
{fontOptions.map((font, ind) => {
return (
<div
key={ind}
style={{
cursor: "pointer",
fontFamily: font.value,
backgroundColor:
fontSelect === font.value &&
"rgb(206 225 247)"
}}
onClick={() => {
setFontSelect(font.value);
convertToImg(font.value, signValue);
}}
>
<div
style={{
padding: "5px 10px 5px 10px",
fontSize: "20px",
color: penColor
}}
>
{signValue ? signValue : "Your signature"}
</div>
</div>
);
})}
</div>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
) : (
<>
<div className="flex justify-center">
<SignatureCanvas
ref={canvasRef}
penColor={penColor}
canvasProps={{
className: props?.isInitial
? "intialSignatureCanvas"
: "signatureCanvas",
style: {
border: "1.6px solid #007bff",
borderRadius: "2px"
}
}}
// backgroundColor="rgb(255, 255, 255)"
onEnd={() =>
handleSignatureChange(
canvasRef.current?.toDataURL()
)
}
dotSize={1}
/>
</div>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent />
<SaveBtn />
</div>
</>
)}
</div>
</>
) : (
<div className="">
<div className="relative flex flex-row items-center justify-between">
<div className="text-base-content font-bold text-lg">
Signature
</div>
<div className="flex flex-row justify-between mt-[10px]">
<PenColorComponent />
<SaveBtn />
<div
className="text-[1.5rem] cursor-pointer"
onClick={handleCancelBtn}
>
&times;
</div>
</>
)}
</div>
</div>
<div className="mx-3 mb-6 mt-3">
<p>{t("at-least-one-signature-type")}</p>
</div>
</div>
)}
</div>
</div>
)}
@@ -21,9 +21,11 @@ function TextFontSetting(props) {
value={
props.fontSize ||
props.currWidgetsDetails?.options?.fontSize ||
"12"
12
}
onChange={(e) => props.setFontSize(e.target.value)}
onChange={(e) => {
props.setFontSize(parseInt(e.target.value));
}}
>
{fontsizeArr.map((size, ind) => {
return (
@@ -4,6 +4,7 @@ import RecipientList from "./RecipientList";
import { useDrag } from "react-dnd";
import WidgetList from "./WidgetList";
import {
color,
radioButtonWidget,
textInputWidget,
textWidget,
@@ -43,163 +44,69 @@ function WidgetComponent({
const [isSignersModal, setIsSignersModal] = useState(false);
const [, dropdown] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 5,
text: "dropdown"
},
item: { type: "BOX", id: 5, text: "dropdown" },
collect: (monitor) => ({
isDragDropdown: !!monitor.isDragging()
})
});
const [, checkbox] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 6,
text: "checkbox"
},
collect: (monitor) => ({
isDragCheck: !!monitor.isDragging()
})
item: { type: "BOX", id: 6, text: "checkbox" },
collect: (monitor) => ({ isDragCheck: !!monitor.isDragging() })
});
const [, textInput] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 7,
text: textInputWidget
},
collect: (monitor) => ({
isDragTextInput: !!monitor.isDragging()
})
item: { type: "BOX", id: 7, text: textInputWidget },
collect: (monitor) => ({ isDragTextInput: !!monitor.isDragging() })
});
const [, initials] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 8,
text: "initials"
},
collect: (monitor) => ({
isDragInitial: !!monitor.isDragging()
})
item: { type: "BOX", id: 8, text: "initials" },
collect: (monitor) => ({ isDragInitial: !!monitor.isDragging() })
});
const [, name] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 9,
text: "name"
},
collect: (monitor) => ({
isDragName: !!monitor.isDragging()
})
item: { type: "BOX", id: 9, text: "name" },
collect: (monitor) => ({ isDragName: !!monitor.isDragging() })
});
const [, company] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 10,
text: "company"
},
collect: (monitor) => ({
isDragCompany: !!monitor.isDragging()
})
item: { type: "BOX", id: 10, text: "company" },
collect: (monitor) => ({ isDragCompany: !!monitor.isDragging() })
});
const [, jobTitle] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 11,
text: "job title"
},
collect: (monitor) => ({
isDragJobtitle: !!monitor.isDragging()
})
item: { type: "BOX", id: 11, text: "job title" },
collect: (monitor) => ({ isDragJobtitle: !!monitor.isDragging() })
});
const [, date] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 12,
text: "date"
},
collect: (monitor) => ({
isDragDate: !!monitor.isDragging()
})
item: { type: "BOX", id: 12, text: "date" },
collect: (monitor) => ({ isDragDate: !!monitor.isDragging() })
});
const [, image] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 13,
text: "image"
},
collect: (monitor) => ({
isDragImage: !!monitor.isDragging()
})
item: { type: "BOX", id: 13, text: "image" },
collect: (monitor) => ({ isDragImage: !!monitor.isDragging() })
});
const [, email] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 14,
text: "email"
},
collect: (monitor) => ({
isDragEmail: !!monitor.isDragging()
})
item: { type: "BOX", id: 14, text: "email" },
collect: (monitor) => ({ isDragEmail: !!monitor.isDragging() })
});
const [, radioButton] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 15,
text: radioButtonWidget
},
collect: (monitor) => ({
isDragRadiotton: !!monitor.isDragging()
})
item: { type: "BOX", id: 15, text: radioButtonWidget },
collect: (monitor) => ({ isDragRadiotton: !!monitor.isDragging() })
});
const [, text] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 16,
text: textWidget
},
collect: (monitor) => ({
isDragText: !!monitor.isDragging()
})
item: { type: "BOX", id: 16, text: textWidget },
collect: (monitor) => ({ isDragText: !!monitor.isDragging() })
});
const isMobile = window.innerWidth < 767;
const scrollContainerRef = useRef(null);
const [widget, setWidget] = useState([]);
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const handleModal = () => {
setIsSignersModal(!isSignersModal);
};
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css";
import RegexParser from "regex-parser";
import { textInputWidget, textWidget } from "../../constant/Utils";
import {
signatureTypes,
textInputWidget,
textWidget
} from "../../constant/Utils";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip";
@@ -11,6 +15,7 @@ import { useTranslation } from "react-i18next";
const WidgetNameModal = (props) => {
const { t } = useTranslation();
const signTypes = props?.signatureType || signatureTypes;
const [formdata, setFormdata] = useState({
name: "",
defaultValue: "",
@@ -21,6 +26,7 @@ const WidgetNameModal = (props) => {
const [isValid, setIsValid] = useState(true);
const statusArr = ["Required", "Optional"];
const inputOpt = ["text", "email", "number"];
const [signatureType, setSignatureType] = useState([]);
useEffect(() => {
if (props.defaultdata) {
@@ -40,12 +46,32 @@ const WidgetNameModal = (props) => {
name: props.defaultdata?.options?.name || ""
});
}
if (signTypes.length > 0) {
const defaultSignatureType = signTypes || [];
setSignatureType(defaultSignatureType);
}
// eslint-disable-next-line
}, [props.defaultdata]);
const handleSubmit = (e) => {
e.preventDefault();
if (props.handleData) {
props.handleData(formdata);
if (["signature", "initials"].includes(props.defaultdata?.type)) {
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
const isDefaultSignTypeOnly =
enabledSignTypes?.length === 1 &&
enabledSignTypes[0]?.name === "default";
if (enabledSignTypes.length === 0) {
alert(t("at-least-one-signature-type"));
} else if (isDefaultSignTypeOnly) {
alert(t("expect-default-one-more-signature-type"));
} else {
const data = { ...formdata, signatureType };
props.handleData(data, props.defaultdata?.type);
}
} else {
props.handleData(formdata);
}
setFormdata({
name: "",
defaultValue: "",
@@ -53,6 +79,7 @@ const WidgetNameModal = (props) => {
hint: "",
textvalidate: ""
});
setSignatureType(signTypes);
}
};
const handleChange = (e) => {
@@ -102,6 +129,15 @@ const WidgetNameModal = (props) => {
}
}
}
const handleCheckboxChange = (index) => {
// Create a copy of the signatureType array
const updatedSignatureType = [...signatureType];
// Toggle the enabled value for the clicked item
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
// Update the state with the modified array
setSignatureType(updatedSignatureType);
};
return (
<ModalUi
isOpen={props.isOpen}
@@ -111,24 +147,30 @@ const WidgetNameModal = (props) => {
<form
onSubmit={handleSubmit}
className={`${
props.defaultdata?.type === textInputWidget ? "pt-0" : ""
props.defaultdata?.type === textInputWidget
? "pt-0"
: ["signature", "initials"].includes(props.defaultdata?.type)
? "pt-2"
: ""
} p-[20px] text-base-content`}
>
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
{t("name")}
<span className="text-[red]"> *</span>
</label>
<input
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)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
{!["signature", "initials"].includes(props.defaultdata?.type) && (
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
{t("name")}
<span className="text-[red]"> *</span>
</label>
<input
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)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
)}
{props.defaultdata?.type === textInputWidget && (
<>
{isEnableSubscription && (
@@ -226,30 +268,63 @@ const WidgetNameModal = (props) => {
</div>
</>
)}
<div className="mb-[0.75rem]">
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div key={ind} className="flex flex-row gap-[5px] items-center">
<input
className="mr-[2px] op-radio op-radio-xs"
type="radio"
name="status"
onChange={() =>
setFormdata({ ...formdata, status: data.toLowerCase() })
}
checked={
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div className="text-[13px] font-medium">
{t(`widget-status.${data}`)}
{!["signature", "initials"].includes(props.defaultdata?.type) && (
<div className="mb-[0.75rem]">
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
{statusArr.map((data, ind) => {
return (
<div
key={ind}
className="flex flex-row gap-[5px] items-center"
>
<input
className="mr-[2px] op-radio op-radio-xs"
type="radio"
name="status"
onChange={() =>
setFormdata({ ...formdata, status: data.toLowerCase() })
}
checked={
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div className="text-[13px] font-medium">
{t(`widget-status.${data}`)}
</div>
</div>
</div>
);
})}
);
})}
</div>
</div>
</div>
)}
{["signature", "initials"].includes(props.defaultdata?.type) && (
<div className="mb-[0.75rem]">
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
{t("allowed-signature-types")}
</label>
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
{signatureType.map((type, i) => {
return (
<div key={i} className="flex flex-row gap-[5px] items-center">
<input
className="mr-[2px] op-checkbox op-checkbox-xs"
type="checkbox"
name="signaturetype"
onChange={() => handleCheckboxChange(i)}
checked={type.enabled}
/>
<div
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
title={`Enabling this allow signers to ${type.name} signature`}
>
{type.name}
</div>
</div>
);
})}
</div>
</div>
)}
{props.defaultdata?.type === textInputWidget && (
<div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]">
@@ -276,9 +351,9 @@ const WidgetNameModal = (props) => {
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
props.fontSize || props.defaultdata?.options?.fontSize || "12"
props.fontSize || props.defaultdata?.options?.fontSize || 12
}
onChange={(e) => props.setFontSize(e.target.value)}
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
>
{fontsizeArr.map((size, ind) => {
return (
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import Parse from "parse";
import AsyncSelect from "react-select/async";
import { useTranslation } from "react-i18next";
import axios from "axios";
const SelectSigners = (props) => {
const { t } = useTranslation();
@@ -31,17 +31,19 @@ const SelectSigners = (props) => {
const loadOptions = async (inputValue) => {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
contactbook.equalTo(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
if (inputValue.length > 1) {
contactbook.matches("Name", new RegExp(inputValue, "i"));
}
contactbook.notEqualTo("IsDeleted", true);
const contactRes = await contactbook.find();
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsigners`;
const token = props.jwttoken
? { jwttoken: props.jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token
};
const searchEmail = inputValue ? inputValue : "";
const axiosRes = await axios.post(url, { searchEmail }, { headers });
const contactRes = axiosRes?.data?.result || [];
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
@@ -52,11 +54,9 @@ const SelectSigners = (props) => {
!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) => ({
@@ -68,7 +68,6 @@ const SelectSigners = (props) => {
console.log("err", error);
}
};
return (
<div className="h-full px-[20px] py-[10px] text-base-content">
<div className="w-full mx-auto p-[8px]">
@@ -85,6 +84,7 @@ const SelectSigners = (props) => {
loadOptions={loadOptions}
onChange={handleOptions}
unstyled
onFocus={() => loadOptions()}
classNames={{
control: () =>
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
@@ -25,7 +25,7 @@ const SuggestionInput = (props) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
(async () => {
const res = await findContact(inputValue);
const res = await findContact(inputValue, props.jwttoken);
if (res?.length > 0) {
setSuggestions(res);
setShowSuggestions(true);
@@ -37,6 +37,7 @@ const SuggestionInput = (props) => {
}, 1000);
}
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputValue]);
const handleInputChange = async (e) => {
+139 -106
View File
@@ -34,7 +34,8 @@ export async function fetchSubscription(
extUserId,
contactObjId,
isGuestSign = false,
isPublic = false
isPublic = false,
jwtToken
) {
try {
const Extand_Class = localStorage.getItem("Extand_Class");
@@ -48,10 +49,13 @@ export async function fetchSubscription(
}
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsubscriptions`;
const token = jwtToken
? { jwttoken: jwtToken }
: { sessionToken: localStorage.getItem("accesstoken") };
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
...token
};
const params = isGuestSign
? { contactId: contactObjId }
@@ -122,9 +126,9 @@ export async function fetchSubscriptionInfo() {
}
}
//function to get subcripition details from subscription class
export async function checkIsSubscribed() {
export async function checkIsSubscribed(jwttoken) {
try {
const res = await fetchSubscription();
const res = await fetchSubscription("", "", false, false, jwttoken);
if (res.plan === "freeplan") {
return { plan: res.plan, isValid: false, adminId: res?.adminId };
} else if (res.billingDate) {
@@ -226,12 +230,25 @@ export const pdfNewWidthFun = (divRef) => {
};
//`contractUsers` function is used to get contract_User details
export const contractUsers = async () => {
export const contractUsers = async (jwttoken) => {
try {
const userDetails = await Parse.Cloud.run("getUserDetails");
const url = `${localStorage.getItem("baseUrl")}functions/getUserDetails`;
const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken");
const token = jwttoken
? { jwttoken: jwttoken }
: { "X-Parse-Session-Token": accesstoken };
const headers = {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
...token
}
};
const userDetails = await axios.post(url, {}, headers);
let data = [];
if (userDetails) {
const json = JSON.parse(JSON.stringify(userDetails));
if (userDetails?.data?.result) {
const json = JSON.parse(JSON.stringify(userDetails.data.result));
data.push(json);
}
return data;
@@ -446,29 +463,29 @@ export const defaultWidthHeight = (type) => {
case "stamp":
return { width: 150, height: 60 };
case "checkbox":
return { width: 15, height: 15 };
return { width: 15, height: 19 };
case textInputWidget:
return { width: 150, height: 25 };
return { width: 150, height: 19 };
case "dropdown":
return { width: 120, height: 22 };
case "initials":
return { width: 50, height: 50 };
case "name":
return { width: 150, height: 25 };
return { width: 150, height: 19 };
case "company":
return { width: 150, height: 25 };
return { width: 150, height: 19 };
case "job title":
return { width: 150, height: 25 };
return { width: 150, height: 19 };
case "date":
return { width: 100, height: 20 };
case "image":
return { width: 70, height: 70 };
case "email":
return { width: 150, height: 20 };
return { width: 150, height: 19 };
case radioButtonWidget:
return { width: 5, height: 10 };
case textWidget:
return { width: 150, height: 25 };
return { width: 150, height: 19 };
default:
return { width: 150, height: 60 };
}
@@ -620,10 +637,10 @@ export const signPdfFun = async (
return { status: "error", message: "something went wrong." };
}
}
//change image width and height to 100/40 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
//change image width and height to 300/120 in png base64
const imagebase64 = await changeImageWH(base64Sign);
//remove suffiix of base64 (without type)
const suffixbase64 = imagebase64 && imagebase64.split(",").pop();
const params = {
mailProvider: activeMailAdapter,
@@ -645,9 +662,7 @@ export const signPdfFun = async (
status: "error",
message: "Currently encrypted pdf files are not supported."
};
} else if (
e.message === "PKCS#12 MAC could not be verified. Invalid password?"
) {
} else if (e?.message?.includes("password")) {
return { status: "error", message: "PFX file password is invalid." };
} else {
return { status: "error", message: "something went wrong." };
@@ -670,7 +685,6 @@ export const createDocument = async (
) => {
if (template && template.length > 0) {
const Doc = template[0];
let placeholdersArr = [];
if (placeholders?.length > 0) {
placeholdersArr = placeholders;
@@ -688,6 +702,9 @@ export const createDocument = async (
}
});
}
const SignatureType = Doc?.SignatureType
? { SignatureType: Doc?.SignatureType }
: {};
const data = {
Name: Doc.Name,
URL: pdfUrl,
@@ -712,7 +729,8 @@ export const createDocument = async (
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5),
IsEnableOTP: Doc?.IsEnableOTP || false,
IsTourEnabled: Doc?.IsTourEnabled || false,
FileAdapterId: Doc?.FileAdapterId || ""
FileAdapterId: Doc?.FileAdapterId || "",
...SignatureType
};
try {
@@ -998,7 +1016,7 @@ export const calculateInitialWidthHeight = (widgetData) => {
const intialText = widgetData;
const span = document.createElement("span");
span.textContent = intialText;
span.style.font = `14px`; // here put your text size and font family
span.style.font = `12px`; // here put your text size and font family
span.style.display = "hidden";
document.body.appendChild(span);
const width = span.offsetWidth;
@@ -1151,10 +1169,7 @@ export function onSaveSign(
Height: posHeight,
SignUrl: signatureImg,
signatureType: type && type,
options: {
...position.options,
response: signatureImg
}
options: { ...position.options, response: signatureImg }
};
}
return position;
@@ -1288,16 +1303,6 @@ export const changeImageWH = async (base64Image) => {
});
};
//function to calculate font size of text area widgets
const calculateFontSize = (position, containerScale, signyourself) => {
const font = position?.options?.fontSize || 12;
if (!signyourself && position?.isMobile && position?.scale) {
return font / position?.scale / containerScale;
} else {
return font / containerScale;
}
};
const getWidgetsFontColor = (type) => {
switch (type) {
case "red":
@@ -1313,14 +1318,7 @@ const getWidgetsFontColor = (type) => {
}
};
//function for embed multiple signature using pdf-lib
export const multiSignEmbed = async (
widgets,
pdfDoc,
signyourself,
scale,
pdfOriginalWH,
containerWH
) => {
export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
// `fontBytes` is used to embed custom font in pdf
const fontBytes = await fileasbytes(
"https://cdn.opensignlabs.com/webfonts/times.ttf"
@@ -1330,11 +1328,6 @@ export const multiSignEmbed = async (
let hasError = false;
for (let item of widgets) {
if (hasError) break; // Stop the outer loop if an error occurred
const containerScale = getContainerScale(
pdfOriginalWH,
item?.pageNumber,
containerWH
);
const typeExist = item.pos.some((data) => data?.type);
let updateItem;
@@ -1364,11 +1357,16 @@ export const multiSignEmbed = async (
const form = pdfDoc.getForm();
const page = pages[pageNo - 1];
const images = await Promise.all(
widgetsPositionArr.map(async (url) => {
let signUrl = url.SignUrl && url.SignUrl;
if (signUrl) {
const res = await fetch(signUrl);
return res.arrayBuffer();
widgetsPositionArr.map(async (widget) => {
// `SignUrl` this is wrong nomenclature and maintain for older code in this options we save base64 of signature image from sign pad
let signbase64 = widget.SignUrl && widget.SignUrl;
if (signbase64) {
let arr = signbase64.split(","),
mime = arr[0].match(/:(.*?);/)[1];
const res = await fetch(signbase64);
const arrayBuffer = await res.arrayBuffer();
const obj = { mimetype: mime, arrayBuffer: arrayBuffer };
return obj;
}
})
);
@@ -1379,17 +1377,17 @@ export const multiSignEmbed = async (
if (
["signature", "stamp", "initials", "image"].includes(position.type)
) {
if (position.ImageType && position.ImageType === "image/jpeg") {
img = await pdfDoc.embedJpg(images[id]);
if (images[id].mimetype === "image/png") {
img = await pdfDoc.embedPng(images[id].arrayBuffer);
} else {
img = await pdfDoc.embedPng(images[id]);
img = await pdfDoc.embedJpg(images[id].arrayBuffer);
}
} else if (!position.type) {
// to handle old widget when only stamp and signature are exists
if (position.ImageType && position.ImageType === "image/jpeg") {
img = await pdfDoc.embedJpg(images[id]);
if (images[id].mimetype === "image/png") {
img = await pdfDoc.embedPng(images[id].arrayBuffer);
} else {
img = await pdfDoc.embedPng(images[id]);
img = await pdfDoc.embedJpg(images[id].arrayBuffer);
}
}
let widgetWidth, widgetHeight;
@@ -1421,7 +1419,17 @@ export const multiSignEmbed = async (
return y;
}
} else {
return resizePos;
const WidgetsTypeTextExist = [
textWidget,
textInputWidget,
"name",
"company",
"job title",
"date",
"email"
].includes(position.type);
const yPosition = WidgetsTypeTextExist ? resizePos + 6 : resizePos;
return yPosition;
}
};
const color = position?.options?.fontColor;
@@ -1445,7 +1453,6 @@ export const multiSignEmbed = async (
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
const checkboxRandomId = "checkbox" + randomId();
if (
position?.options?.response &&
position?.options?.response?.length > 0
@@ -1497,12 +1504,6 @@ export const multiSignEmbed = async (
});
}
} else if (widgetTypeExist) {
const fontSize = calculateFontSize(
position,
containerScale,
signyourself
);
parseInt(fontSize);
let textContent;
if (position?.options?.response) {
textContent = position.options?.response;
@@ -1534,11 +1535,9 @@ export const multiSignEmbed = async (
lines.push(currentLine.trim());
return lines;
};
// Function to break text into lines based on when user go next line on press enter button
const breakTextIntoLines = (textContent, width) => {
const lines = [];
for (const word of textContent.split("\n")) {
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
//checking string length to container width
@@ -1555,7 +1554,6 @@ export const multiSignEmbed = async (
return lines;
};
//check if text content have `\n` string it means user press enter to go next line and handle condition
//else auto adjust text content according to container width
const lines = isNewOnEnterLineExist
@@ -1581,8 +1579,6 @@ export const multiSignEmbed = async (
y += 18; // Adjust the line height as needed
}
} else if (position.type === "dropdown") {
const fontsize = parseInt(position?.options?.fontSize) || 12;
const dropdownRandomId = "dropdown" + randomId();
const dropdown = form.createDropdown(dropdownRandomId);
dropdown.addOptions(position?.options?.values);
@@ -1597,10 +1593,10 @@ export const multiSignEmbed = async (
// - `FontSize` is the size you want to set (e.g., 12)
// - `Tf` specifies the font and size
// - `0 g` sets the text color to black
const defaultAppearance = `/Helv ${fontsize} Tf 0 g`;
const defaultAppearance = `/Helv ${fontSize} Tf 0 g`;
// Set the default appearance for the dropdown field
dropdown.acroField.setDefaultAppearance(defaultAppearance);
dropdown.setFontSize(fontsize);
dropdown.setFontSize(fontSize);
const dropdownObj = {
x: xPos(position),
y: yPos(position),
@@ -1681,7 +1677,6 @@ export const multiSignEmbed = async (
}
if (!hasError) {
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
// console.log("pdf", pdfBytes);
return pdfBytes;
} else {
return {
@@ -1764,14 +1759,17 @@ export const contactBook = async (objectId) => {
};
//function for getting document details from contract_Documents class
export const contractDocument = async (documentId) => {
export const contractDocument = async (documentId, JwtToken) => {
const data = { docId: documentId };
const token = JwtToken
? { jwtToken: JwtToken }
: { sessionToken: localStorage.getItem("accesstoken") };
const documentDeatils = await axios
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
...token
}
})
.then((Listdata) => {
@@ -1887,12 +1885,16 @@ export const handleCopyNextToWidget = (
//get position of previous widget and create new widget next to that widget on same data except
// xPosition and key
let newposition = position;
const calculateXPosition =
parseInt(position.xPosition) +
defaultWidthHeight(widgetType).width +
resizeBorderExtraWidth();
const calculateXPosition = parseInt(position.xPosition) + 10;
const calculateYPosition = parseInt(position.yPosition) + 10;
const newId = randomId();
newposition = { ...newposition, xPosition: calculateXPosition, key: newId };
newposition = {
...newposition,
xPosition: calculateXPosition,
yPosition: calculateYPosition,
key: newId
};
//if condition to create widget in request-sign flow
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
@@ -1967,20 +1969,28 @@ export const getAppLogo = async () => {
}
};
export const getTenantDetails = async (objectId) => {
export const getTenantDetails = async (objectId, jwttoken, contactId) => {
try {
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
tenantCreditsQuery.equalTo("UserId", {
__type: "Pointer",
className: "_User",
objectId: objectId
const url = `${localStorage.getItem("baseUrl")}functions/gettenant`;
const parseAppId = localStorage.getItem("parseAppId");
const accesstoken = localStorage.getItem("accesstoken");
const token = jwttoken
? { jwttoken: jwttoken }
: { "X-Parse-Session-Token": accesstoken };
const data = jwttoken ? {} : { userId: objectId, contactId: contactId };
const res = await axios.post(url, data, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
...token
}
});
const res = await tenantCreditsQuery.first();
if (res) {
const updateRes = JSON.parse(JSON.stringify(res));
const updateRes = JSON.parse(JSON.stringify(res.data.result));
return updateRes;
}
} catch (e) {
} catch (err) {
console.log("err in gettenant", err);
return "user does not exist!";
}
};
@@ -2229,18 +2239,21 @@ export const handleDownloadCertificate = async (
}
}
};
export async function findContact(value) {
export async function findContact(value, jwttoken) {
try {
const currentUser = Parse.User.current();
const contactbook = new Parse.Query("contracts_Contactbook");
contactbook.equalTo(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactbook.notEqualTo("IsDeleted", true);
contactbook.matches("Email", new RegExp(value, "i"));
const contactRes = await contactbook.find();
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsigners`;
const token = jwttoken
? { jwttoken: jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token
};
const searchEmail = value;
const axiosRes = await axios.post(url, { searchEmail }, { headers });
const contactRes = axiosRes?.data?.result || [];
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
return res;
@@ -2559,3 +2572,23 @@ export function generateTitleFromFilename(filename) {
return "Untitled Document";
}
}
export const signatureTypes = [
{ name: "draw", enabled: true },
{ name: "typed", enabled: true },
{ name: "upload", enabled: true },
{ name: "default", enabled: true }
];
// `handleSignatureType` is used to return update signature types as per tenant or user
export async function handleSignatureType(tenantSignTypes, signatureType) {
const docSignTypes = signatureType || signatureTypes;
let updatedSignatureType = signatureType || signatureTypes;
if (tenantSignTypes?.length > 0) {
updatedSignatureType = tenantSignTypes?.map((item) => {
const match = docSignTypes.find((data) => data.name === item.name);
return match ? { ...item, enabled: match.enabled } : item;
});
}
return updatedSignatureType;
}
+3 -3
View File
@@ -27,21 +27,21 @@ export default function reportJson(id) {
const templateSubAction = isEnableSubscription
? [
{
btnId: "2434",
btnId: "2435",
btnLabel: "Embed",
hoverLabel: "Embed",
btnIcon: "fa-light fa-code",
action: "Embed"
},
{
btnId: "2434",
btnId: "2436",
btnLabel: "Copy TemplateId",
hoverLabel: "Copy TemplateId",
btnIcon: "fa-light fa-copy",
action: "CopyTemplateId"
},
{
btnId: "2434",
btnId: "2437",
btnLabel: "Copy Public URL",
hoverLabel: "Copy Public URL",
btnIcon: "fa-light fa-copy",
+8
View File
@@ -154,6 +154,14 @@ const sidebarList = [
pageType: "",
description: "",
objectId: "webhook"
},
{
icon: "fa-light fa-sliders",
title: "Preferences",
target: "_self",
pageType: "",
description: "",
objectId: "preferences"
}
]
}
+18 -10
View File
@@ -16,17 +16,18 @@ const plans = [
"Unlimited digital signatures",
"Sign documents yourself",
"Request signatures from others",
"Unlimited templates",
"14 field types",
"Automatic e-signatures",
"Completion certificates",
"Send in order",
"Organize docs in OpenSign™ Drive",
"Document templates",
"Import from Dropbox",
"Contact book",
"Document expiry support",
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"Public profiles",
"And much more"
],
@@ -34,17 +35,18 @@ const plans = [
"Unlimited digital signatures",
"Sign documents yourself",
"Request signatures from others",
"Unlimited templates",
"14 field types",
"Automatic e-signatures",
"Completion certificates",
"Send in order",
"Organize docs in OpenSign™ Drive",
"Document templates",
"Import from Dropbox",
"Contact book",
"Document expiry support",
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"Public profiles",
"And much more"
]
@@ -61,10 +63,10 @@ const plans = [
subtitle: "Exclusive Access to advanced features.",
btn: { text: "Subscribe", color: "op-btn-primary" },
url: isStaging
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
yearlyUrl: isStaging
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
target: "_self",
benefits: [
@@ -77,10 +79,12 @@ const plans = [
"API Access",
"upto 100 API signatures",
"Custom email templates",
"Connect your own Gmail or SMTP account for sending emails",
"Auto reminders",
"Bulk send (upto 100 docs)",
"Premium Public profile usernames",
"Embedding (coming soon)"
"Enforce email-based verification to confirm signer identity",
"Embedded signing"
],
yearlyBenefits: [
"Everything in OpenSign™ free",
@@ -92,10 +96,12 @@ const plans = [
"API Access",
"upto 240 API signatures",
"Custom email templates",
"Connect your own Gmail or SMTP account for sending emails",
"Auto reminders",
"Bulk send (upto 240 docs)",
"Premium Public profile usernames",
"Embedding (coming soon)"
"Enforce email-based verification to confirm signer identity",
"Embedded signing"
]
},
{
@@ -108,10 +114,10 @@ const plans = [
subtitle: "Exclusive Access to advanced features.",
btn: { text: "Subscribe", color: "op-btn-accent" },
url: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
yearlyUrl: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
target: "_self",
benefits: [
@@ -120,6 +126,7 @@ const plans = [
"Teams and Organizations",
"Share Templates with teams",
"Share Templates with individuals",
"BYOC - Store your documents in your own cloud storage",
"DocumentId removal from signed docs",
"Bulk send (upto 100 docs)",
"Request Payments (coming soon)",
@@ -131,6 +138,7 @@ const plans = [
"Teams and Organizations",
"Share Templates with teams",
"Share Templates with individuals",
"BYOC - Store your documents in your own cloud storage",
"DocumentId removal from signed docs",
"Bulk send (upto 500 docs)",
"Request Payments (coming soon)",
@@ -231,10 +239,10 @@ export const paidUrl = (plan) => {
}
const url = {
monthly: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
yearly: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly"
};
+3 -1
View File
@@ -255,7 +255,9 @@ const HomeLayout = () => {
return (
<div>
<div className="sticky top-0 z-[501]">
{!isLoader && <Header showSidebar={showSidebar} />}
{!isLoader && (
<Header showSidebar={showSidebar} setIsMenu={setIsOpen} />
)}
</div>
{isUserValid ? (
<>
+69 -64
View File
@@ -12,6 +12,7 @@ import { showTenant } from "../redux/reducers/ShowTenant";
import Loader from "../primitives/Loader";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const";
const AddAdmin = () => {
const { t, i18n } = useTranslation();
@@ -84,77 +85,81 @@ const AddAdmin = () => {
const handleSubmit = async (event) => {
event.preventDefault();
if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage();
setState({ loading: true });
const userDetails = {
name: name,
email: email,
phone: phone,
company: company,
jobTitle: jobTitle
};
localStorage.setItem("userDetails", JSON.stringify(userDetails));
try {
event.preventDefault();
const user = new Parse.User();
user.set("name", name);
user.set("email", email);
user.set("password", password);
user.set("phone", phone);
user.set("username", email);
const userRes = await user.save();
if (userRes) {
const params = {
userDetails: {
jobTitle: jobTitle,
company: company,
name: name,
email: email,
phone: phone,
role: "contracts_Admin"
}
};
try {
const usersignup = await Parse.Cloud.run("addadmin", params);
if (usersignup) {
if (isSubscribeNews) {
subscribeNewsletter();
if (!emailRegex.test(email)) {
alert("Please enter a valid email address.");
} else {
if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage();
setState({ loading: true });
const userDetails = {
name: name,
email: email,
phone: phone,
company: company,
jobTitle: jobTitle
};
localStorage.setItem("userDetails", JSON.stringify(userDetails));
try {
event.preventDefault();
const user = new Parse.User();
user.set("name", name);
user.set("email", email);
user.set("password", password);
user.set("phone", phone);
user.set("username", email);
const userRes = await user.save();
if (userRes) {
const params = {
userDetails: {
jobTitle: jobTitle,
company: company,
name: name,
email: email,
phone: phone,
role: "contracts_Admin"
}
handleNavigation(userRes.getSessionToken());
}
} catch (err) {
alert(err.message);
setState({ loading: false });
}
}
} catch (error) {
console.log("err ", error);
if (error.code === 202) {
const params = { email: email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert(t("already-exists-this-username"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
};
try {
await Parse.User.requestPasswordReset(email).then(
async function (res) {
if (res.data === undefined) {
alert(t("verification-code-sent"));
}
const usersignup = await Parse.Cloud.run("addadmin", params);
if (usersignup) {
if (isSubscribeNews) {
subscribeNewsletter();
}
);
handleNavigation(userRes.getSessionToken());
}
} catch (err) {
console.log(err);
alert(err.message);
setState({ loading: false });
}
}
} catch (error) {
console.log("err ", error);
if (error.code === 202) {
const params = { email: email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert(t("already-exists-this-username"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
try {
await Parse.User.requestPasswordReset(email).then(
async function (res) {
if (res.data === undefined) {
alert(t("verification-code-sent"));
}
}
);
} catch (err) {
console.log(err);
}
setState({ loading: false });
}
} else {
alert(error.message);
setState({ loading: false });
}
} else {
alert(error.message);
setState({ loading: false });
}
}
}
File diff suppressed because it is too large Load Diff
+17 -13
View File
@@ -7,7 +7,7 @@ import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { isEnableSubscription } from "../constant/const";
import { emailRegex, isEnableSubscription } from "../constant/const";
import { getAppLogo } from "../constant/Utils";
import { useTranslation } from "react-i18next";
@@ -36,18 +36,22 @@ function ForgotPassword() {
const handleSubmit = async (event) => {
event.preventDefault();
localStorage.setItem("appLogo", appInfo.applogo);
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
if (state.email) {
const username = state.email;
try {
await Parse.User.requestPasswordReset(username);
setSentStatus("success");
} catch (err) {
console.log("err ", err.code);
setSentStatus("failed");
} finally {
setTimeout(() => setSentStatus(""), 1000);
if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address.");
} else {
localStorage.setItem("appLogo", appInfo.applogo);
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
if (state.email) {
const username = state.email;
try {
await Parse.User.requestPasswordReset(username);
setSentStatus("success");
} catch (err) {
console.log("err ", err.code);
setSentStatus("failed");
} finally {
setTimeout(() => setSentStatus(""), 1000);
}
}
}
};
+4 -4
View File
@@ -1394,20 +1394,20 @@ const Forms = (props) => {
)}
{isAdvanceOpt && props.title !== "Sign Yourself" ? (
<div
<span
onClick={() => setIsAdvanceOpt(!isAdvanceOpt)}
className={`mt-2.5 op-link op-link-primary text-sm`}
>
{t("hide-advanced-options")}
</div>
</span>
) : (
props.title !== "Sign Yourself" && (
<div
<span
onClick={() => setIsAdvanceOpt(!isAdvanceOpt)}
className={`mt-2.5 op-link op-link-primary text-sm`}
>
{t("advanced-options")}
</div>
</span>
)
)}
+147 -140
View File
@@ -9,7 +9,7 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize";
import ModalUi from "../primitives/ModalUi";
import { isEnableSubscription } from "../constant/const";
import { emailRegex, isEnableSubscription } from "../constant/const";
import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
@@ -89,167 +89,174 @@ function Login() {
const handleSubmit = async (event) => {
localStorage.removeItem("accesstoken");
event.preventDefault();
const { email, password } = state;
if (email && password) {
try {
setState({ ...state, loading: true });
localStorage.setItem("appLogo", appInfo.applogo);
// Pass the username and password to logIn function
const user = await Parse.User.logIn(email, password);
if (user) {
let _user = user.toJSON();
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("userEmail", email);
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
const userSettings = appInfo.settings;
await Parse.Cloud.run("getUserDetails")
.then(async (extUser) => {
if (extUser) {
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
const IsDisabled = extUser?.get("IsDisabled") || false;
if (!IsDisabled) {
const userRole = extUser?.get("UserRole");
const menu =
userRole &&
userSettings.find((menu) => menu.role === userRole);
if (menu) {
const _currentRole = userRole;
const redirectUrl =
location?.state?.from ||
`/${menu.pageType}/${menu.pageId}`;
let _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
const checkLanguage = extUser?.get("Language");
if (checkLanguage) {
checkLanguage && i18n.changeLanguage(checkLanguage);
}
if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address.");
} else {
const { email, password } = state;
if (email && password) {
try {
setState({ ...state, loading: true });
localStorage.setItem("appLogo", appInfo.applogo);
// Pass the username and password to logIn function
const user = await Parse.User.logIn(email, password);
if (user) {
let _user = user.toJSON();
localStorage.setItem("UserInformation", JSON.stringify(_user));
localStorage.setItem("userEmail", email);
localStorage.setItem("accesstoken", _user.sessionToken);
localStorage.setItem("scriptId", true);
if (_user.ProfilePic) {
localStorage.setItem("profileImg", _user.ProfilePic);
} else {
localStorage.setItem("profileImg", "");
}
// Check extended class user role and tenentId
try {
const userSettings = appInfo.settings;
await Parse.Cloud.run("getUserDetails")
.then(async (extUser) => {
if (extUser) {
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
const IsDisabled = extUser?.get("IsDisabled") || false;
if (!IsDisabled) {
const userRole = extUser?.get("UserRole");
const menu =
userRole &&
userSettings.find((menu) => menu.role === userRole);
if (menu) {
const _currentRole = userRole;
const redirectUrl =
location?.state?.from ||
`/${menu.pageType}/${menu.pageId}`;
let _role = _currentRole.replace("contracts_", "");
localStorage.setItem("_user_role", _role);
const checkLanguage = extUser?.get("Language");
if (checkLanguage) {
checkLanguage && i18n.changeLanguage(checkLanguage);
}
const results = [extUser];
const extUser_str = JSON.stringify(results);
const results = [extUser];
const extUser_str = JSON.stringify(results);
localStorage.setItem("Extand_Class", extUser_str);
const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", extInfo.Email);
localStorage.setItem("username", extInfo.Name);
if (extInfo?.TenantId) {
const tenant = {
Id: extInfo?.TenantId?.objectId || "",
Name: extInfo?.TenantId?.TenantName || ""
};
localStorage.setItem("TenantId", tenant?.Id);
dispatch(showTenant(tenant?.Name));
localStorage.setItem("TenantName", tenant?.Name);
}
localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType);
if (isEnableSubscription) {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const res = await fetchSubscription();
const plan = res.plan;
const billingDate = res.billingDate;
if (plan === "freeplan") {
setState({ ...state, loading: false });
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
localStorage.removeItem("userDetails");
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
localStorage.setItem("Extand_Class", extUser_str);
const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", extInfo.Email);
localStorage.setItem("username", extInfo.Name);
if (extInfo?.TenantId) {
const tenant = {
Id: extInfo?.TenantId?.objectId || "",
Name: extInfo?.TenantId?.TenantName || ""
};
localStorage.setItem("TenantId", tenant?.Id);
dispatch(showTenant(tenant?.Name));
localStorage.setItem("TenantName", tenant?.Name);
}
localStorage.setItem("PageLanding", menu.pageId);
localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType);
if (isEnableSubscription) {
const LocalUserDetails = {
name: results[0].get("Name"),
email: results[0].get("Email"),
phone: results[0]?.get("Phone") || "",
company: results[0].get("Company")
};
localStorage.setItem(
"userDetails",
JSON.stringify(LocalUserDetails)
);
const res = await fetchSubscription();
const plan = res.plan;
const billingDate = res.billingDate;
if (plan === "freeplan") {
setState({ ...state, loading: false });
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
localStorage.removeItem("userDetails");
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
setState({ ...state, loading: false });
navigate(redirectUrl);
} else {
setState({ ...state, loading: false });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
handlePaidRoute(plan);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
} else {
setState({ ...state, loading: false });
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
setIsModal(true);
}
} else {
setState({ ...state, loading: false });
setIsModal(true);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg:
"You don't have access, please contact the admin."
});
logOutUser();
}
} else {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg:
"You don't have access, please contact the admin."
});
logOutUser();
if (isEnableSubscription) {
setState({ ...state, loading: false });
setIsModal(true);
} else {
setState({ ...state, loading: false });
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "User not found."
});
logOutUser();
}
}
} else {
if (isEnableSubscription) {
setState({ ...state, loading: false });
setIsModal(true);
} else {
setState({ ...state, loading: false });
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "User not found."
});
logOutUser();
}
}
})
.catch((error) => {
// const payload = { sessionToken: user.getSessionToken() };
// handleSubmitbtn(payload);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `Something went wrong.`
})
.catch((error) => {
// const payload = { sessionToken: user.getSessionToken() };
// handleSubmitbtn(payload);
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `Something went wrong.`
});
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
console.error("Error while fetching Follow", error);
});
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
console.error("Error while fetching Follow", error);
} catch (error) {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `${error.message}`
});
} catch (error) {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: `${error.message}`
});
console.log(error);
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
console.log(error);
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
}
}
} catch (error) {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "Invalid username or password!"
});
console.error("Error while logging in user", error);
} finally {
setTimeout(
() => setState((prev) => ({ ...prev, alertMsg: "" })),
2000
);
}
} catch (error) {
setState({
...state,
loading: false,
alertType: "danger",
alertMsg: "Invalid username or password!"
});
console.error("Error while logging in user", error);
} finally {
setTimeout(() => setState((prev) => ({ ...prev, alertMsg: "" })), 2000);
}
}
};
+9 -5
View File
@@ -136,10 +136,10 @@ const ManageSign = () => {
const replaceSpace = sanitizeFileName(sanitizename);
let file;
if (signature) {
file = base64StringtoFile(signature, `${replaceSpace}_sign.png`);
file = base64StringtoFile(signature, `${replaceSpace}_sign`);
} else {
if (image && !isUrl) {
file = base64StringtoFile(image, `${replaceSpace}__sign.png`);
file = base64StringtoFile(image, `${replaceSpace}__sign`);
}
}
let imgUrl;
@@ -153,12 +153,12 @@ const ManageSign = () => {
let initialFile;
if (Initials) {
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign.png`);
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign`);
} else {
if (imgInitials && !isInitialsUrl) {
initialFile = base64StringtoFile(
imgInitials,
`${replaceSpace}__sign.png`
`${replaceSpace}__sign`
);
}
}
@@ -179,14 +179,18 @@ const ManageSign = () => {
};
function base64StringtoFile(base64String, filename) {
let arr = base64String.split(","),
// type of uploaded image
mime = arr[0].match(/:(.*?);/)[1],
// decode base64
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
const ext = mime.split("/").pop();
const name = `${filename}.${ext}`;
return new File([u8arr], name, { type: mime });
}
const uploadFile = async (file) => {
+183 -120
View File
@@ -1,5 +1,10 @@
import React, { useState, useRef, useEffect } from "react";
import { isEnableSubscription, isStaging, themeColor } from "../constant/const";
import {
emailRegex,
isEnableSubscription,
isStaging,
themeColor
} from "../constant/const";
import { PDFDocument } from "pdf-lib";
import "../styles/signature.css";
import Parse from "parse";
@@ -33,7 +38,10 @@ import {
getDefaultSignature,
onClickZoomIn,
onClickZoomOut,
fetchUrl
fetchUrl,
signatureTypes,
handleSignatureType,
getTenantDetails
} from "../constant/Utils";
import Header from "../components/pdf/PdfHeader";
import RenderPdf from "../components/pdf/RenderPdf";
@@ -132,12 +140,11 @@ function PdfRequestFiles(props) {
const [publicRes, setPublicRes] = useState({});
const [documentId, setDocumentId] = useState("");
const [isPublicContact, setIsPublicContact] = useState(false);
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const [plancode, setPlanCode] = useState("");
const isHeader = useSelector((state) => state.showHeader);
const divRef = useRef(null);
const [isDownloadModal, setIsDownloadModal] = useState(false);
const [signatureType, setSignatureType] = useState([]);
const isMobile = window.innerWidth < 767;
let isGuestSignFlow = false;
@@ -304,17 +311,13 @@ function PdfRequestFiles(props) {
: [];
if (documentData && documentData[0]?.error) {
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Invalid"
});
props?.setTemplateStatus({ status: "Invalid" });
throw new Error("error: Invalid TemplateId");
} else if (documentData && documentData.length > 0) {
if (documentData[0]?.IsPublic) {
//handle condition when someone use plan js then setTemplateStatus is not supporting
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Success"
});
props?.setTemplateStatus({ status: "Success" });
const url =
documentData[0] &&
(documentData[0]?.SignedUrl || documentData[0]?.URL);
@@ -323,8 +326,6 @@ function PdfRequestFiles(props) {
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
@@ -354,24 +355,18 @@ function PdfRequestFiles(props) {
}
setUnSignedSigners(placeholdersOrSigners);
setPdfDetails(documentData);
setIsLoading({
isLoad: false
});
setIsLoading({ isLoad: false });
} else {
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Private"
});
setIsLoading(false);
props?.setTemplateStatus({ status: "Private" });
setIsLoading({ isLoad: false });
setHandleError(t("something-went-wrong-mssg"));
console.error("error: TemplateId is not public");
return;
}
} else {
props?.setTemplateStatus &&
props?.setTemplateStatus({
status: "Invalid"
});
props?.setTemplateStatus({ status: "Invalid" });
setIsLoading(false);
setHandleError(t("something-went-wrong-mssg"));
console.error("error: Invalid TemplateId");
@@ -388,6 +383,33 @@ function PdfRequestFiles(props) {
return;
}
};
const fetchTenantDetails = async (contactId) => {
const user = JSON.parse(
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
)
);
try {
const tenantDetails = await getTenantDetails(
user?.objectId, // userId
"", // jwttoken
contactId // contactId
);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist"));
} else if (tenantDetails) {
const signatureType = tenantDetails?.SignatureType || [];
const filterSignTypes = signatureType?.filter(
(x) => x.enabled === true
);
return filterSignTypes;
}
} catch (e) {
alert(t("user-not-exist"));
}
};
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (docId, isNextUser) => {
try {
@@ -395,11 +417,27 @@ function PdfRequestFiles(props) {
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
);
const jsonSender = JSON.parse(senderUser);
const contactId = jsonSender?.objectId
? ""
: contactBookId || signerObjectId || "";
const tenantSignTypes = await fetchTenantDetails(contactId);
// `currUserId` will be contactId or extUserId
let currUserId;
//getting document details
const documentData = await contractDocument(docId);
if (documentData && documentData.length > 0) {
const userSignatureType =
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
const docSignTypes =
documentData?.[0]?.SignatureType || userSignatureType;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
setSignatureType(updatedSignatureType);
const updatedPdfDetails = [...documentData];
updatedPdfDetails[0].SignatureType = updatedSignatureType;
setPdfDetails(updatedPdfDetails);
const url =
documentData[0] &&
(documentData[0]?.SignedUrl || documentData[0]?.URL);
@@ -408,8 +446,6 @@ function PdfRequestFiles(props) {
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
@@ -431,7 +467,7 @@ function PdfRequestFiles(props) {
currUserId = getCurrentSigner?.objectId
? getCurrentSigner.objectId
: contactBookId || signerObjectId || "";
: contactBookId || signerObjectId || ""; //signerObjectId is contactBookId refer from public template flow
if (isEnableSubscription) {
await checkIsSubscribed(
documentData[0]?.ExtUserPtr?.objectId,
@@ -495,9 +531,9 @@ function PdfRequestFiles(props) {
const obj = documentData?.[0];
setSendInOrder(obj?.SendinOrder || false);
if (
obj &&
obj?.Signers?.length > 0 &&
obj?.Placeholders?.length > 0
obj?.Signers?.length &&
obj?.Placeholders?.length &&
currUserId !== "undefined"
) {
const params = {
event: "viewed",
@@ -891,9 +927,29 @@ function PdfRequestFiles(props) {
setIsUiLoading(true);
// `widgets` is Used to return widgets details with page number of current user
const widgets = checkUser?.[0]?.placeHolder;
let pdfArrBuffer;
//`contractDocument` function used to get updated SignedUrl
// to resolve issue of widgets get remove automatically when more than 1 signers try to sign doc at a time
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
const url = documentData[0]?.SignedUrl || documentData[0]?.URL;
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError("Error: invalid document!");
} else {
pdfArrBuffer = arrayBuffer;
}
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
setHandleError("Error: Something went wrong!");
} else {
setHandleError("Document not Found!");
}
// Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = pdfArrayBuffer;
const existingPdfBytes = pdfArrBuffer;
try {
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const isSignYourSelfFlow = false;
@@ -910,9 +966,7 @@ function PdfRequestFiles(props) {
widgets,
pdfDoc,
isSignYourSelfFlow,
scale,
pdfOriginalWH,
containerWH
scale
);
// console.log("pdfte", pdfBytes);
//get ExistUserPtr object id of user class to get tenantDetails
@@ -954,11 +1008,7 @@ function PdfRequestFiles(props) {
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString(
"en-US",
{
day: "numeric",
month: "long",
year: "numeric"
}
{ day: "numeric", month: "long", year: "numeric" }
);
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
@@ -1072,12 +1122,16 @@ function PdfRequestFiles(props) {
}
} else {
setIsUiLoading(false);
setIsAlert({ isShow: true, alertMessage: resSign.message });
setIsAlert({
title: "Error",
isShow: true,
alertMessage: resSign.message
});
}
} else {
setIsUiLoading(false);
setIsAlert({
header: t("error"),
title: "Error",
isShow: true,
alertMessage: t("pdf-uncompatible")
});
@@ -1363,9 +1417,7 @@ function PdfRequestFiles(props) {
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/updatecontacttour`,
{
contactId: signerObjectId
},
{ contactId: signerObjectId },
{
headers: {
"Content-Type": "application/json",
@@ -1506,65 +1558,69 @@ function PdfRequestFiles(props) {
//`handlePublicUser` function to use create user from public role and create document from public template
const handlePublicUser = async (e) => {
e.preventDefault();
setLoading(true);
try {
const params = {
...contact,
templateid: pdfDetails[0]?.objectId,
role: pdfDetails[0]?.PublicRole[0]
};
const userRes = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}/functions/publicuserlinkcontacttodoc`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
if (!emailRegex.test(contact.email)) {
alert("Please enter a valid email address.");
} else {
setLoading(true);
try {
const params = {
...contact,
templateid: pdfDetails[0]?.objectId,
role: pdfDetails[0]?.PublicRole[0]
};
const userRes = await axios.post(
`${localStorage.getItem(
"baseUrl"
)}/functions/publicuserlinkcontacttodoc`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
}
}
}
);
);
if (userRes?.data?.result) {
setPublicRes(userRes.data.result);
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
if (isEnableOTP) {
await SendOtp();
if (userRes?.data?.result) {
setPublicRes(userRes.data.result);
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
if (isEnableOTP) {
await SendOtp();
} else {
setIsPublicContact(false);
setIsPublicTemplate(false);
setDocumentId(userRes.data?.result?.docId);
const contactId = userRes.data.result?.contactId;
setSignerObjectId(contactId);
}
} else {
setIsPublicContact(false);
setIsPublicTemplate(false);
setDocumentId(userRes.data?.result?.docId);
const contactId = userRes.data.result?.contactId;
setSignerObjectId(contactId);
console.log("error in public-sign to create user details");
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
} catch (e) {
console.log("e", e);
if (
e?.response?.data?.error === "Insufficient Credit" ||
e?.response?.data?.error === "Plan expired"
) {
handleCloseOtp();
setIsAlert({
title: t("insufficient-credits"),
isShow: true,
alertMessage: t("insufficient-credits-mssg")
});
} else {
handleCloseOtp();
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
} else {
console.log("error in public-sign to create user details");
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
} catch (e) {
console.log("e", e);
if (
e?.response?.data?.error === "Insufficient Credit" ||
e?.response?.data?.error === "Plan expired"
) {
handleCloseOtp();
setIsAlert({
title: t("insufficient-credits"),
isShow: true,
alertMessage: t("insufficient-credits-mssg")
});
} else {
handleCloseOtp();
setIsAlert({
title: "Error",
isShow: true,
alertMessage: t("something-went-wrong-mssg")
});
}
}
};
@@ -1946,7 +2002,7 @@ function PdfRequestFiles(props) {
})
}
type="button"
className="op-btn op-btn-secondary"
className="op-btn op-btn-secondary ml-1"
>
{t("close")}
</button>
@@ -2089,27 +2145,30 @@ function PdfRequestFiles(props) {
</div>
</ModalUi>
{/* this component is used for signature pad modal */}
<SignPad
isSignPad={isSignPad}
isStamp={isStamp}
setIsImageSelect={setIsImageSelect}
setIsSignPad={setIsSignPad}
setImage={setImage}
isImageSelect={isImageSelect}
imageRef={imageRef}
onImageChange={onImageChange}
setSignature={setSignature}
image={image}
onSaveImage={saveImage}
onSaveSign={saveSign}
defaultSign={defaultSignImg}
myInitial={myInitial}
isInitial={isInitial}
setIsInitial={setIsInitial}
setIsStamp={setIsStamp}
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
{documentId && (
<SignPad
signatureTypes={signatureType}
isSignPad={isSignPad}
isStamp={isStamp}
setIsImageSelect={setIsImageSelect}
setIsSignPad={setIsSignPad}
setImage={setImage}
isImageSelect={isImageSelect}
imageRef={imageRef}
onImageChange={onImageChange}
setSignature={setSignature}
image={image}
onSaveImage={saveImage}
onSaveSign={saveSign}
defaultSign={defaultSignImg}
myInitial={myInitial}
isInitial={isInitial}
setIsInitial={setIsInitial}
setIsStamp={setIsStamp}
currWidgetsDetails={currWidgetsDetails}
setCurrWidgetsDetails={setCurrWidgetsDetails}
/>
)}
{/* pdf header which contain funish back button */}
<Header
isPdfRequestFiles={isPublicTemplate ? false : true}
@@ -2238,6 +2297,10 @@ function PdfRequestFiles(props) {
setIsLoading={setIsLoading}
xyPostion={signerPos}
setDefaultSignAlert={setDefaultSignAlert}
isDefault={
signatureType?.find((x) => x.name === "default")
?.enabled || false
}
/>
)}
</div>
+154 -96
View File
@@ -38,7 +38,9 @@ import {
onClickZoomOut,
rotatePdfPage,
handleRemoveWidgets,
handleRotateWarning
handleRotateWarning,
signatureTypes,
handleSignatureType
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom";
@@ -151,28 +153,23 @@ function PlaceHolderSign() {
const [scale, setScale] = useState(1);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const [planCode, setPlanCode] = useState("");
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
const [signatureType, setSignatureType] = useState(signatureTypes);
const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({
accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor),
collect: (monitor) => ({
isOver: !!monitor.isOver()
})
collect: (monitor) => ({ isOver: !!monitor.isOver() })
});
const [{ isDragSign }, dragSignature] = useDrag({
type: "BOX",
item: { type: "BOX", id: 1, text: "signature" },
collect: (monitor) => ({
isDragSign: !!monitor.isDragging()
})
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
});
const [{ isDragStamp }, dragStamp] = useDrag({
type: "BOX",
item: { type: "BOX", id: 2, text: "stamp" },
collect: (monitor) => ({
isDragStamp: !!monitor.isDragging()
})
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
});
const documentId = docId;
useEffect(() => {
@@ -201,10 +198,15 @@ function PlaceHolderSign() {
if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist"));
} else if (tenantDetails) {
const signatureType = tenantDetails?.SignatureType || [];
const filterSignTypes = signatureType?.filter(
(x) => x.enabled === true
);
if (tenantDetails?.RequestBody) {
setRequestBody(tenantDetails?.RequestBody);
setRequestSubject(tenantDetails?.RequestSubject);
}
return filterSignTypes;
}
} catch (e) {
alert(t("user-not-exist"));
@@ -255,7 +257,7 @@ function PlaceHolderSign() {
}
//function for get document details
const getDocumentDetails = async () => {
fetchTenantDetails();
const tenantSignTypes = await fetchTenantDetails();
//getting document details
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
@@ -312,7 +314,18 @@ function PlaceHolderSign() {
});
}
}
setPdfDetails(documentData);
const userSignatureType =
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
const docSignTypes =
documentData?.[0]?.SignatureType || userSignatureType;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
setSignatureType(updatedSignatureType);
const updatedPdfDetails = [...documentData];
updatedPdfDetails[0].SignatureType = updatedSignatureType;
setPdfDetails(updatedPdfDetails);
//condition when placeholder have empty array with role details and signers array have signers data
//and both array length are same
//this case happen using placeholder form in auto save funtionality to save draft type document without adding any placehlder
@@ -475,8 +488,10 @@ function PlaceHolderSign() {
let dropData = [];
let placeHolder;
const dragTypeValue = item?.text ? item.text : monitor.type;
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
const widgetWidth =
defaultWidthHeight(dragTypeValue).width * containerScale;
const widgetHeight =
defaultWidthHeight(dragTypeValue).height * containerScale;
//adding and updating drop position in array when user drop signature button in div
if (item === "onclick") {
const divHeight = divRef.current.getBoundingClientRect().height;
@@ -565,22 +580,33 @@ function PlaceHolderSign() {
}
setSignerPos(updatesignerPos);
} else {
let updatesignerPos;
//if condition when widget type is prefill label text widget
if (dragTypeValue === textWidget) {
const prefileTextWidget = {
signerPtr: {},
signerObjId: "",
blockColor: "#f58f8c",
placeHolder: [placeHolder],
Role: "prefill",
Id: key
};
signerPos.push(prefileTextWidget);
setSignerPos(signerPos);
//check text widgets data (prefill) already exist then and want to add text widget on new page
//create new page entry with old data and update placeholder
if (filterSignerPos) {
const addPrefillData =
filterSignerPos && filterSignerPos?.placeHolder;
addPrefillData.push(placeHolder);
const updatePrefillPos = signerPos.map((x) =>
x.Role === "prefill" ? { ...x, placeHolder: addPrefillData } : x
);
setSignerPos(updatePrefillPos);
} //else condition if user do not have any text widget data
else {
const prefillTextWidget = {
signerPtr: {},
signerObjId: "",
blockColor: "#f58f8c",
placeHolder: [placeHolder],
Role: "prefill",
Id: key
};
setSignerPos((prev)=>[...prev,prefillTextWidget]);
}
} else {
//else condition to add placeholder widgets on multiple page first time
updatesignerPos = signerPos.map((x) =>
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId && x?.placeHolder
? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
: x.Id === uniqueId
@@ -755,15 +781,17 @@ function PlaceHolderSign() {
signerupdate.push(newUpdatePos[0]);
setSignerPos(signerupdate);
} else {
const updatedData = signerPos.map((item) => {
if (item.Id === Id) {
// Create a copy of the item object and delete the placeHolder field
const updatedItem = { ...item };
delete updatedItem.placeHolder;
return updatedItem;
}
return item;
});
const updatedData = signerPos
.filter((item) => !(item.Id === Id && item.Role === "prefill")) // Remove prefill object
.map((item) => {
if (item.Id === Id && item.Role !== "prefill") {
// Create a copy of the item object and delete the placeHolder field
const updatedItem = { ...item };
delete updatedItem.placeHolder;
return updatedItem;
}
return item;
});
setSignerPos(updatedData);
}
}
@@ -813,9 +841,7 @@ function PlaceHolderSign() {
placeholder,
pdfDoc,
isSignYourSelfFlow,
scale,
pdfOriginalWH,
containerWH
scale
);
const pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfBase64);
const tenantId = localStorage.getItem("TenantId");
@@ -844,17 +870,24 @@ function PlaceHolderSign() {
};
const alertSendEmail = async () => {
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
const getPrefill = signerPos?.find((data) => data.Role === "prefill");
let isLabel = false;
let unfilledTextWidgetId = "";
//checking all signers placeholder exist or not
const isPlaceholderExist = filterPrefill.every((data) => data.placeHolder);
const prefillPlaceholder = getPrefill[0]?.placeHolder;
const prefillPlaceholder = getPrefill?.placeHolder;
//condition is used to check text widget data is empty or have response
if (getPrefill && getPrefill.length > 0) {
if (getPrefill) {
if (prefillPlaceholder) {
prefillPlaceholder.map((data) => {
if (!isLabel) {
isLabel = data.pos.some((position) => !position.options.response);
const unfilledTextWidgets = data.pos.find(
(position) => !position.options.response
);
if (unfilledTextWidgets) {
isLabel = true;
unfilledTextWidgetId = unfilledTextWidgets.key;
}
}
});
}
@@ -882,6 +915,7 @@ function PlaceHolderSign() {
}
if (getPrefill && isLabel) {
setIsSendAlert({ mssg: textWidget, alert: true });
setUnSignedWidgetId(unfilledTextWidgetId);
} else if (isSignatureExist) {
if (isPlaceholderExist) {
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
@@ -907,24 +941,26 @@ function PlaceHolderSign() {
}, 2000);
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [signerPos, signersdata]);
}, [signerPos, signersdata, signatureType]);
// `autosavedetails` is used to save doc details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
const autosavedetails = async () => {
const signers = signersdata?.filter(
(x) =>
x?.Role !== "prefill" &&
x.objectId && {
const signers = signersdata?.reduce((acc, x) => {
if (x.objectId) {
acc.push({
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x.objectId
}
);
});
}
return acc;
}, []);
try {
const docCls = new Parse.Object("contracts_Document");
docCls.id = documentId;
docCls.set("Placeholders", signerPos);
docCls.set("Signers", signers);
docCls.set("SignatureType", signatureType);
await docCls.save();
} catch (e) {
console.log("error", e);
@@ -964,21 +1000,15 @@ function PlaceHolderSign() {
//filter label widgets after add label widgets data on pdf
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
try {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
SentToOthers: true,
SignatureType: pdfDetails?.[0]?.SignatureType
};
if (updateExpiryDate) {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
SentToOthers: true,
ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
};
} else {
data = {
Placeholders: filterPrefill,
SignedUrl: pdfUrl,
Signers: signers,
SentToOthers: true
};
data["ExpiryDate"] = { iso: updateExpiryDate, __type: "Date" };
}
await axios
.put(
@@ -1330,7 +1360,7 @@ function PlaceHolderSign() {
isHideLabel: isHideLabel || false,
defaultValue: defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1368,7 +1398,7 @@ function PlaceHolderSign() {
isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1386,7 +1416,7 @@ function PlaceHolderSign() {
values: dropdownOptions,
defaultValue: defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1420,7 +1450,15 @@ function PlaceHolderSign() {
setFontSize();
setFontColor();
};
const handleWidgetdefaultdata = (defaultdata) => {
const handleWidgetdefaultdata = (defaultdata, isSignWidget) => {
if (isSignWidget) {
const updatedPdfDetails = [...pdfDetails];
const signtypes = defaultdata.signatureType || signatureType;
updatedPdfDetails[0].SignatureType = signtypes;
// Update the SignatureType with the modified array
setPdfDetails(updatedPdfDetails);
setSignatureType(signtypes);
}
const options = ["email", "number", "text"];
let inputype;
if (defaultdata.textvalidate) {
@@ -1459,7 +1497,7 @@ function PlaceHolderSign() {
}
: {},
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1475,7 +1513,7 @@ function PlaceHolderSign() {
status: defaultdata.status,
defaultValue: defaultdata.defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1541,14 +1579,12 @@ function PlaceHolderSign() {
} else {
updatedTourStatus = [{ placeholder: true }];
}
await axios
.put(
try {
await axios.put(
`${localStorage.getItem(
"baseUrl"
)}classes/contracts_Users/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{ TourStatus: updatedTourStatus },
{
headers: {
"Content-Type": "application/json",
@@ -1556,18 +1592,18 @@ function PlaceHolderSign() {
sessionToken: localStorage.getItem("accesstoken")
}
}
)
.then(() => {
// const json = Listdata.data;
// const res = json.results;
})
.catch((err) => {
console.log("axois err ", err);
});
);
} catch (err) {
console.log("axois err ", err);
}
}
};
const handleRecipientSign = () => {
navigate(`/recipientSignPdf/${documentId}/${currentId}`);
if (currentId) {
navigate(`/recipientSignPdf/${documentId}/${currentId}`);
} else {
navigate(`/recipientSignPdf/${documentId}`);
}
};
const handleLinkUser = (id) => {
setIsAddUser({ [id]: true });
@@ -1648,8 +1684,15 @@ function PlaceHolderSign() {
const signerAssignTour = [
{
selector: '[data-tut="assignSigner"]',
content:
"You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour. ",
content: t("attach-signer-tour"),
position: "top",
style: { fontSize: "13px" }
}
];
const textFieldTour = [
{
selector: '[data-tut="IsSigned"]',
content: t("text-field-tour"),
position: "top",
style: { fontSize: "13px" }
}
@@ -1759,6 +1802,13 @@ function PlaceHolderSign() {
rounded={5}
closeWithMask={false}
/>
<Tour
onRequestClose={() => setIsSendAlert({})}
steps={textFieldTour}
isOpen={isSendAlert.mssg === textWidget}
rounded={5}
closeWithMask={false}
/>
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdfDetails[0].URL}
@@ -1779,20 +1829,18 @@ function PlaceHolderSign() {
<div className=" w-full md:w-[95%] ">
{/* this modal is used show alert set placeholder for all signers before send mail */}
<ModalUi
isOpen={isSendAlert.alert}
isOpen={
isSendAlert.alert && isSendAlert.mssg !== textWidget
}
title={
isSendAlert.mssg === "sure" ||
isSendAlert.mssg === textWidget
? t("fields-required")
: isSendAlert.mssg === "confirm" && t("send-mail")
(isSendAlert.mssg === "confirm" && t("send-mail"))
}
handleClose={() => handleCloseSendmailModal()}
>
<div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content">
{isSendAlert.mssg === "sure" ? (
<span>{t("placeholder-alert-1")}</span>
) : isSendAlert.mssg === textWidget ? (
<p>{t("placeholder-alert-2")}</p>
) : (
isSendAlert.mssg === "confirm" && (
<>
@@ -1873,9 +1921,11 @@ function PlaceHolderSign() {
<ModalUi
isOpen={isSend}
title={
mailStatus === "quotareached"
? t("quota-mail-head")
: t("Mails Sent")
mailStatus === "success"
? t("mails-sent")
: mailStatus === "quotareached"
? t("quota-mail-head")
: t("mail-not-delivered")
}
handleClose={() => {
setIsSend(false);
@@ -1902,7 +1952,12 @@ function PlaceHolderSign() {
<div className="my-3">{handleShareList()}</div>
</div>
) : (
<p>{t("placeholder-alert-6")}</p>
<div className="mb-[10px]">
<p>{t("placeholder-alert-6")}</p>
{isCurrUser && (
<p className="mt-1">{t("placeholder-alert-5")}</p>
)}
</div>
)}
{!mailStatus && (
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
@@ -2018,6 +2073,7 @@ function PlaceHolderSign() {
{/* pdf header which contain funish back button */}
<Header
completeBtnTitle={t("next")}
isPlaceholder={true}
pageNumber={pageNumber}
allPages={allPages}
@@ -2085,6 +2141,7 @@ function PlaceHolderSign() {
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
unSignedWidgetId={unSignedWidgetId}
/>
)}
</div>
@@ -2209,6 +2266,7 @@ function PlaceHolderSign() {
signersData={signersdata}
/>
<WidgetNameModal
signatureType={signatureType}
widgetName={widgetName}
defaultdata={currWidgetsDetails}
isOpen={isNameModal}
+218
View File
@@ -0,0 +1,218 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import Alert from "../primitives/Alert";
import { useTranslation } from "react-i18next";
import Loader from "../primitives/Loader";
import Tooltip from "../primitives/Tooltip";
import {
getTenantDetails,
handleSignatureType,
signatureTypes
} from "../constant/Utils";
import Parse from "parse";
import { Tooltip as ReactTooltip } from "react-tooltip";
const Preferences = () => {
const { t } = useTranslation();
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTopLoader, setIsTopLoader] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [signatureType, setSignatureType] = useState([]);
const [errMsg, setErrMsg] = useState("");
useEffect(() => {
fetchSignType();
}, []);
const fetchSignType = async () => {
setIsTopLoader(true);
try {
const user = JSON.parse(
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
)
);
const tenantDetails = await getTenantDetails(user?.objectId);
const signatureType = tenantDetails?.SignatureType || [];
const tenantSignTypes = signatureType?.filter((x) => x.enabled === true);
const getUser = await Parse.Cloud.run("getUserDetails");
if (getUser) {
const _getUser = JSON.parse(JSON.stringify(getUser));
if (tenantSignTypes?.length > 0) {
const signatureType = _getUser?.SignatureType || signatureTypes;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
signatureType
);
setSignatureType(updatedSignatureType);
} else {
const SignatureType = _getUser?.SignatureType || signatureTypes;
setSignatureType(SignatureType);
}
}
} catch (err) {
console.log("err while getting user details", err);
setErrMsg("Something went wrong");
} finally {
setIsTopLoader(false);
}
};
// `handleCheckboxChange` is trigger when user enable/disable checkbox of respective type
const handleCheckboxChange = (index) => {
// Create a copy of the signatureType array
const updatedSignatureType = [...signatureType];
// Toggle the enabled value for the clicked item
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
// Update the state with the modified array
setSignatureType(updatedSignatureType);
};
// `handleSave` is used save updated value signature type
const handleSave = async () => {
setIsLoader(true);
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
const isDefaultSignTypeOnly =
enabledSignTypes?.length === 1 && enabledSignTypes[0]?.name === "default";
if (enabledSignTypes.length === 0) {
setIsAlert({
type: "danger",
msg: t("at-least-one-signature-type")
});
} else if (isDefaultSignTypeOnly) {
setIsAlert({
type: "danger",
msg: t("expect-default-one-more-signature-type")
});
} else {
try {
const updateRes = await Parse.Cloud.run("updatesignaturetype", {
SignatureType: signatureType
});
if (updateRes) {
setIsAlert({ type: "success", msg: "Saved successfully." });
}
} catch (err) {
console.log("Error updating signature type", err);
setIsAlert({ type: "danger", msg: err.message });
}
}
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
setIsLoader(false);
};
return (
<React.Fragment>
<Title title={"API Token"} />
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
{isTopLoader ? (
<div className="flex justify-center items-center h-screen">
<Loader />
</div>
) : (
<>
{errMsg ? (
<div className="flex justify-center items-center h-screen">
{errMsg}
</div>
) : (
<div className="relative bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
{isLoader && (
<div className="flex justify-center items-center absolute w-full h-full rounded-box bg-black/30">
<Loader />
</div>
)}
<h1 className="ml-4 mt-3 mb-2 font-semibold">
OpenSign {t("Preferences")}{" "}
<span>
<Tooltip
message={`OpenSign™ ${t("Preferences")}`}
isSubscribe={true}
/>
</span>
</h1>
<div className="ml-4 mt-1 mb-2 flex flex-col">
<div className="mb-[0.75rem]">
<label
htmlFor="signaturetype"
className="text-[14px] mb-[0.7rem] font-medium"
>
{t("allowed-signature-types")}
<a data-tooltip-id="signtypes-tooltip" className="ml-1">
<sup>
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
</sup>
</a>
<ReactTooltip id="signtypes-tooltip" className="z-[999]">
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
<p className="font-bold">
{t("allowed-signature-types")}
</p>
<p>{t("allowed-signature-types-help.p1")}</p>
<p className="p-[5px] ml-2">
<ol className="list-disc">
<li>
<span className="font-bold">Draw: </span>
<span>
{t("allowed-signature-types-help.l1")}
</span>
</li>
<li>
<span className="font-bold">Typed: </span>
<span>
{t("allowed-signature-types-help.l2")}
</span>
</li>
<li>
<span className="font-bold">Upload: </span>
<span>
{t("allowed-signature-types-help.l3")}
</span>
</li>
<li>
<span className="font-bold">Default: </span>
<span>
{t("allowed-signature-types-help.l4")}
</span>
</li>
</ol>
</p>
</div>
</ReactTooltip>
</label>
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
{signatureType.map((type, i) => (
<div
key={i}
className="flex flex-row gap-[5px] items-center"
>
<input
className="mr-[2px] op-checkbox op-checkbox-xs"
type="checkbox"
name="signaturetype"
onChange={() => handleCheckboxChange(i)}
checked={type.enabled}
/>
<div
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
title={`Enabling this allow signers to ${type.name} signature`}
>
{type.name}
</div>
</div>
))}
</div>
<button
className="op-btn op-btn-primary"
onClick={handleSave}
>
Save
</button>
</div>
</div>
</div>
)}
</>
)}
</React.Fragment>
);
};
export default Preferences;
+84 -80
View File
@@ -10,7 +10,7 @@ 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 { emailRegex, isEnableSubscription } from "../constant/const";
import {
getAppLogo,
openInNewTab,
@@ -83,92 +83,96 @@ const Signup = () => {
};
const handleSubmit = (event) => {
event.preventDefault();
if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage();
setState({ loading: true });
const userDetails = {
name: name,
email: email,
phone: phone,
company: company,
jobTitle: jobTitle
};
localStorage.setItem("userDetails", JSON.stringify(userDetails));
try {
event.preventDefault();
var user = new Parse.User();
user.set("name", name);
user.set("email", email);
user.set("password", password);
user.set("phone", phone);
user.set("username", email);
let res = user.save();
res
.then(async (r) => {
if (r) {
let roleData = appInfo.settings;
if (roleData && roleData.length > 0) {
const params = {
userDetails: {
jobTitle: jobTitle,
company: company,
name: name,
email: email,
phone: phone,
role: appInfo.defaultRole
}
};
try {
const usersignup = await Parse.Cloud.run(
"usersignup",
params
);
if (usersignup) {
const param = new URLSearchParams(location.search);
const isFreeplan =
param?.get("subscription") === "freeplan";
if (isFreeplan) {
await handleFreePlan(r.id);
if (!emailRegex.test(email)) {
alert("Please enter a valid email address.");
} else {
if (lengthValid && caseDigitValid && specialCharValid) {
clearStorage();
setState({ loading: true });
const userDetails = {
name: name,
email: email,
phone: phone,
company: company,
jobTitle: jobTitle
};
localStorage.setItem("userDetails", JSON.stringify(userDetails));
try {
event.preventDefault();
var user = new Parse.User();
user.set("name", name);
user.set("email", email);
user.set("password", password);
user.set("phone", phone);
user.set("username", email);
let res = user.save();
res
.then(async (r) => {
if (r) {
let roleData = appInfo.settings;
if (roleData && roleData.length > 0) {
const params = {
userDetails: {
jobTitle: jobTitle,
company: company,
name: name,
email: email,
phone: phone,
role: appInfo.defaultRole
}
handleNavigation(r.getSessionToken(), isFreeplan);
};
try {
const usersignup = await Parse.Cloud.run(
"usersignup",
params
);
if (usersignup) {
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);
setState({ loading: false });
}
}
}
})
.catch(async (err) => {
if (err.code === 202) {
const params = { email: email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert(t("user-already-exist-name"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
try {
await Parse.User.requestPasswordReset(email).then(
async function (res1) {
if (res1.data === undefined) {
alert(t("verification-code-sent"));
}
}
);
} catch (err) {
console.log(err);
}
} catch (err) {
alert(err.message);
setState({ loading: false });
}
}
}
})
.catch(async (err) => {
if (err.code === 202) {
const params = { email: email };
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert(t("user-already-exist-name"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
try {
await Parse.User.requestPasswordReset(email).then(
async function (res1) {
if (res1.data === undefined) {
alert(t("verification-code-sent"));
}
}
);
} catch (err) {
console.log(err);
}
alert(err.message);
setState({ loading: false });
}
} else {
alert(err.message);
setState({ loading: false });
}
});
} catch (error) {
console.log("err ", error);
});
} catch (error) {
console.log("err ", error);
}
}
}
};
+13 -23
View File
@@ -36,7 +36,8 @@ import {
getDefaultSignature,
onClickZoomIn,
onClickZoomOut,
rotatePdfPage
rotatePdfPage,
signatureTypes
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -424,9 +425,7 @@ function SignYourSelf() {
const getWidth = widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
: defaultWidthHeight(dragTypeValue).width;
const getHeight = widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
: defaultWidthHeight(dragTypeValue).height;
const getHeight = defaultWidthHeight(dragTypeValue).height;
dropObj = {
xPosition: getWidth / 2 + containerWH.width / 2,
@@ -457,9 +456,7 @@ function SignYourSelf() {
const getWidth = widgetTypeExist
? calculateInitialWidthHeight(widgetValue).getWidth
: defaultWidthHeight(dragTypeValue).width;
const getHeight = widgetTypeExist
? calculateInitialWidthHeight(widgetValue).getHeight
: defaultWidthHeight(dragTypeValue).height;
const getHeight = defaultWidthHeight(dragTypeValue).height;
dropObj = {
xPosition: getXPosition / (containerScale * scale),
yPosition: getYPosition / (containerScale * scale),
@@ -661,9 +658,7 @@ function SignYourSelf() {
xyPostion,
pdfDoc,
isSignYourSelfFlow,
scale,
pdfOriginalWH,
containerWH
scale
);
// console.log("pdf", pdfBytes);
//function for call to embed signature in pdf and get digital signature pdf
@@ -687,10 +682,7 @@ function SignYourSelf() {
});
} else {
console.log("err in signing", err.message);
if (
err.message ===
"PKCS#12 MAC could not be verified. Invalid password?"
) {
if (err?.message?.includes("password")) {
setIsAlert({
header: t("error"),
isShow: true,
@@ -757,10 +749,10 @@ function SignYourSelf() {
alert(t("something-went-wrong-mssg"));
}
}
//change image width and height to 100/40 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
//change image width and height to 300/120 in png base64
const imagebase64 = await changeImageWH(base64Sign);
//remove suffiix of base64 (without type)
const suffixbase64 = imagebase64 && imagebase64.split(",").pop();
const params = {
pdfFile: base64Url,
docId: documentId,
@@ -1031,7 +1023,6 @@ function SignYourSelf() {
const getPageNumer = xyPostion.filter(
(data) => data.pageNumber === pageNumber
);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
@@ -1061,7 +1052,7 @@ function SignYourSelf() {
isReadOnly: isReadOnly,
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor || currWidgetsDetails?.options?.fontColor || "black"
}
@@ -1095,7 +1086,6 @@ function SignYourSelf() {
const getPageNumer = xyPostion.filter(
(data) => data.pageNumber === pageNumber
);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
@@ -1105,8 +1095,7 @@ function SignYourSelf() {
...position,
options: {
...position.options,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize: fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor || currWidgetsDetails?.options?.fontColor || "black"
}
@@ -1305,6 +1294,7 @@ function SignYourSelf() {
/>
{/* this is modal of signature pad */}
<SignPad
signatureTypes={signatureTypes}
isSignPad={isSignPad}
isStamp={isStamp}
setIsImageSelect={setIsImageSelect}
+108 -120
View File
@@ -29,7 +29,11 @@ import {
onClickZoomOut,
onClickZoomIn,
handleRemoveWidgets,
handleRotateWarning
handleRotateWarning,
color,
signatureTypes,
getTenantDetails,
handleSignatureType
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -68,10 +72,7 @@ const TemplatePlaceholder = () => {
const [isSendAlert, setIsSendAlert] = useState(false);
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isRotate, setIsRotate] = useState({
status: false,
degree: 0
});
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: t("loading-mssg")
@@ -102,50 +103,21 @@ const TemplatePlaceholder = () => {
const [isTextSetting, setIsTextSetting] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({
accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor),
collect: (monitor) => ({
isOver: !!monitor.isOver()
})
collect: (monitor) => ({ isOver: !!monitor.isOver() })
});
const [{ isDragSign }, dragSignature] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 1,
text: "signature"
},
collect: (monitor) => ({
isDragSign: !!monitor.isDragging()
})
item: { type: "BOX", id: 1, text: "signature" },
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
});
const [{ isDragStamp }, dragStamp] = useDrag({
type: "BOX",
item: {
type: "BOX",
id: 2,
text: "stamp"
},
collect: (monitor) => ({
isDragStamp: !!monitor.isDragging()
})
item: { type: "BOX", id: 2, text: "stamp" },
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
});
const [uniqueId, setUniqueId] = useState("");
const [isModalRole, setIsModalRole] = useState(false);
@@ -166,6 +138,7 @@ const TemplatePlaceholder = () => {
const [fontColor, setFontColor] = useState();
const [zoomPercent, setZoomPercent] = useState(0);
const [scale, setScale] = useState(1);
const [signatureType, setSignatureType] = useState([]);
useEffect(() => {
fetchTemplate();
@@ -213,9 +186,38 @@ const TemplatePlaceholder = () => {
handleNavigation(plan);
}
}
//function to fetch tenant Details
const fetchTenantDetails = async () => {
const user = JSON.parse(
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
)
);
if (user) {
try {
const tenantDetails = await getTenantDetails(user?.objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist"));
} else if (tenantDetails) {
const signatureType = tenantDetails?.SignatureType || [];
const filterSignTypes = signatureType?.filter(
(x) => x.enabled === true
);
return filterSignTypes;
}
} catch (e) {
alert(t("user-not-exist"));
}
} else {
alert(t("user-not-exist"));
}
};
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
const fetchTemplate = async () => {
try {
const tenantSignTypes = await fetchTenantDetails();
const params = { templateId: templateId };
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
@@ -237,7 +239,18 @@ const TemplatePlaceholder = () => {
if (isEnableSubscription) {
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
}
setPdfDetails(documentData);
const userSignatureType =
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
const docSignTypes =
documentData?.[0]?.SignatureType || userSignatureType;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
setSignatureType(updatedSignatureType);
const updatedPdfDetails = [...documentData];
updatedPdfDetails[0].SignatureType = updatedSignatureType;
setPdfDetails(updatedPdfDetails);
setIsSigners(true);
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
setIsSelectId(0);
@@ -260,11 +273,7 @@ const TemplatePlaceholder = () => {
blockColor: x.blockColor
};
} else {
return {
Role: x.Role,
Id: x.Id,
blockColor: x.blockColor
};
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
}
});
setSignersData(updatedSigners);
@@ -287,11 +296,7 @@ const TemplatePlaceholder = () => {
documentData[0].Placeholders.length > 0
) {
let updatedSigners = documentData[0].Placeholders.map((x) => {
return {
Role: x.Role,
Id: x.Id,
blockColor: x.blockColor
};
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
});
setSignerPos(documentData[0].Placeholders);
setUniqueId(updatedSigners[0].Id);
@@ -304,17 +309,33 @@ const TemplatePlaceholder = () => {
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
const loadObj = {
isLoad: false
};
setHandleError(t("something-went-wrong-mssg"));
setIsLoading(loadObj);
setIsLoading({ isLoad: false });
} else {
setHandleError(t("no-data-avaliable"));
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
setIsLoading({ isLoad: false });
}
const res = await contractUsers();
if (res[0] && res.length) {
setSignerUserId(res[0].objectId);
setCurrentEmail(res[0].Email);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRecipients = tourstatus.filter(
(data) => data.templateTour
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].templateTour);
}
}
setIsLoading({ isLoad: false });
} else if (res === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg"));
setIsLoading({ isLoad: false });
} else if (res.length === 0) {
setHandleError(t("no-data-avaliable"));
setIsLoading({ isLoad: false });
}
} catch (err) {
console.log("err ", err);
@@ -324,37 +345,6 @@ const TemplatePlaceholder = () => {
setHandleError(t("something-went-wrong-mssg"));
}
}
const res = await contractUsers();
if (res[0] && res.length) {
setSignerUserId(res[0].objectId);
setCurrentEmail(res[0].Email);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRecipients = tourstatus.filter(
(data) => data.templateTour
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].templateTour);
}
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (res === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
setHandleError(t("something-went-wrong-mssg"));
setIsLoading(loadObj);
} else if (res.length === 0) {
setHandleError(t("no-data-avaliable"));
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
}
};
//function for setting position after drop signature button over pdf
@@ -379,8 +369,10 @@ const TemplatePlaceholder = () => {
);
const key = randomId();
const dragTypeValue = item?.text ? item.text : monitor.type;
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
const widgetWidth =
defaultWidthHeight(dragTypeValue).width * containerScale;
const widgetHeight =
defaultWidthHeight(dragTypeValue).height * containerScale;
let dropData = [],
currentPagePosition;
let placeHolder;
@@ -402,10 +394,7 @@ const TemplatePlaceholder = () => {
Height: widgetHeight / (containerScale * scale)
};
dropData.push(dropObj);
placeHolder = {
pageNumber: pageNumber,
pos: dropData
};
placeHolder = { pageNumber: pageNumber, pos: dropData };
} else {
const offset = monitor.getClientOffset();
//adding and updating drop position in array when user drop signature button in div
@@ -436,10 +425,7 @@ const TemplatePlaceholder = () => {
};
dropData.push(dropObj);
placeHolder = {
pageNumber: pageNumber,
pos: dropData
};
placeHolder = { pageNumber: pageNumber, pos: dropData };
}
let filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
const getPlaceHolder = filterSignerPos?.placeHolder;
@@ -456,10 +442,7 @@ const TemplatePlaceholder = () => {
);
const getPos = currentPagePosition?.pos;
const newSignPos = getPos.concat(dropData);
let xyPos = {
pageNumber: pageNumber,
pos: newSignPos
};
let xyPos = { pageNumber: pageNumber, pos: newSignPos };
updatePlace.push(xyPos);
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
@@ -495,7 +478,6 @@ const TemplatePlaceholder = () => {
setWidgetType(dragTypeValue);
setSignKey(key);
setSelectWidgetId(key);
setIsMailSend(false);
} else {
setIsReceipent(false);
@@ -594,9 +576,7 @@ const TemplatePlaceholder = () => {
}
}
setIsMailSend(false);
setTimeout(() => {
setIsDragging(false);
}, 200);
setTimeout(() => setIsDragging(false), 200);
};
//function for delete signature block
const handleDeleteSign = (key, Id) => {
@@ -712,7 +692,7 @@ const TemplatePlaceholder = () => {
}, 2000);
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [signerPos]);
}, [signerPos, signatureType]);
// `autosavedetails` is used to save template details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
const autosavedetails = async () => {
@@ -734,6 +714,7 @@ const TemplatePlaceholder = () => {
templateCls.id = templateId;
templateCls.set("Placeholders", signerPos);
templateCls.set("Signers", signers);
templateCls.set("SignatureType", signatureType);
await templateCls.save();
} catch (err) {
console.log("error in autosave template", err);
@@ -742,8 +723,7 @@ const TemplatePlaceholder = () => {
const handleSaveTemplate = async () => {
if (signersdata?.length) {
const loadObj = { isLoad: true, message: t("loading-mssg") };
setIsLoading(loadObj);
setIsLoading({ isLoad: true, message: t("loading-mssg") });
setIsSendAlert(false);
let signers = [];
if (signersdata?.length > 0) {
@@ -782,7 +762,8 @@ const TemplatePlaceholder = () => {
NextReminderDate: pdfDetails[0]?.NextReminderDate,
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
IsTourEnabled: pdfDetails[0]?.IsTourEnabled === true ? true : false,
URL: pdfUrl
URL: pdfUrl,
SignatureType: signatureType
};
const updateTemplate = new Parse.Object("contracts_Template");
updateTemplate.id = templateId;
@@ -891,9 +872,7 @@ const TemplatePlaceholder = () => {
`${localStorage.getItem(
"baseUrl"
)}classes/contracts_Users/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{ TourStatus: updatedTourStatus },
{
headers: {
"Content-Type": "application/json",
@@ -1136,7 +1115,7 @@ const TemplatePlaceholder = () => {
isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1174,7 +1153,7 @@ const TemplatePlaceholder = () => {
defaultValue: defaultValue,
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1192,7 +1171,7 @@ const TemplatePlaceholder = () => {
values: dropdownOptions,
defaultValue: defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1227,7 +1206,15 @@ const TemplatePlaceholder = () => {
setFontColor();
};
const handleWidgetdefaultdata = (defaultdata) => {
const handleWidgetdefaultdata = (defaultdata, isSignWidget) => {
if (isSignWidget) {
const updatedPdfDetails = [...pdfDetails];
const signtypes = defaultdata.signatureType || signatureType;
updatedPdfDetails[0].SignatureType = signtypes;
// Update the SignatureType with the modified array
setPdfDetails(updatedPdfDetails);
setSignatureType(signtypes);
}
const options = ["email", "number", "text"];
let inputype;
if (defaultdata.textvalidate) {
@@ -1267,7 +1254,7 @@ const TemplatePlaceholder = () => {
}
: {},
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1283,7 +1270,7 @@ const TemplatePlaceholder = () => {
status: defaultdata.status,
defaultValue: defaultdata.defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontSize || currWidgetsDetails?.options?.fontSize || 12,
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
@@ -1723,6 +1710,7 @@ const TemplatePlaceholder = () => {
/>
</ModalUi>
<WidgetNameModal
signatureType={signatureType}
widgetName={widgetName}
defaultdata={currWidgetsDetails}
isOpen={isNameModal}
+31 -26
View File
@@ -5,6 +5,7 @@ import { NavLink, useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
import { emailRegex } from "../constant/const";
const UpdateExistUserAdmin = () => {
const { t } = useTranslation();
const navigate = useNavigate();
@@ -35,33 +36,37 @@ const UpdateExistUserAdmin = () => {
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmitLoading(true);
try {
const updateUserAsAdmin = await Parse.Cloud.run(
"updateuserasadmin",
formdata
);
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
if (updateUserAsAdmin === "admin_created") {
setIsAlert({ type: "success", msg: t("admin-created") });
navigate("/");
if (!emailRegex.test(formdata.email)) {
alert("Please enter a valid email address.");
} else {
setIsSubmitLoading(true);
try {
const updateUserAsAdmin = await Parse.Cloud.run(
"updateuserasadmin",
formdata
);
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
if (updateUserAsAdmin === "admin_created") {
setIsAlert({ type: "success", msg: t("admin-created") });
navigate("/");
}
} catch (err) {
console.log("err in updateuserasadmin", err.code);
if (err.code === 404) {
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
} else if (err.code === 101) {
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
} else if (err.code === 137) {
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
} else {
setErrMsg(t("something-went-wrong-mssg"));
}
} finally {
setIsSubmitLoading(false);
setTimeout(() => {
setIsAlert(() => ({ type: "danger", msg: "" }));
}, 2000);
}
} catch (err) {
console.log("err in updateuserasadmin", err.code);
if (err.code === 404) {
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
} else if (err.code === 101) {
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
} else if (err.code === 137) {
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
} else {
setErrMsg(t("something-went-wrong-mssg"));
}
} finally {
setIsSubmitLoading(false);
setTimeout(() => {
setIsAlert(() => ({ type: "danger", msg: "" }));
}, 2000);
}
};
return (
+57 -119
View File
@@ -1,7 +1,9 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import Loader from "./Loader";
import { useTranslation } from "react-i18next";
import axios from "axios";
import { getTenantDetails } from "../constant/Utils";
import { emailRegex } from "../constant/const";
const AddContact = (props) => {
const { t } = useTranslation();
const [name, setName] = useState("");
@@ -12,6 +14,7 @@ const AddContact = (props) => {
const [isUserExist, setIsUserExist] = useState(false);
useEffect(() => {
checkUserExist();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Load user details from localStorage when the component mounts
useEffect(() => {
@@ -26,127 +29,61 @@ const AddContact = (props) => {
}, [addYourself]);
const checkUserExist = async () => {
const user = Parse.User.current();
try {
const query = new Parse.Query("contracts_Contactbook");
query.equalTo("CreatedBy", user);
query.notEqualTo("IsDeleted", true);
query.equalTo("Email", user.getEmail());
const res = await query.first();
// console.log(res);
if (!res) {
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/isuserincontactbook`;
const token = props?.jwttoken
? { jwttoken: props?.jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token
};
const axiosRes = await axios.post(url, {}, { headers });
const contactRes = axiosRes?.data?.result || {};
if (!contactRes?.objectId) {
setIsUserExist(true);
}
} catch (err) {
console.log("err", err);
console.log("err ", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
if (localStorage.getItem("TenantId")) {
try {
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();
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");
if (localStorage.getItem("TenantId")) {
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 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));
props.details(parseData);
if (props.closePopup) {
props.closePopup();
}
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 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(parseData);
}
if (props.closePopup) {
props.closePopup();
}
if (!emailRegex.test(email)) {
alert("Please enter a valid email address.");
} else {
setIsLoader(true);
const user = JSON.parse(
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
)
);
const userId = user?.objectId || "";
const tenantDetails = await getTenantDetails(userId, props.jwttoken);
const tenantId = tenantDetails?.objectId || "";
if (tenantId) {
try {
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/savecontact`;
const token = props?.jwttoken
? { jwttoken: props?.jwttoken }
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
const data = { name, email, phone, tenantId };
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
...token
};
const axiosRes = await axios.post(url, data, { headers });
const contactRes = axiosRes?.data?.result || {};
if (contactRes?.objectId) {
props.details(contactRes);
if (props.closePopup) {
props.closePopup();
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
@@ -155,18 +92,19 @@ const AddContact = (props) => {
setEmail("");
}
}
} else {
alert(t("add-signer-alert"));
} catch (err) {
console.log("Err", err);
setIsLoader(false);
if (err?.response?.data?.error?.includes("already exists")) {
alert(t("add-signer-alert"));
} else {
alert(t("something-went-wrong-mssg"));
}
}
} catch (err) {
// console.log("err", err);
} else {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
} else {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
};
@@ -15,7 +15,10 @@ import {
copytoData,
fetchUrl,
getSignedUrl,
replaceMailVaribles
getTenantDetails,
handleSignatureType,
replaceMailVaribles,
signatureTypes
} from "../constant/Utils";
import Confetti from "react-confetti";
import EditorToolbar, {
@@ -77,6 +80,7 @@ const ReportTable = (props) => {
const [isDownloadModal, setIsDownloadModal] = useState(false);
const [isEmbed, setIsEmbed] = useState(false);
const [isPublicTour, setIsPublicTour] = useState();
const [signatureType, setSignatureType] = useState([]);
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
const startIndex = (currentPage - 1) * props.docPerPage;
@@ -195,6 +199,33 @@ const ReportTable = (props) => {
}
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
//function to fetch tenant Details
const fetchTenantDetails = async () => {
const user = JSON.parse(
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
)
);
if (user) {
try {
const tenantDetails = await getTenantDetails(user?.objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert(t("user-not-exist"));
} else if (tenantDetails) {
const signatureType = tenantDetails?.SignatureType || [];
const filterSignTypes = signatureType?.filter(
(x) => x.enabled === true
);
return filterSignTypes;
}
} catch (e) {
alert(t("user-not-exist"));
}
} else {
alert(t("user-not-exist"));
}
};
// `handleURL` is used to open microapp
const handleURL = async (item, act) => {
if (props.ReportName === "Templates") {
@@ -203,9 +234,7 @@ const ReportTable = (props) => {
} else {
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
try {
const params = {
templateId: item.objectId
};
const params = { templateId: item.objectId };
const templateDeatils = await axios.post(
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
params,
@@ -249,6 +278,16 @@ const ReportTable = (props) => {
}
}
}
const tenantSignTypes = await fetchTenantDetails();
const docSignTypes = Doc?.SignatureType || signatureTypes;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
const SignatureType =
updatedSignatureType.length > 0
? { SignatureType: updatedSignatureType }
: {};
let placeholdersArr = [];
if (Doc.Placeholders?.length > 0) {
placeholdersArr = Doc.Placeholders;
@@ -275,7 +314,8 @@ const ReportTable = (props) => {
AutomaticReminders: Doc?.AutomaticReminders || false,
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5,
IsEnableOTP: Doc?.IsEnableOTP || false,
FileAdapterId: Doc?.FileAdapterId || ""
FileAdapterId: Doc?.FileAdapterId || "",
...SignatureType
};
try {
const res = await axios.post(
@@ -910,6 +950,13 @@ const ReportTable = (props) => {
}
);
const templateRes = axiosRes.data && axiosRes.data.result;
const tenantSignTypes = await fetchTenantDetails();
const docSignTypes = templateRes?.SignatureType || signatureTypes;
const updatedSignatureType = await handleSignatureType(
tenantSignTypes,
docSignTypes
);
setSignatureType(updatedSignatureType);
setPlaceholders(templateRes?.Placeholders);
setTemplateDetails(templateRes);
setIsLoader({});
@@ -1165,7 +1212,7 @@ const ReportTable = (props) => {
<Loader />
</div>
)}
<div className="p-2 w-full overflow-hidden bg-base-100 text-base-content op-card shadow-lg">
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isCelebration && (
<div className="relative z-[1000]">
<Confetti
@@ -1226,11 +1273,17 @@ const ReportTable = (props) => {
)}
</div>
<div
className={`${
isDashboard && props.List?.length > 0 ? "min-h-[317px]" : "h-full"
} overflow-auto w-full`}
className={`overflow-auto w-full border-b ${
props.List?.length > 0
? isDashboard
? "min-h-[317px]"
: currentList?.length === props.docPerPage
? "h-fit"
: "h-screen"
: ""
}`}
>
<table className="op-table border-collapse w-full ">
<table className="op-table border-collapse w-full mb-4">
<thead className="text-[14px]">
<tr className="border-y-[1px]">
{props.heading?.map((item, index) => (
@@ -1311,7 +1364,14 @@ const ReportTable = (props) => {
</td>
</tr>
) : (
<tr className="border-y-[1px]" key={index}>
<tr
className={`${
currentList?.length === props.docPerPage
? "last:border-none"
: ""
} border-y-[1px] `}
key={index}
>
{props.heading.includes("Sr.No") && (
<th className="px-4 py-2">
{startIndex + index + 1}
@@ -1479,13 +1539,13 @@ const ReportTable = (props) => {
}
className="op-btn op-btn-primary"
>
{t("submit")}
{t("yes")}
</button>
<button
onClick={() => handleClose(item)}
className="op-btn op-btn-secondary"
>
{t("cancel")}
{t("no")}
</button>
</div>
</div>
@@ -1590,9 +1650,7 @@ const ReportTable = (props) => {
className={
act.action !== "option"
? `${
act?.btnColor
? act.btnColor
: ""
act?.btnColor || ""
} op-btn op-btn-sm mr-1`
: "text-base-content focus:outline-none text-lg mr-2 relative"
}
@@ -1609,9 +1667,10 @@ const ReportTable = (props) => {
)}`}
</span>
)}
{/* template report */}
{isOption[item.objectId] &&
act.action === "option" && (
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow-black/20 shadow bg-base-100 text-base-content rounded-box">
{act.subaction?.map((subact) => (
<li
key={subact.btnId}
@@ -1665,9 +1724,10 @@ const ReportTable = (props) => {
{t(`btnLabel.${act.btnLabel}`)}
</span>
)}
{/* doc report */}
{isOption[item.objectId] &&
act.action === "option" && (
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow-black/20 shadow bg-base-100 text-base-content rounded-box">
{act.subaction?.map((subact) => (
<li
key={subact.btnId}
@@ -1920,6 +1980,7 @@ const ReportTable = (props) => {
Placeholders={placeholders}
item={templateDeatils}
handleClose={handleQuickSendClose}
signatureType={signatureType}
/>
)}
</ModalUi>
@@ -2129,6 +2190,24 @@ const ReportTable = (props) => {
)}
</tbody>
</table>
{props.List?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
</div>
<div className="op-join flex flex-wrap items-center p-2">
{props.List.length > props.docPerPage && (
@@ -2160,24 +2239,6 @@ const ReportTable = (props) => {
</button>
)}
</div>
{props.List?.length <= 0 && (
<div
className={`${
isDashboard ? "h-[317px]" : ""
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
>
<div className="w-[60px] h-[60px] overflow-hidden">
<img
className="w-full h-full object-contain"
src={pad}
alt="img"
/>
</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
title={t("add-contact")}
isOpen={isContactform}
@@ -19,11 +19,16 @@ const LinkUserModal = (props) => {
details={props.handleAddUser}
closePopup={props.closePopup}
signersData={props?.signersData}
jwttoken={props?.jwttoken}
/>
<div className="op-divider text-base-content mx-[25%] my-1">
{t("or")}
</div>
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
<AddContact
details={props.handleAddUser}
closePopup={props.closePopup}
jwttoken={props?.jwttoken}
/>
</ModalUi>
);
};
@@ -1,5 +1,5 @@
{
"header-news": "New feature: Public profiles are now live! Easily create and share public templates",
"header-news": "New feature: Teams plan users can now integrate their own AWS S3 buckets for file storage",
"header-news-btn": "Setup Now",
"create-account": "Create Account",
"login": "Login",
@@ -94,6 +94,7 @@
"My Signature": "My Signature",
"API Token": "API Token",
"Webhook": "Webhook",
"Preferences": "Preferences",
"Teams": "Teams",
"Team-update-successfully": "Team Updated successfully.",
"Users": "Users"
@@ -291,7 +292,7 @@
"subject": "Subject",
"body": "Body",
"add-contact": "Add Contact",
"add-signer-alert": "Contact already exist!",
"add-signer-alert": "Contact already exist! Please select it from Signers dropdown",
"record-delete-alert": "Record deleted successfully!",
"record-revoke-alert": "Record revoked successfully!",
"mail-sent-alert": "Mail sent successfully.",
@@ -445,7 +446,8 @@
"cutomize-email": "Cutomize Email",
"upgrade-to-customize-email": "Upgrade to customize Email",
"sign-url": "Sign url",
"Mails Sent": "Mails Sent",
"mails-sent": "Mails Sent",
"mail-not-delivered": "Mail not delivered",
"document-alert": "Document Alert",
"owner-subscription-expired": "Owner's subscription has expired.",
"subscription-expired": "Subscription Expired",
@@ -679,9 +681,21 @@
"p3": "Tip: If your signers are familiar with OpenSign and frequently use it, you may want to disable the guided tour for a more seamless experience. Choose the option that best fits your document signing needs."
},
"form-title-1": "Document Flow Setting",
"form-title-2": "Security Setting",
"form-title-2": "Security Settings",
"public-tour-message": "The template needs to be public before you can generate a shareable link.",
"add-user-template": "You need to add a role before you can add fields for it.",
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>"
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>",
"text-field-tour": "Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
"attach-signer-tour": "You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
"allowed-signature-types": "Allowed signature types",
"at-least-one-signature-type": "At least one signature type should be enabled.",
"expect-default-one-signature-type": "Please enable one additional signature type apart from the default.",
"add-signer-note": "Note - This contact will be added to your contact list",
"allowed-signature-types-help": {
"p1": "This signature preference controls which signing options are available to your signers. Only the signature types you select will appear in the signature widget when a document is signed. For example, if you choose only the 'Draw' option in preferences, your signer will see only the 'Draw' option in the signature widget, while the other three options will not be available.",
"l1": "Create a handwritten signature directly on the screen using your finger or a stylus.",
"l2": "Enter your name and choose from various font styles to create a signature.",
"l3": "Upload an existing digital version of your signature.",
"l4": "Use a pre-set signature style saved under 'My Signature' in your settings."
}
}
@@ -1,5 +1,5 @@
{
"header-news": "Nueva funcionalidad: ¡Los perfiles públicos ahora están disponibles! Crea y comparte plantillas públicas fácilmente",
"header-news": "Nueva característica: los usuarios del plan Teams ahora pueden integrar sus propios depósitos de AWS S3 para el almacenamiento de archivos",
"header-news-btn": "Configurar ahora",
"create-account": "Crear cuenta",
"login": "Iniciar sesión",
@@ -94,6 +94,7 @@
"My Signature": "Mi firma",
"API Token": "Token API",
"Webhook": "Webhook",
"Preferences": "Preferencias",
"Teams": "Equipos",
"Team-update-successfully": "Equipo actualizado correctamente.",
"Users": "Usuarios"
@@ -291,7 +292,7 @@
"subject": "Asunto",
"body": "Cuerpo",
"add-contact": "Agregar contacto",
"add-signer-alert": "¡El contacto ya existe!",
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
"record-delete-alert": "¡Entrada eliminada exitosamente!",
"record-revoke-alert": "¡Entrada revocada exitosamente!",
"mail-sent-alert": "Correo enviado exitosamente.",
@@ -445,7 +446,8 @@
"cutomize-email": "Personalizar correo",
"upgrade-to-customize-email": "Mejora para personalizar el correo",
"sign-url": "URL de firma",
"Mails Sent": "Correos enviados",
"mails-sent": "Correos enviados",
"mail-not-delivered": "correo no entregado",
"document-alert": "Alerta de documento",
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
"subscription-expired": "Suscripción expirada",
@@ -676,11 +678,24 @@
"add-user-template": "Es necesario agregar un rol antes de poder agregarle campos.",
"pdf-uncompatible": "Este pdf no es compatible con opensign, comuníquese con <support@opensignlabs.com>",
"enable-tour": "Habilitar recorrido",
"istourenabled-help":{
"istourenabled-help": {
"p1": "Esto permitirá realizar una visita guiada para los firmantes, brindando instrucciones durante el proceso de firma.",
"p2": "Esto desactivará la visita guiada, lo que garantizará una experiencia de firma más rápida e ininterrumpida.",
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
},
"form-title-1": "Configuración del flujo de documentos",
"form-title-2": "Configuración de seguridad"
"form-title-2": "Configuración de seguridad",
"text-field-tour": "Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
"attach-signer-tour": "Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
"allowed-signature-types": "Tipos de firma permitidos",
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
"allowed-signature-types-help": {
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
"l3": "Cargue una versión digital existente de su firma.",
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
}
}
@@ -1,5 +1,5 @@
{
"header-news": "Nouvelle fonctionnalité : les profils publics sont désormais en ligne ! Créez et partagez facilement des modèles publics",
"header-news": "Nouvelle fonctionnalité : les utilisateurs du forfait Teams peuvent désormais intégrer leurs propres compartiments AWS S3 pour le stockage de fichiers",
"header-news-btn": "Configurer maintenant",
"create-account": "Créer un compte",
"login": "Se Connecter",
@@ -94,6 +94,7 @@
"My Signature": "Ma signature",
"API Token": "Jeton API",
"Webhook": "Webhook",
"Preferences": "Préférences",
"Teams": "Équipe",
"Team-update-successfully": "Équipe mise à jour avec succès.",
"Users": "Utilisateurs"
@@ -290,7 +291,7 @@
"subject": "Sujet",
"body": "Corps",
"add-contact": "Ajouter le contact",
"add-signer-alert": "Le contact existe déjà !",
"add-signer-alert": "Le contact existe déjà ! Veuillez le sélectionner dans la liste déroulante 'Signataires'",
"record-delete-alert": "Enregistrement supprimé avec succès !",
"record-revoke-alert": " Enregistrement révoqué avec succès ! ",
"mail-sent-alert": "Courriel envoyé avec succès.",
@@ -444,7 +445,8 @@
"cutomize-email": "Personnaliser l'e-mail",
"upgrade-to-customize-email": "Mise à niveau pour personnaliser l'e-mail",
"sign-url": "Signer l'URL",
"Mails Sent": "Mails envoyés avec succès !",
"mails-sent": "Mails envoyés avec succès !",
"mail-not-delivered": "Courrier non distribué",
"document-alert": "Alerte document",
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
"subscription-expired": "Abonnement expiré",
@@ -672,14 +674,27 @@
"server-down": "Impossible de se connecter au serveur OpenSign. Si vous hébergez vous-même OpenSign, veuillez vous assurer que toutes les étapes de la documentation ont été correctement suivies. Si vous exécutez OpenSign localement, vous y accédez peut-être via un numéro de port incorrect.",
"admin-exists": "L'administrateur existe déjà. Veuillez vous connecter à l'application en utilisant les informations d'identification de l'administrateur afin de gérer les utilisateurs.",
"enable-tour": "Activer la visite",
"istourenabled-otp-help":{
"istourenabled-otp-help": {
"p1": "Cela permettra une visite guidée pour les signataires, fournissant des instructions pendant le processus de signature.",
"p2": "Cela désactivera la visite guidée, garantissant ainsi une expérience de signature plus rapide et ininterrompue.",
"p3": "Conseil : si vos signataires connaissent OpenSign et l'utilisent fréquemment, vous souhaiterez peut-être désactiver la visite guidée pour une expérience plus fluide. Choisissez l'option qui correspond le mieux à vos besoins en matière de signature de documents."
"p3": "Conseil : si vos signataires connaissent OpenSign et l'utilisent fréquemment, vous souhaiterez peut-être désactiver la visite guidée pour une expérience plus fluide. Choisissez l'option qui correspond le mieux à vos besoins en matière de signature de documents."
},
"form-title-1": "Paramètre du flux de documents",
"form-title-2": "Paramètre de sécurité",
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
"add-user-template": "Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
"pdf-uncompatible": "Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>"
"pdf-uncompatible": "Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>",
"text-field-tour": "Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
"attach-signer-tour": "Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
"allowed-signature-types": "Types de signature autorisés",
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
"expect-default-one-signature-type": "Veuillez activer un type de signature supplémentaire en plus de celui par défaut.",
"add-signer-note": "Remarque - Ce contact sera ajouté à votre liste de contacts",
"allowed-signature-types-help": {
"p1": "Cette préférence de signature contrôle les options de signature disponibles pour vos signataires. Seuls les types de signature que vous sélectionnez apparaîtront dans le widget de signature lorsqu'un document est signé. Par exemple, si vous choisissez uniquement l'option 'Draw' dans les préférences, votre signataire ne verra que l'option 'Draw' dans le widget de signature, tandis que les trois autres options ne seront pas disponibles.",
"l1": "Créez une signature manuscrite directement sur l'écran à l'aide de votre doigt ou d'un stylet.",
"l2": "Entrez votre nom et choisissez parmi différents styles de police pour créer une signature.",
"l3": "Téléchargez une version numérique existante de votre signature.",
"l4": "Utilisez un style de signature prédéfini enregistré sous 'Ma signature' dans vos paramètres."
}
}
+45 -4
View File
@@ -127,9 +127,15 @@ export const updateMailCount = async (extUserId, plan, monthchange) => {
};
export function formatWidgetOptions(type, options) {
const colorsArr = ['red', 'black', 'blue', 'yellow'];
const fontSizes = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
const status = options?.required === true ? 'required' : 'optional' || 'required';
const defaultValue = options?.default || '';
const values = options?.values || [];
const color = options?.color ? options.color : 'black';
const fontColor = colorsArr.includes(color) ? color : 'black';
const size = options?.fontsize ? parseInt(options.fontsize) : 12;
const fontSize = fontSizes.includes(size) ? size : 12;
switch (type) {
case 'signature':
return { name: 'signature', status: 'required' };
@@ -140,13 +146,34 @@ export function formatWidgetOptions(type, options) {
case 'image':
return { status: status, name: options.name || 'image' };
case 'email':
return { status: status, name: options.name || 'email', validation: { type: 'email' } };
return {
status: status,
name: options.name || 'email',
validation: { type: 'email' },
fontColor: fontColor,
fontSize: fontSize,
};
case 'name':
return { status: status, name: options.name || 'name' };
return {
status: status,
name: options.name || 'name',
fontColor: fontColor,
fontSize: fontSize,
};
case 'job title':
return { status: status, name: options.name || 'job title' };
return {
status: status,
name: options.name || 'job title',
fontColor: fontColor,
fontSize: fontSize,
};
case 'company':
return { status: status, name: options.name || 'company' };
return {
status: status,
name: options.name || 'company',
fontColor: fontColor,
fontSize: fontSize,
};
case 'date': {
let today = new Date();
let dd = String(today.getDate()).padStart(2, '0');
@@ -160,6 +187,8 @@ export function formatWidgetOptions(type, options) {
name: options.name || 'date',
response: defaultValue || today,
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
fontColor: fontColor,
fontSize: fontSize,
};
}
case 'textbox':
@@ -169,6 +198,8 @@ export function formatWidgetOptions(type, options) {
defaultValue: defaultValue,
hint: options.hint,
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
fontColor: fontColor,
fontSize: fontSize,
};
case 'checkbox': {
const arr = options?.values;
@@ -188,6 +219,8 @@ export function formatWidgetOptions(type, options) {
maxRequiredCount: options?.validation?.maxselections || 0,
},
defaultValue: selectedvalues || [],
fontColor: fontColor,
fontSize: fontSize,
};
}
case 'radio button': {
@@ -198,6 +231,8 @@ export function formatWidgetOptions(type, options) {
isReadOnly: options?.readonly || false,
isHideLabel: options?.hidelabel || false,
defaultValue: defaultValue,
fontColor: fontColor,
fontSize: fontSize,
};
}
case 'dropdown':
@@ -206,6 +241,8 @@ export function formatWidgetOptions(type, options) {
name: options.name || 'dropdown',
values: values,
defaultValue: defaultValue,
fontColor: fontColor,
fontSize: fontSize,
};
default:
break;
@@ -234,3 +271,7 @@ export const planCredits = {
'teams-monthly': 100,
'teams-yearly': 500,
};
export function parseJwt(token) {
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
}
@@ -0,0 +1,35 @@
The OpenSign Enterprise license (the “Enterprise License”)
Copyright (c) 2020-2024 Qik Innovations Private Limited.
With regard to the OpenSign Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the OpenSign Subscription Terms of Service(the “Enterprise Terms”), or other
agreement governing the use of the Software, as agreed by you and OpenSign,
and otherwise have a valid OpenSign Enterprise license for the
correct number of user seats. Subject to the foregoing sentence, you are free to
modify this Software and publish patches to the Software. You agree that OpenSign
and/or its licensors (as applicable) retain all right, title and interest in and
to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid OpenSign Enterprise license for the correct
number of user seats. Notwithstanding the foregoing, you may copy and modify
the Software for development and testing purposes, without requiring a
subscription. You agree that OpenSign and/or its licensors (as applicable) retain
all right, title and interest in and to all such modifications. You are not
granted any other rights beyond what is expressly stated herein. Subject to the
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the OpenSign Software, those
components are licensed under the original license provided by the owner of the
applicable component.
@@ -9,7 +9,6 @@ import getDocument from './routes/getDocument.js';
import getContact from './routes/getContact.js';
import deleteContact from './routes/deleteContact.js';
import getContactList from './routes/getContactList.js';
import draftDocument from './routes/draftDocument.js';
import getTemplate from './routes/getTemplate.js';
import deletedTemplate from './routes/deleteTemplate.js';
import getTemplatetList from './routes/getTemplateList.js';
@@ -30,6 +29,7 @@ import createFolder from './routes/createFolder.js';
import updateFolder from './routes/updateFolder.js';
import getFolderList from './routes/getFolderList.js';
import deleteFolder from './routes/deleteFolder.js';
import draftTemplate from './routes/draftTemplate.js';
dotenv.config();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
@@ -59,9 +59,6 @@ app.post('/createdocumentwithbinary', upload.array('file', 1), createDocumentwit
// create Document with co-ordinate
app.post('/createdocument', createDocumentwithCoordinate);
// create Document with base64 without placeholder
app.post('/draftdocument', draftDocument);
// create Document with templateId
app.post('/createdocument/:template_id', createDocumentWithTemplate);
@@ -77,6 +74,9 @@ app.delete('/document/:document_id', deleteDocument);
// get all types of documents on the basis of doctype
app.get('/documentlist/:doctype', getDocumentList);
// create draft template with file base64
app.post('/drafttemplate', draftTemplate);
// create Template with co-ordinate
app.post('/createtemplate', createTemplatewithCoordinate);
@@ -49,7 +49,7 @@ export default async function createDocumentWithTemplate(request, response) {
const send_email = request.body.send_email;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || false;
const sendInOrder = request.body.sendInOrder || true;
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
try {
@@ -62,7 +62,7 @@ export default async function createDocumentwithCoordinate(request, response) {
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || false;
const sendInOrder = request.body.sendInOrder || true;
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
@@ -17,7 +17,7 @@ export default async function createTemplatewithCoordinate(request, response) {
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false;
const SendinOrder = request.body.sendInOrder || true;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
@@ -1,168 +0,0 @@
import axios from 'axios';
import { customAPIurl, saveFileUsage } from '../../../../Utils.js';
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function draftDocument(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const send_email = request.body.send_email || true;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL);
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
// Valid Token then proceed request
if (signers && signers.length > 0) {
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
object.set('IsSendMail', send_email);
object.set('IsEnableOTP', isEnableOTP);
object.set('IsTourEnabled', isTourEnabled);
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
let contact = [];
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
});
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
});
}
}
}
object.set('Signers', contact);
}
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
url: `${baseUrl.origin}/placeholdersign/${res.id}`,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,238 @@
import axios from 'axios';
import {
color,
customAPIurl,
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
} from '../../../../Utils.js';
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
import jwt from 'jsonwebtoken';
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function draftTemplate(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || true;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
const isPublic = request.body?.isPublic;
const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL).origin;
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
contractsUser.include('TenantId');
const extUser = await contractsUser.first({ useMasterKey: true });
const parseExtUser = JSON.parse(JSON.stringify(extUser));
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const filename = sanitizeFileName(`${name}.pdf`);
let adapter = {};
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
if (ActiveFileAdapter) {
adapter =
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
}
if (adapter?.id) {
const filedata = Buffer.from(base64File, 'base64');
// `uploadFileToS3` is used to save document in user's file storage
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
} else {
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Template');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
object.set('IsEnableOTP', isEnableOTP);
object.set('IsTourEnabled', isTourEnabled);
const Public = isPublic !== undefined ? isPublic : false;
if (isPublic !== undefined) {
object.set('IsPublic', Public);
}
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
if (element?.name && element?.email) {
const body = {
name: element?.name,
email: element?.email,
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
} else {
const newObj = { ...element, contactPtr: {}, index: index };
contact.push(newObj);
}
}
const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId);
if (updatedSigners && updatedSigners.length > 0) {
object.set(
'Signers',
updatedSigners?.map(x => x.contactPtr)
);
}
let updatePlaceholders = [];
for (const signer of contact) {
const placeHolder = [];
if (signer.widgets?.length > 0) {
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const options = formatWidgetOptions(widget.type, widget.options);
const widgetData = {
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({ pageNumber, pos: [widgetData] });
}
}
}
updatePlaceholders.push({
signerObjId: signer?.contactPtr?.objectId || '',
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
});
}
if (updatePlaceholders?.length > 0) {
object.set('Placeholders', updatePlaceholders);
}
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Template',
objectId: folderId,
});
}
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_template',
properties: { response_code: 200 },
});
}
const JwtToken = jwt.sign(
{ user_email: parseUser.userId.email, template_id: res.id },
reqToken
);
return response.json({ objectId: res.id, url: `${baseUrl}/drafttemplate/${JwtToken}` });
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
+18
View File
@@ -75,6 +75,15 @@ import saveToFileAdapter from './parsefunction/saveToFileAdapter.js';
import getFileAdapter from './parsefunction/getFileAdapter.js';
import addPfxFile from './parsefunction/addPfxFile.js';
import getPfxFile from './parsefunction/getPfxFile.js';
import getTenant from './parsefunction/getTenant.js';
import getSigners from './parsefunction/getSigners.js';
import saveFile from './parsefunction/saveFile.js';
import savecontact from './parsefunction/savecontact.js';
import isUserInContactBook from './parsefunction/isUserInContactBook.js';
import updateTourStatus from './parsefunction/updateTourStatus.js';
import saveTemplate from './parsefunction/saveTemplate.js';
import updateToPublicTemplate from './parsefunction/updateToPublicTemplate.js';
import updateSignatureType from './parsefunction/updatesignaturetype.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -162,3 +171,12 @@ Parse.Cloud.define('addfileadapter', addFileAdapter);
Parse.Cloud.define('getfileadapter', getFileAdapter);
Parse.Cloud.define('addpfx', addPfxFile);
Parse.Cloud.define('getpfx', getPfxFile);
Parse.Cloud.define('gettenant', getTenant);
Parse.Cloud.define('getsigners', getSigners);
Parse.Cloud.define('savetemplate', saveTemplate);
Parse.Cloud.define('savefile', saveFile);
Parse.Cloud.define('savecontact', savecontact);
Parse.Cloud.define('isuserincontactbook', isUserInContactBook);
Parse.Cloud.define('updatetourstatus', updateTourStatus);
Parse.Cloud.define('updatetopublictemplate', updateToPublicTemplate);
Parse.Cloud.define('updatesignaturetype', updateSignatureType);
@@ -1,33 +1,64 @@
export default async function AllowedCredits(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function checkCredits(userId) {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const AllowedCredits = _resSub?.AllowedCredits || 0;
const AddonCredits = _resSub?.AddonCredits || 0;
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const AllowedCredits = _resSub?.AllowedCredits || 0;
const AddonCredits = _resSub?.AddonCredits || 0;
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
export default async function AllowedCredits(request) {
const jwttoken = request?.headers?.jwttoken || '';
try {
if (request?.user) {
return await checkCredits(request.user.id);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await checkCredits(userId);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
console.log('err in allowedCredits', err);
@@ -1,21 +1,54 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function GetTemplate(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
const jwttoken = request.headers?.jwttoken;
const sessiontoken = request.headers?.sessiontoken;
try {
if (!ispublic) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (templateId && userId) {
let userEmail;
if (jwttoken) {
try {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
userEmail = decoded?.user_email;
} else {
return { error: 'Invalid token!' };
}
} else {
return { error: 'Invalid token!' };
}
} catch (err) {
console.log('err in jwt', err);
return { error: "You don't have access of this document!" };
}
} else if (sessiontoken) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': sessiontoken,
},
});
userEmail = userRes.data && userRes.data.email;
}
if (templateId && userEmail) {
try {
let template = new Parse.Query('contracts_Template');
template.equalTo('objectId', templateId);
@@ -24,7 +57,7 @@ export default async function GetTemplate(request) {
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userRes.data.email);
extUserQuery.equalTo('Email', userEmail);
extUserQuery.include('TeamIds');
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
@@ -49,6 +82,7 @@ export default async function GetTemplate(request) {
template.include('Signers');
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
template.include('Placeholders.signerPtr');
}
}
const res = await template.first({ useMasterKey: true });
@@ -64,6 +98,8 @@ export default async function GetTemplate(request) {
console.log('err', err);
return err;
}
} else {
return { error: "You don't have access of this document!" };
}
} else if (templateId && ispublic) {
try {
@@ -92,7 +128,7 @@ export default async function GetTemplate(request) {
}
} catch (err) {
console.log('err', err);
if (err.code == 209) {
if (err?.response?.data?.code === 209 || err.code == 209) {
return { error: 'Invalid session token' };
} else {
return { error: "You don't have access of this document!" };
@@ -71,6 +71,9 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
object.set('IsEnableOTP', template?.IsEnableOTP || false);
object.set('IsTourEnabled', template?.IsTourEnabled || false);
object.set('FileAdapterId', template?.FileAdapterId || '');
if (template?.SignatureType?.length > 0) {
object.set('SignatureType', template?.SignatureType);
}
let signers = template?.Signers || [];
const signerobj = {
__type: 'Pointer',
@@ -254,6 +257,7 @@ const deductcount = async _resSub => {
export default async function PublicUserLinkContactToDoc(req) {
const email = req.params.email;
const templateid = req.params.templateid;
const signatureType = req.params.signatureType;
const name = req.params.name;
const phone = req.params.phone;
const role = req.params.role;
@@ -301,8 +305,11 @@ export default async function PublicUserLinkContactToDoc(req) {
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
const docRes = await createDocumentFromTemplate(_template_json, existContact, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
@@ -327,9 +334,12 @@ export default async function PublicUserLinkContactToDoc(req) {
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
const docRes = await createDocumentFromTemplate(_template_json, contactRes, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
@@ -354,11 +364,14 @@ export default async function PublicUserLinkContactToDoc(req) {
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
template_json,
_template_json,
contactRes,
index
);
@@ -391,11 +404,14 @@ export default async function PublicUserLinkContactToDoc(req) {
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
template_json,
_template_json,
contactRes,
index
);
@@ -1,6 +1,6 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const licenseKey = process.env.LICENSE_KEY;
@@ -30,7 +30,8 @@ async function deductcount(docsCount, extUserId, subscription) {
console.log('Err in deduct in quick send', err);
}
}
async function sendMail(document, sessionToken) {
async function sendMail(document) {
//sessionToken
const baseUrl = new URL(process.env.PUBLIC_URL);
// console.log("pdfDetails", pdfDetails);
@@ -57,7 +58,7 @@ async function sendMail(document, sessionToken) {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
sessionToken: sessionToken,
// sessionToken: sessionToken,
};
const objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
@@ -112,13 +113,232 @@ async function sendMail(document, sessionToken) {
}
}
}
async function callwebhookevent(document) {
const Signers = document.Signers;
const allSigner = document?.Placeholders?.map(item => {
if (item.signerObjId) {
const signer = Signers?.find(e => item?.signerPtr?.objectId === e?.objectId);
if (signer) {
return {
role: item?.Role || '',
name: signer?.Name || '',
email: signer?.Email || '',
phone: signer?.Phone || '',
};
}
} else {
return { role: item?.Role || '', name: '', email: item?.email || '', phone: '' };
}
});
const params = {
event: 'created',
body: {
objectId: document?.objectId,
file: document?.SignedUrl || document?.URL,
name: document?.Name,
note: document?.Note || '',
description: document?.Description || '',
signers: allSigner,
createdBy: document?.ExtUserPtr.Email,
createdAt: document?.createdAt,
},
};
try {
await axios
.post(document?.ExtUserPtr?.Webhook, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: document.CreatedBy.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err?.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: document.CreatedBy.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err?.message);
}
});
} catch (err) {
console.log('Err ', err?.message);
}
}
async function batchQuery(userId, Documents, Ip, parseConfig) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
try {
const requests = Documents.map(x => {
const Signers = x.Signers;
const allSigner = x?.Placeholders?.map(
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
).filter(signer => Object.keys(signer).length > 0);
const date = new Date();
const isoDate = date.toISOString();
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
if (allSigner && allSigner.length > 0) {
allSigner.forEach(x => {
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
Acl = { ...Acl, ...obj };
});
}
return {
method: 'POST',
path: '/app/classes/contracts_Document',
body: {
Name: x.Name,
URL: x.URL,
Note: x.Note,
Description: x.Description,
CreatedBy: x.CreatedBy,
SendinOrder: x.SendinOrder || true,
ExtUserPtr: {
__type: 'Pointer',
className: x.ExtUserPtr.className,
objectId: x.ExtUserPtr?.objectId,
},
Placeholders: x.Placeholders.map(y =>
y?.signerPtr?.objectId
? {
...y,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.signerPtr.objectId,
},
signerObjId: y.signerObjId,
}
: { ...y, signerPtr: {}, signerObjId: '' }
),
SignedUrl: x.URL || x.SignedUrl,
SentToOthers: true,
Signers: allSigner?.map(y => ({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.objectId,
})),
ACL: Acl,
SentToOthers: true,
RemindOnceInEvery: x.RemindOnceInEvery || 5,
AutomaticReminders: x.AutomaticReminders || false,
TimeToCompleteDays: x.TimeToCompleteDays || 15,
OriginIp: Ip,
DocSentAt: { __type: 'Date', iso: isoDate },
IsEnableOTP: x?.IsEnableOTP || false,
IsTourEnabled: x?.IsTourEnabled || false,
FileAdapterId: x?.FileAdapterId || '',
...(x?.SignatureType ? { SignatureType: x?.SignatureType } : {}),
},
};
});
// console.log('requests ', requests);
if (licenseKey) {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExt.TenantId.objectId,
});
subscription.include('ExtUserPtr');
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (requests?.length <= totalcredits) {
const response = await axios.post('batch', { requests: requests }, parseConfig);
// Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = Documents.map((x, i) => ({
...x,
objectId: response.data[i]?.success?.objectId,
createdAt: response.data[i]?.success?.createdAt,
}));
deductcount(response.data.length, resExt.id, _resSub);
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], ''); //sessionToken
}
callwebhookevent(Documents[0]);
return 'success';
}
} else {
throw new Parse.Error(429, 'Quota reached, Please buy credits and try again later.');
}
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Please purchase or renew your subscription.'
);
}
} else {
if (requests?.length > 0) {
const newrequests = [requests?.[0]];
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
// Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const document = Documents?.[0];
const updateDocuments = {
...document,
objectId: response.data[0]?.success?.objectId,
createdAt: response.data[0]?.success?.createdAt,
};
deductcount(response.data.length, resExt.id);
sendMail(updateDocuments); //sessionToken
callwebhookevent(Documents[0]);
return 'success';
}
}
}
} catch (error) {
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
const msg =
error?.response?.data?.error ||
error?.response?.data ||
error?.message ||
'Something went wrong.';
console.log('Error performing batch query:', code, msg);
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
export default async function createBatchDocs(request) {
const strDocuments = request.params.Documents;
const sessionToken = request.headers['sessiontoken'];
const sessionToken = request.headers?.sessiontoken;
const jwttoken = request.headers?.jwttoken;
const Documents = JSON.parse(strDocuments);
const Ip = request?.headers?.['x-real-ip'] || '';
const parseConfig = {
baseURL: serverUrl, //localStorage.getItem('baseUrl'),
baseURL: serverUrl,
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': sessionToken,
@@ -126,167 +346,38 @@ export default async function createBatchDocs(request) {
},
};
try {
const user = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
if (user.data) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user.data.objectId,
});
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
try {
const requests = Documents.map(x => {
const Signers = x.Signers;
const allSigner = x?.Placeholders?.map(
item =>
Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
).filter(signer => Object.keys(signer).length > 0);
const date = new Date();
const isoDate = date.toISOString();
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
if (allSigner && allSigner.length > 0) {
allSigner.forEach(x => {
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
Acl = { ...Acl, ...obj };
});
}
return {
method: 'POST',
path: '/app/classes/contracts_Document',
body: {
Name: x.Name,
URL: x.URL,
Note: x.Note,
Description: x.Description,
CreatedBy: x.CreatedBy,
SendinOrder: x.SendinOrder || true,
ExtUserPtr: {
__type: 'Pointer',
className: x.ExtUserPtr.className,
objectId: x.ExtUserPtr.objectId,
},
Placeholders: x.Placeholders.map(y =>
y?.signerPtr?.objectId
? {
...y,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.signerPtr.objectId,
},
signerObjId: y.signerObjId,
}
: { ...y, signerPtr: {}, signerObjId: '' }
),
SignedUrl: x.URL || x.SignedUrl,
SentToOthers: true,
Signers: allSigner?.map(y => ({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.objectId,
})),
ACL: Acl,
SentToOthers: true,
RemindOnceInEvery: x.RemindOnceInEvery || 5,
AutomaticReminders: x.AutomaticReminders || false,
TimeToCompleteDays: x.TimeToCompleteDays || 15,
OriginIp: Ip,
DocSentAt: { __type: 'Date', iso: isoDate },
IsEnableOTP: x?.IsEnableOTP || false,
IsTourEnabled: x?.IsTourEnabled || false,
FileAdapterId: x?.FileAdapterId || '',
},
};
});
// console.log('requests ', requests);
if (licenseKey) {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExt.TenantId.objectId,
});
subscription.include('ExtUserPtr');
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (requests?.length <= totalcredits) {
const response = await axios.post('batch', { requests: requests }, parseConfig);
// // Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = Documents.map((x, i) => ({
...x,
objectId: response.data[i]?.success?.objectId,
createdAt: response.data[i]?.success?.createdAt,
}));
deductcount(response.data.length, resExt.id, _resSub);
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], sessionToken);
}
return 'success';
}
} else {
throw new Parse.Error(
429,
'Quota reached, Please buy credits and try again later.'
);
}
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Please purchase or renew your subscription.'
);
}
} else {
if (requests?.length > 0) {
const newrequests = [requests?.[0]];
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
// Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const document = Documents?.[0];
const updateDocuments = {
...document,
objectId: response.data[0]?.success?.objectId,
createdAt: response.data[0]?.success?.createdAt,
};
deductcount(response.data.length, resExt.id);
sendMail(updateDocuments, sessionToken);
return 'success';
}
}
}
} catch (error) {
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
const msg =
error?.response?.data?.error ||
error?.response?.data ||
error?.message ||
'Something went wrong.';
console.log('Error performing batch query:', code, msg);
throw new Parse.Error(code, msg);
if (request?.user) {
return await batchQuery(request.user.id, Documents, Ip, parseConfig);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await batchQuery(userId, Documents, Ip, parseConfig);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
console.log('err in createbatchdoc', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
new Parse.Error(code, msg);
throw new Parse.Error(code, msg);
}
}
@@ -1,10 +1,12 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function getDocument(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId;
const jwttoken = request?.headers?.jwttoken || '';
const sessiontoken = request?.headers?.sessiontoken || '';
try {
if (docId) {
try {
@@ -27,12 +29,12 @@ export default async function getDocument(request) {
if (!IsEnableOTP) {
return document;
} else {
if (request?.headers?.['sessiontoken']) {
if (sessiontoken) {
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
'X-Parse-Session-Token': sessiontoken,
},
});
const userId = userRes.data && userRes.data?.objectId;
@@ -46,6 +48,37 @@ export default async function getDocument(request) {
console.log('err user in not authenticated', err);
return { error: "You don't have access of this document!" };
}
} else if (jwttoken) {
try {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const acl = res.getACL();
if (userId && acl && acl.getReadAccess(userId)) {
return document;
} else {
return { error: "You don't have access of this document!" };
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} catch (err) {
console.log('err in jwt', err);
return { error: "You don't have access of this document!" };
}
} else {
return { error: "You don't have access of this document!" };
}
@@ -8,7 +8,7 @@ export default function getPresignedUrl(url, adapter) {
AWS.config.update({ credentials: credentials, region: adapter?.region || process.env.DO_REGION });
const spacesEndpoint = adapter?.endpoint || new AWS.Endpoint(process.env.DO_ENDPOINT);
const s3 = new AWS.S3({ endpoint: spacesEndpoint });
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: "v4" });
// Create a new URL object
const parsedUrl = new URL(url);
@@ -0,0 +1,65 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function getSigners(request) {
const jwttoken = request.headers.jwttoken || '';
const search = request.params.search || '';
const searchEmail = request.params.searchEmail || '';
try {
if (request.user) {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request?.user?.id,
});
if (search) {
contactbook.matches('Name', new RegExp(search, 'i'));
} else if (searchEmail) {
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
}
contactbook.notEqualTo('IsDeleted', true);
const contactRes = await contactbook.find({ sessionToken: request.user.getSessionToken() });
const _contactRes = JSON.parse(JSON.stringify(contactRes));
return _contactRes;
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
if (search) {
contactbook.matches('Name', new RegExp(search, 'i'));
} else if (searchEmail) {
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
}
contactbook.notEqualTo('IsDeleted', true);
const contactRes = await contactbook.find({ useMasterKey: true });
const _contactRes = JSON.parse(JSON.stringify(contactRes));
return _contactRes;
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
console.log('err in get signers', err);
throw err;
}
}
@@ -1,13 +1,57 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getSubscription(request) {
const extUserId = request.params.extUserId || '';
const contactId = request.params.contactId || '';
const ispublic = request.params.ispublic || false;
const jwttoken = request.headers?.jwttoken || '';
if (extUserId) {
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const verifyToken = jwttoken;
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
if (decoded?.user_email) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('Email', decoded?.user_email);
const exUser = await extCls.first({ useMasterKey: true });
if (exUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: exUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
return { status: 'success', result: _subcripitions };
} else {
return { status: 'success', result: {} };
}
} else {
return { status: 'error', result: 'User not found!' };
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else if (extUserId) {
try {
let userId;
//`ispublic` is used in public profile to get subscription details
@@ -51,13 +95,22 @@ export default async function getSubscription(request) {
} else if (contactId) {
try {
const contactCls = new Parse.Query('contracts_Contactbook');
contactCls.include('CreatedBy');
const contactUser = await contactCls.get(contactId, { useMasterKey: true });
if (contactUser) {
const _contractUser = JSON.parse(JSON.stringify(contactUser));
let TenantId = _contractUser?.TenantId?.objectId || '';
if (!TenantId) {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', _contractUser.CreatedBy.email);
const contactUser = await extUserCls.first({ useMasterKey: true });
TenantId = contactUser?.get('TenantId')?.id || '';
}
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: contactUser.get('TenantId').id,
objectId: TenantId,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
@@ -0,0 +1,83 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function getTenantByUserId(userId, contactId) {
try {
if (contactId) {
const contactquery = new Parse.Query('contracts_Contactbook');
contactquery.equalTo('objectId', contactId);
const contactuser = await contactquery.first({ useMasterKey: true });
if (contactuser) {
const tenantId = contactuser?.get('TenantId')?.id;
if (tenantId) {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('objectId', tenantId);
tenantCreditsQuery.exclude('FileAdapters,PfxFile,ContactNumber');
const res = await tenantCreditsQuery.first({ useMasterKey: true });
return res;
} else {
return {};
}
} else {
return {};
}
} else {
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const extuser = await query.first({ useMasterKey: true });
if (extuser) {
const user = extuser?.get('CreatedBy')?.id || userId;
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: user,
});
tenantCreditsQuery.exclude('FileAdapters,PfxFile');
const res = await tenantCreditsQuery.first({ useMasterKey: true });
return res;
} else {
return {};
}
}
} catch (err) {
console.log('err in getTenant ', err);
return 'user does not exist!';
}
}
export default async function getTenant(request) {
const jwttoken = request.headers.jwttoken || '';
const userId = request.params.userId || '';
const contactId = request.params.contactId || '';
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const verifyToken = jwttoken;
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const apiUserId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: apiUserId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
if (decoded?.user_email) {
return await getTenantByUserId(apiUserId, contactId);
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else if (userId || contactId) {
return await getTenantByUserId(userId, contactId);
} else {
return {};
}
}
@@ -1,5 +1,9 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function getUserDetails(request) {
const reqEmail = request.params.email;
const jwttoken = request?.headers?.jwttoken || '';
if (reqEmail || request.user) {
try {
const userId = request.params.userId;
@@ -36,6 +40,48 @@ async function getUserDetails(request) {
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
try {
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('Email', decoded?.user_email);
userQuery.include('TenantId');
userQuery.include('UserId');
userQuery.include('CreatedBy');
userQuery.exclude('CreatedBy.authData');
userQuery.exclude('TenantId.FileAdapters');
userQuery.exclude('google_refresh_token');
userQuery.exclude('TenantId.PfxFile');
const res = await userQuery.first({ useMasterKey: true });
if (res) {
return res;
} else {
return '';
}
} catch (err) {
console.log('Err ', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
@@ -0,0 +1,47 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function isUserInContactBook(request) {
try {
const jwttoken = request.headers.jwttoken;
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const email = userRes?.get('email');
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', userPtr);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const res = await query.first({ useMasterKey: true });
return res;
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else if (request.user) {
const email = request.user.get('email');
const userPtr = { __type: 'Pointer', className: '_User', objectId: request.user?.id };
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', userPtr);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const res = await query.first({ sessionToken: request.user.getSessionToken() });
return res;
}
} catch (err) {
console.log('err', err);
throw err;
}
}
@@ -458,17 +458,17 @@ export default async function GenerateCertificate(docDetails) {
});
currentPage.drawText('Viewed on :', {
x: half,
x: half + 55,
y: yPosition2,
size: text,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
x: half + 75,
x: half + 112,
y: yPosition2,
size: text,
size: timeText,
font: timesRomanFont,
color: textValueColor,
});
@@ -490,17 +490,17 @@ export default async function GenerateCertificate(docDetails) {
});
currentPage.drawText('Signed on :', {
x: half,
y: yPosition3,
size: text,
x: half + 55,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
x: half + 70,
y: yPosition3,
size: text,
x: half + 108,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
color: textValueColor,
});
@@ -520,22 +520,23 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Security level :', {
x: half,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`Email, OTP Auth`, {
x: half + 90,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textValueColor,
});
if (IsEnableOTP) {
currentPage.drawText('Security level :', {
x: half + 55,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`Email, OTP Auth`, {
x: half + 125,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textValueColor,
});
}
currentPage.drawText('Signature :', {
x: 30,
y: yPosition5,
@@ -102,8 +102,16 @@ async function sendCompletedMail(obj) {
const sender = obj.doc.ExtUserPtr;
const pdfName = doc.Name;
const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const recipient =
doc?.Signers?.length > 0 ? doc?.Signers?.map(x => x?.Email)?.join(',') : sender.Email;
let signersMail;
if (doc?.Signers?.length > 0) {
const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email);
signersMail = isOwnerExistsinSigners
? doc?.Signers?.map(x => x?.Email)?.join(',')
: [...doc?.Signers?.map(x => x?.Email), sender.Email]?.join(',');
} else {
signersMail = sender.Email;
}
const recipient = signersMail;
let subject = `Document "${pdfName}" has been signed by all parties`;
let body =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
@@ -316,13 +324,16 @@ async function PDF(req) {
}
}
const _resDoc = resDoc?.toJSON();
// `FileAdapter` && `ActiveFileAdapter` is used to save file in user's fileAdapter
const ActiveFileAdapter = _resDoc?.ExtUserPtr?.TenantId?.ActiveFileAdapter;
const FileAdapter =
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === ActiveFileAdapter) || {};
// `fileAdapterId` is used check document uploaded in custom file adapter and get customFileAdapter id
const fileAdapterId = _resDoc?.FileAdapterId || '';
let adapterConfig = {};
if (FileAdapter && ActiveFileAdapter) {
adapterConfig = { ActiveFileAdapter: ActiveFileAdapter, ...FileAdapter };
if (fileAdapterId) {
// `FileAdapter` is used to credintials of file adapter
const FileAdapter =
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === fileAdapterId) || {};
if (FileAdapter) {
adapterConfig = FileAdapter;
}
}
let signUser;
let className;
@@ -373,7 +384,8 @@ async function PDF(req) {
isCompleted = true;
}
const randomNumber = Math.floor(Math.random() * 5000);
const docName = _resDoc?.Name?.replace(/\s+/g, '_')?.toLowerCase();
// below regex is used to replace all word with "_" except A to Z, a to z, numbers
const docName = _resDoc?.Name?.replace(/[^a-zA-Z0-9._-]/g, '_')?.toLowerCase();
const name = `signed_${docName}_${randomNumber}.pdf`;
const filePath = `./exports/${name}`;
let pdfSize = PdfBuffer.length;
@@ -0,0 +1,131 @@
import uploadFileToS3 from './uploadFiletoS3.js';
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function saveFile(request) {
const jwttoken = request.headers.jwttoken || '';
if (!request.params.fileBase64) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.');
}
const fileBase64 = request.params.fileBase64;
const id = request.params.id;
try {
if (request.user) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('UserId', request.user);
extCls.include('TenantId');
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
const fileAdapters = _resExt?.TenantId.FileAdapters || [];
const fileAdapter = fileAdapters?.find(x => x.id === id) || {};
if (fileAdapter?.accessKeyId) {
const adapterConfig = {
id: id,
fileAdapter: fileAdapter?.fileAdapter,
bucketName: fileAdapter?.bucketName,
region: fileAdapter?.region,
endpoint: fileAdapter?.endpoint,
accessKeyId: fileAdapter?.accessKeyId,
secretAccessKey: fileAdapter?.secretAccessKey,
baseUrl: fileAdapter?.baseUrl,
};
const buffer = Buffer.from(fileBase64, 'base64');
const fileName = request.params.fileName;
const ext = request.params.fileName?.split('.')?.pop();
let mimeType;
if (ext === 'pdf') {
mimeType = 'application/pdf';
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
mimeType = `image/${ext}`;
}
try {
const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig);
return { url: presignedUrl };
} catch (err) {
console.error('Error generate presigned url:', err);
const msg = 'Fileadapter credentials are invalid.';
throw new Parse.Error(400, msg);
}
} else {
const fileName = request.params.fileName;
const pdfFile = new Parse.File(fileName, { base64: fileBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save({ useMasterKey: true });
const presignedUrl = pdfData.url();
return { url: presignedUrl };
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('Email', decoded?.user_email);
extCls.include('TenantId');
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
const fileAdapters = _resExt?.TenantId.FileAdapters || [];
const fileAdapter = fileAdapters?.find(x => x.id === id) || {};
if (fileAdapter?.accessKeyId) {
const adapterConfig = {
id: id,
fileAdapter: fileAdapter?.fileAdapter,
bucketName: fileAdapter?.bucketName,
region: fileAdapter?.region,
endpoint: fileAdapter?.endpoint,
accessKeyId: fileAdapter?.accessKeyId,
secretAccessKey: fileAdapter?.secretAccessKey,
baseUrl: fileAdapter?.baseUrl,
};
const buffer = Buffer.from(fileBase64, 'base64');
const fileName = request.params.fileName;
const ext = request.params.fileName?.split('.')?.pop();
let mimeType;
if (ext === 'pdf') {
mimeType = 'application/pdf';
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
mimeType = `image/${ext}`;
}
try {
const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig);
return { url: presignedUrl };
} catch (err) {
console.error('Error generate presigned url:', err);
const msg = 'Fileadapter credentials are invalid.';
throw new Parse.Error(400, msg);
}
} else {
const fileName = request.params.fileName;
const pdfFile = new Parse.File(fileName, { base64: fileBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save({ useMasterKey: true });
const presignedUrl = pdfData.url();
return { url: presignedUrl };
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
console.log('err in savetoS3', err);
throw err;
}
}
@@ -0,0 +1,114 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function updateTemplate(template, isJwt = false) {
try {
if (template?.Id) {
const updateTemplate = new Parse.Object('contracts_Template');
updateTemplate.id = template.Id;
if (template?.URL) {
updateTemplate.set('URL', template.URL);
}
if (template?.Name) {
updateTemplate.set('Name', template.Name);
}
if (template?.Note) {
updateTemplate.set('Note', template.Note);
}
if (template?.Description) {
updateTemplate.set('Description', template.Description);
}
if (template?.SendinOrder) {
updateTemplate.set('SendinOrder', template.SendinOrder);
}
if (template?.AutomaticReminders) {
updateTemplate.set('AutomaticReminders', template.AutomaticReminders);
}
if (template?.RemindOnceInEvery) {
updateTemplate.set('RemindOnceInEvery', template.RemindOnceInEvery);
}
if (template?.NextReminderDate) {
updateTemplate.set('NextReminderDate', new Date(template.NextReminderDate));
}
if (template?.IsEnableOTP) {
updateTemplate.set('IsEnableOTP', template.IsEnableOTP);
}
if (template?.IsTourEnabled) {
updateTemplate.set('IsTourEnabled', template.IsTourEnabled);
}
const isPublic = template?.IsPublic !== undefined ? template?.IsPublic : false;
if (template?.IsPublic !== undefined) {
updateTemplate.set('IsPublic', isPublic);
}
updateTemplate.set('Placeholders', template.Placeholders);
updateTemplate.set('Signers', template.Signers);
if (template?.SignatureType?.length > 0) {
updateTemplate.set('SignatureType', template.SignatureType);
}
let updateTemplateRes;
if (isJwt) {
updateTemplateRes = await updateTemplate.save(null, { useMasterKey: true });
} else {
updateTemplateRes = await updateTemplate.save();
}
return updateTemplateRes;
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide Id.');
}
} catch (err) {
console.log('err in update template', err);
throw err;
}
}
export default async function saveTemplate(request) {
const jwttoken = request.headers.jwttoken || '';
const template = {
Id: request.params?.templateId,
URL: request.params?.URL || '',
Name: request.params?.Name,
Note: request.params?.Note,
Description: request.params?.Description,
Placeholders: request.params?.Placeholders,
Signers: request.params?.Signers,
SendMail: request.params?.SendMail || false,
SendinOrder: request.params?.SendinOrder || true,
AutomaticReminders: request.params?.AutomaticReminders,
RemindOnceInEvery: parseInt(request.params.RemindOnceInEvery) || 15,
NextReminderDate: request.params?.NextReminderDate,
IsEnableOTP: request.params?.IsEnableOTP === true ? true : false,
IsTourEnabled: request.params?.IsTourEnabled === true ? true : false,
IsPublic: request.params?.IsPublic,
SignatureType: request.params?.SignatureType || [],
};
try {
if (request.user) {
return await updateTemplate(template);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await updateTemplate(template, true);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
console.log('err in get signers', err);
throw err;
}
}
@@ -0,0 +1,180 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function savecontact(request) {
const name = request.params.name;
const phone = request.params.phone;
const email = request.params.email;
const tenantId = request.params.tenantId;
const jwttoken = request.headers.jwttoken;
if (request.user) {
const currentUser = request?.user;
const currentUserPtr = { __type: 'Pointer', className: '_User', objectId: currentUser?.id };
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', currentUserPtr);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const res = await query.first({ sessionToken: request.user.getSessionToken() });
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('IsDeleted', false);
if (tenantId) {
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: 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) {
contactQuery.set('CreatedBy', currentUserPtr);
contactQuery.set('UserId', user);
const acl = new Parse.ACL();
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, 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));
return parseData;
}
} catch (err) {
console.log('err ', err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params);
contactQuery.set('CreatedBy', currentUserPtr);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = new Parse.ACL();
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, 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));
return parseData;
}
}
} else {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.');
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
const currentUser = userRes;
const currentUserPtr = { __type: 'Pointer', className: '_User', objectId: userId };
if (decoded?.user_email) {
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', currentUserPtr);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const res = await query.first({ useMasterKey: true });
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('IsDeleted', false);
if (tenantId) {
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: 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) {
contactQuery.set('CreatedBy', currentUserPtr);
contactQuery.set('UserId', user);
const acl = new Parse.ACL();
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, 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));
return parseData;
}
} catch (err) {
console.log('err ', err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params);
contactQuery.set('CreatedBy', currentUserPtr);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = new Parse.ACL();
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, 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));
return parseData;
}
}
} else {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
}
}
@@ -37,7 +37,8 @@ const makeEmail = async (to, from, subject, html, url, pdfName) => {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
const isSecure = new URL(url)?.protocol === 'https:';
const isSecure =
new URL(url)?.protocol === 'https:' && new URL(url)?.hostname !== 'localhost';
if (useLocal !== 'true' || isSecure) {
https.get(url, async function (response) {
response.pipe(Pdf);
@@ -18,7 +18,7 @@ async function sendMailProvider(req, plan, monthchange) {
port: process.env.SMTP_PORT || 465,
secure: smtpsecure,
auth: {
user: process.env.SMTP_USER_EMAIL,
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
pass: process.env.SMTP_PASS,
},
});
@@ -33,7 +33,9 @@ async function sendMailProvider(req, plan, monthchange) {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
const isSecure = new URL(req.params.url)?.protocol === 'https:';
const isSecure =
new URL(req.params.url)?.protocol === 'https:' &&
new URL(req.params.url)?.hostname !== 'localhost';
if (useLocal !== 'true' || isSecure) {
https.get(req.params.url, async function (response) {
response.pipe(Pdf);
@@ -65,7 +67,7 @@ async function sendMailProvider(req, plan, monthchange) {
const pdfName = req.params.pdfName && `${req.params.pdfName}.pdf`;
const file = {
filename: pdfName || 'exported.pdf',
content: smtpenable ? PdfBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'),
content: smtpenable ? PdfBuffer : undefined,
data: smtpenable ? undefined : PdfBuffer,
};
@@ -200,7 +202,9 @@ async function sendcustomsmtp(extRes, req) {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
const isSecure = new URL(req.params.url)?.protocol === 'https:';
const isSecure =
new URL(req.params.url)?.protocol === 'https:' &&
new URL(req.params.url)?.hostname !== 'localhost';
if (useLocal !== 'true' || isSecure) {
https.get(req.params.url, async function (response) {
response.pipe(Pdf);
@@ -0,0 +1,73 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function updateTemplate(template, isJwt = false) {
try {
if (template?.Id) {
const updateTemplate = new Parse.Object('contracts_Template');
updateTemplate.id = template.Id;
const isPublic = template?.IsPublic !== undefined ? template?.IsPublic : false;
if (template?.IsPublic !== undefined) {
updateTemplate.set('IsPublic', isPublic);
updateTemplate.set('PublicRole', template.PublicRole);
}
let updateTemplateRes;
if (isJwt) {
updateTemplateRes = await updateTemplate.save(null, { useMasterKey: true });
} else {
updateTemplateRes = await updateTemplate.save();
}
return updateTemplateRes;
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide Id.');
}
} catch (err) {
console.log('err in update template', err);
throw err;
}
}
export default async function updateToPublicTemplate(request) {
const jwttoken = request.headers.jwttoken || '';
const Role = request.params.Role;
if (!Role) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide public role.');
}
const PublicRole = [Role];
const template = {
Id: request.params.templateId,
IsPublic: request.params?.IsPublic,
PublicRole: PublicRole,
};
try {
if (request.user) {
return await updateTemplate(template);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await updateTemplate(template, true);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
console.log('err in get signers', err);
throw err;
}
}
@@ -0,0 +1,58 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function updateTourStatus(request) {
const tourstatus = request.params.TourStatus;
const extUserId = request.params.ExtUserId;
const jwttoken = request?.headers?.jwttoken || '';
if (request.user) {
try {
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUserId;
updateUser.set('TourStatus', tourstatus);
const res = await updateUser.save();
return res;
} catch (err) {
console.log('Err ', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
try {
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUserId;
updateUser.set('TourStatus', tourstatus);
const res = await updateUser.save(null, { useMasterKey: true });
return res;
} catch (err) {
console.log('Err ', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
}
@@ -0,0 +1,52 @@
export default async function updateSignatureType(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const SignatureType = request.params.SignatureType || [];
if (SignatureType.length > 0) {
const enabledSignTypes = SignatureType?.filter(x => x.enabled);
const isDefaultSignTypeOnly =
enabledSignTypes?.length === 1 && enabledSignTypes[0]?.name === 'default';
if (enabledSignTypes.length === 0) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'At least one signature type should be enabled.'
);
} else if (isDefaultSignTypeOnly) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'At least one signature type other than the default should be enabled.'
);
} else {
try {
const orgQuery = new Parse.Query('contracts_Users');
orgQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resUser = await orgQuery.first({ useMasterKey: true });
if (resUser) {
const newOrg = new Parse.Object('contracts_Users');
newOrg.id = resUser.id;
newOrg.set('SignatureType', SignatureType);
const updateUserRes = await newOrg.save(null, { useMasterKey: true });
if (updateUserRes) {
const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes));
return _updateUserRes;
}
} else {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Premission denied.');
}
} catch (err) {
console.log('err in addorganization', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide signature types.');
}
}
@@ -14,6 +14,7 @@ async function uploadFileToS3(buffer, fileName, mimeType, adapter) {
client = new S3Client({
region: adapter?.region,
credentials: { accessKeyId: adapter?.accessKeyId, secretAccessKey: adapter?.secretAccessKey },
signatureVersion: "v4"
});
}
const prefixId = crypto.randomBytes(16).toString('hex');
@@ -0,0 +1,43 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const docSchema = new Parse.Schema('contracts_Document');
docSchema.addArray('SignatureType');
await docSchema.update();
const templateSchema = new Parse.Schema('contracts_Template');
templateSchema.addArray('SignatureType');
await templateSchema.update();
const extUserSchema = new Parse.Schema('contracts_Users');
extUserSchema.addArray('SignatureType');
await extUserSchema.update();
const tenantSchema = new Parse.Schema('partners_Tenant');
tenantSchema.addArray('SignatureType');
return tenantSchema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const docSchema = new Parse.Schema('contracts_Document');
docSchema.deleteField('SignatureType');
await docSchema.update();
const templateSchema = new Parse.Schema('contracts_Template');
docSchema.deleteField('SignatureType');
await templateSchema.update();
const extUserSchema = new Parse.Schema('contracts_Users');
docSchema.deleteField('SignatureType');
await extUserSchema.update();
const tenantSchema = new Parse.Schema('partners_Tenant');
docSchema.deleteField('SignatureType');
return tenantSchema.update();
};
+1 -1
View File
@@ -62,7 +62,7 @@ if (smtpenable) {
port: process.env.SMTP_PORT || 465,
secure: smtpsecure,
auth: {
user: process.env.SMTP_USER_EMAIL,
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
pass: process.env.SMTP_PASS,
},
});
+553 -444
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -18,10 +18,10 @@
"watch": "nodemon index.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.670.0",
"@aws-sdk/s3-request-presigner": "^3.670.0",
"@aws-sdk/client-s3": "^3.685.0",
"@aws-sdk/s3-request-presigner": "^3.688.0",
"@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^2.2.0",
"@parse/s3-files-adapter": "^3.0.0",
"@pdf-lib/fontkit": "^1.1.1",
"@signpdf/placeholder-pdf-lib": "^3.2.4",
"@signpdf/signer-p12": "^3.2.4",
@@ -35,25 +35,25 @@
"generate-api-key": "^1.0.2",
"googleapis": "^144.0.0",
"mailgun.js": "^10.2.3",
"mongoose": "^8.7.1",
"mongoose": "^8.8.0",
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"node-forge": "^1.3.1",
"nodemailer": "^6.9.15",
"nodemailer": "^6.9.16",
"parse": "^5.3.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^7.3.0",
"parse-server-api-mail-adapter": "^4.0.0",
"parse-server-api-mail-adapter": "^4.1.0",
"pdf-lib": "^1.17.1",
"posthog-node": "^4.2.1",
"ws": "^8.18.0"
},
"type": "module",
"devDependencies": {
"@babel/eslint-parser": "^7.25.8",
"eslint": "^9.12.0",
"@babel/eslint-parser": "^7.25.9",
"eslint": "^9.14.0",
"jasmine": "^5.4.0",
"mongodb-runner": "^5.7.0",
"mongodb-runner": "^5.7.1",
"nodemon": "^3.1.7",
"nyc": "^17.1.0",
"prettier": "^3.3.3"