Compare commits

...
304 Commits
Author SHA1 Message Date
AndrewandGitHub 39472d6874 Merge pull request #1197 from OpenSignLabs/validation 2024-09-11 12:26:18 +05:30
prafull-opensignlabs 6207e99cc0 fix: pagination issue 2024-09-11 12:23:09 +05:30
prafull-opensignlabsandGitHub bdc163199d Merge pull request #1196 from OpenSignLabs/raktima-patch-3
fix: after submitting the request sign form a blank page appears on mobile view
2024-09-11 11:06:13 +05:30
RaktimaNXG e7a04ede65 fix: after submitting the request sign form a blank page appears on mobile view 2024-09-11 10:52:18 +05:30
prafull-opensignlabsandGitHub f6aee4148d Merge pull request #1194 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/npm_and_yarn-7a84200127
build(deps): bump express from 4.19.2 to 4.20.0 in /apps/OpenSignServer in the npm_and_yarn group
2024-09-11 10:51:13 +05:30
dependabot[bot]andGitHub 34fcfc8f30 build(deps): bump express
Bumps the npm_and_yarn group in /apps/OpenSignServer with 1 update: [express](https://github.com/expressjs/express).


Updates `express` from 4.19.2 to 4.20.0
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.20.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 04:58:43 +00:00
prafull-opensignlabsandGitHub af9af347e6 Merge pull request #1192 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/npm_and_yarn-7ee3b15cf5
build(deps-dev): bump postcss from 8.4.44 to 8.4.45 in /apps/OpenSign in the npm_and_yarn group
2024-09-11 10:27:04 +05:30
dependabot[bot]andGitHub 6f46279b52 build(deps-dev): bump postcss
Bumps the npm_and_yarn group in /apps/OpenSign with 1 update: [postcss](https://github.com/postcss/postcss).


Updates `postcss` from 8.4.44 to 8.4.45
- [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.44...8.4.45)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 12:53:18 +00:00
AndrewandGitHub 8f93ed8b8e Merge pull request #1191 from OpenSignLabs/validation 2024-09-10 16:41:59 +05:30
prafull-opensignlabs df91363ff4 fix: pagination issue 2024-09-10 16:41:08 +05:30
AndrewandGitHub e40e59714d Merge pull request #1190 from OpenSignLabs/validation 2024-09-10 13:43:28 +05:30
prafull-opensignlabs a43d95afc3 refactor: validation 2024-09-10 13:42:35 +05:30
AndrewandGitHub 8fa4b5ab22 Merge pull request #1189 from OpenSignLabs/validation 2024-09-09 15:46:44 +05:30
prafull-opensignlabs 68a030145f fix: handle invalid session token error while logging out 2024-09-09 15:44:01 +05:30
AndrewandGitHub 4f48657623 Merge pull request #1175 from OpenSignLabs/validation 2024-09-09 10:19:08 +05:30
prafull-opensignlabs 79b05b4a66 refactor: handle tab in menu 2024-09-06 20:10:56 +05:30
AndrewandGitHub fb9e37e71c Merge pull request #1173 from OpenSignLabs/validation 2024-09-06 19:37:54 +05:30
prafull-opensignlabs cd7a741d53 fix: size of tour 2024-09-06 19:36:27 +05:30
AmolandGitHub 5fdc90b4f9 Merge pull request #1172 from OpenSignLabs/validation
fix: quick send not working
2024-09-06 18:51:45 +05:30
prafull-opensignlabs e775840abb fix: quicksend not working 2024-09-06 18:50:15 +05:30
prafull-opensignlabs d7df54dfa1 fix: quick send not working 2024-09-06 18:45:35 +05:30
AndrewandGitHub 646d165d37 Merge pull request #1171 from OpenSignLabs/validation 2024-09-06 18:04:14 +05:30
prafull-opensignlabs ba0a80cca4 refactor: text change 2024-09-06 18:01:47 +05:30
AndrewandGitHub 9d59858542 Merge pull request #1170 from OpenSignLabs/validation 2024-09-06 17:59:59 +05:30
prafull-opensignlabs aaff0a5697 refactor: text translation 2024-09-06 17:58:18 +05:30
AndrewandGitHub 1bb7cee238 Merge pull request #1169 from OpenSignLabs/validation 2024-09-06 17:46:22 +05:30
prafull-opensignlabs d330e662d3 refactor: correct keys in translation files 2024-09-06 17:14:53 +05:30
prafull-opensignlabs b530ff76c4 refactor: translate text 2024-09-06 16:41:56 +05:30
AndrewandGitHub e59a166445 Merge pull request #1168 from OpenSignLabs/validation 2024-09-06 14:48:47 +05:30
prafull-opensignlabs 980e713735 refactor: validation 2024-09-06 14:47:57 +05:30
prafull-opensignlabsandGitHub 7a5228bbab Merge pull request #1167 from OpenSignLabs/validation
refactor: validation
2024-09-06 12:00:26 +05:30
prafull-opensignlabs 5a26e5c2ee refactor: validaiton 2024-09-06 11:56:54 +05:30
prafull-opensignlabsandGitHub c2dccf17ce Merge pull request #1166 from OpenSignLabs/validation
fix: email count not update until re-login
2024-09-05 19:21:06 +05:30
prafull-opensignlabs 181d6bf1e4 fix: email count not update until re-login 2024-09-05 19:19:15 +05:30
prafull-opensignlabsandGitHub a07be3b5dc Merge pull request #1165 from OpenSignLabs/validation
feat: show email used count
2024-09-05 18:38:23 +05:30
prafull-opensignlabs af2666c83d feat: show email used count 2024-09-05 18:36:30 +05:30
prafull-opensignlabsandGitHub cad9929b56 Merge pull request #1164 from OpenSignLabs/raktima-patch-3
fix: display incorrect count of document on dashboard
2024-09-05 18:35:48 +05:30
RaktimaNXG 3b79da527d fix: display incorrect count of document on dashboard 2024-09-05 18:32:42 +05:30
AndrewandGitHub 3eaba55823 Merge pull request #1163 from OpenSignLabs/validation 2024-09-05 17:00:57 +05:30
prafull-opensignlabs e2eddb6362 refactor: validation 2024-09-05 16:59:47 +05:30
prafull-opensignlabsandGitHub d885570dd8 Merge pull request #1162 from OpenSignLabs/raktima-patch-2
fix: drop widgets in center of document in mobile view
2024-09-05 10:10:27 +05:30
RaktimaNXG f3de883e39 fix: increase close button size of react tour in mobile view 2024-09-05 09:59:07 +05:30
RaktimaNXG 6da22d23a8 fix: remove console 2024-09-04 19:41:39 +05:30
RaktimaNXG 49461a43bb fix: drop widgets in center of document in mobile view 2024-09-04 19:39:10 +05:30
prafull-opensignlabsandGitHub d4414f99af Merge pull request #1161 from OpenSignLabs/raktima-patch-2
fix: resolve issue to allow edits options on completed documents in the SignYourself flow
2024-09-04 18:52:06 +05:30
RaktimaNXG 52f37b0a37 fix: resolve issue to allow edits options on completed documents in the SignYourself flow 2024-09-04 18:31:19 +05:30
prafull-opensignlabsandGitHub ce949e4029 Merge pull request #1160 from OpenSignLabs/raktima-patch-2
fix: Issue with changing signers list order in report view button
2024-09-04 17:32:09 +05:30
RaktimaNXG 19d3465a13 add comment 2024-09-04 16:03:13 +05:30
RaktimaNXG a0e1425c74 fix: handle condition to send document for signing 2024-09-04 15:58:50 +05:30
prafull-opensignlabsandGitHub a5d8379b18 Merge pull request #1159 from OpenSignLabs/raktima-patch-3
fix: issue while rename document/folder in opensign-drive
2024-09-04 15:44:15 +05:30
RaktimaNXG 9ac487ea5b fix: unexpected view button of signers for signyourself document in report 2024-09-04 15:41:25 +05:30
RaktimaNXG 9cc9a93c3d fix: handle error 2024-09-04 15:13:10 +05:30
RaktimaNXG 4f2b0313de refactor code 2024-09-04 15:09:38 +05:30
RaktimaNXG 806894b24b fix : issue while rename the document/folder in opensign-drive 2024-09-04 14:48:49 +05:30
RaktimaNXG cbb737f96b Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-patch-2 2024-09-04 11:53:22 +05:30
prafull-opensignlabsandGitHub 5407f8156a Merge pull request #1157 from OpenSignLabs/raktima-patch-3
refactor: change order in decline modal
2024-09-04 10:08:47 +05:30
RaktimaNXG a0c2fd09ae fix: resolve issue of disappear widgets when adding it on multiple pages 2024-09-03 21:05:56 +05:30
RaktimaNXG 48b744c8ba refactor code 2024-09-03 20:59:41 +05:30
prafull-opensignlabsandGitHub 812b946d85 Merge pull request #1156 from OpenSignLabs/raktima-patch-3
feat: display the email of the user who declined the document along with the decline reason in the document decline popup.
2024-09-03 20:47:59 +05:30
RaktimaNXG 27eca2b470 fix: add error iin alert 2024-09-03 20:43:45 +05:30
RaktimaNXG ab6c88bebd feat: save declined by user details in-progress report 2024-09-03 20:23:54 +05:30
RaktimaNXG d547e0552a fix: handle error 2024-09-03 20:11:14 +05:30
RaktimaNXG 03fc0b84cb fix: remove console 2024-09-03 19:53:43 +05:30
RaktimaNXG b0825634ee resolve conflict 2024-09-03 19:50:42 +05:30
RaktimaNXG 9675e051bd feat: display the email of the user who declined the document along with the decline reason in the document decline popup. 2024-09-03 19:48:21 +05:30
AndrewandGitHub 981654a2aa Merge pull request #1155 from OpenSignLabs/validation 2024-09-03 19:32:05 +05:30
prafull-opensignlabs 8c97ea0da0 fix: duplicate entry issue 2024-09-03 19:30:51 +05:30
prafull-opensignlabsandGitHub 6c80911c90 Merge pull request #1154 from OpenSignLabs/rotatepage_document
feat: introduce a guide video in profile page for setting up a public template and managing the public profile
2024-09-03 16:57:38 +05:30
RaktimaNXG 740b5ea719 fix: placeholder disappear issue in template flow 2024-09-03 15:49:37 +05:30
RaktimaNXG 76c958fb7a fix: remove unnecessary variable from locales en and fr file 2024-09-03 13:54:08 +05:30
RaktimaNXG 28c53fd115 fix: update text of demo video 2024-09-03 13:31:19 +05:30
RaktimaNXG 6d9fc8b3df fix: resolve error 2024-09-03 13:13:49 +05:30
RaktimaNXG a044f4eceb refactor code 2024-09-03 12:33:23 +05:30
RaktimaNXG 1f35efcd51 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into rotatepage_document 2024-09-03 12:27:37 +05:30
RaktimaNXG c587abddae fix: on edit button show upgrade now validation issue in profile page 2024-09-03 12:26:49 +05:30
prafull-opensignlabsandGitHub d7f4c9a329 Merge pull request #1153 from OpenSignLabs/validation
build(deps): update dependencies
2024-09-03 12:17:00 +05:30
prafull-opensignlabs 8a3e6f5781 build(deps): update dependencies 2024-09-03 12:15:06 +05:30
RaktimaNXG cfbebc4989 refactor code 2024-09-02 21:10:15 +05:30
RaktimaNXG 99ec772d64 feat: embed demo video to create public profile 2024-09-02 21:01:44 +05:30
RaktimaNXG 1f60b0bbcb Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-patch-2 2024-09-02 20:03:47 +05:30
RaktimaNXG c8fb9a5b09 fix: signers list order change issue in reports 2024-09-02 20:01:18 +05:30
AndrewandGitHub d6f117c7bf Merge pull request #1152 from OpenSignLabs/validation
fix: failed to load pdf issue on self-host with docker
2024-09-02 19:34:05 +05:30
prafull-opensignlabs a765668354 fix: failed to load pdf issue on self-host with docker 2024-09-02 19:31:38 +05:30
prafull-opensignlabs da053658e8 refactor: handle uncuaght err in send mail 2024-09-02 19:29:36 +05:30
prafull-opensignlabsandGitHub cb5bcd72e4 Merge pull request #1151 from OpenSignLabs/validation
refactor: add logout btn
2024-09-02 13:07:43 +05:30
prafull-opensignlabs d4d53623ef refactor: add logout btn 2024-09-02 13:03:47 +05:30
RaktimaNXG 8a31b008d2 fix: ui design of mobile view on public script load 2024-09-02 12:55:53 +05:30
prafull-opensignlabsandGitHub fa4541b59f Merge pull request #1150 from OpenSignLabs/validation
fix: get is not function
2024-09-02 11:14:08 +05:30
prafull-opensignlabs aae6b32828 fix: get is not function 2024-09-02 11:09:23 +05:30
RaktimaNXG 7158d6735b resolve conflict 2024-09-02 09:51:26 +05:30
AmolandGitHub f4b896d5e6 Merge pull request #1141 from OpenSignLabs/validation 2024-09-01 18:45:59 +05:30
prafull-opensignlabs d2f814e6f8 refactor: prepare for v2.3.0 2024-09-01 18:45:07 +05:30
prafull-opensignlabs 87cec67e60 refactor: add help test, remove uncessary code 2024-09-01 17:51:56 +05:30
AmolandGitHub 041c46f38e Merge pull request #1140 from OpenSignLabs/validation 2024-08-31 23:30:23 +05:30
prafull-opensignlabs 54a7628f75 fix: quick send not working 2024-08-31 23:28:39 +05:30
AmolandGitHub 7cbbc16390 Merge pull request #1139 from OpenSignLabs/validation 2024-08-31 23:02:51 +05:30
prafull-opensignlabs 14532bf6db refactor: change help text 2024-08-31 22:59:45 +05:30
AmolandGitHub 7bfe18e718 Merge pull request #1137 from OpenSignLabs/validation 2024-08-30 20:04:58 +05:30
prafull-opensignlabs 07903a07a8 refactor: add help text for test token 2024-08-30 19:53:17 +05:30
RaktimaNXG 14fe3b2b98 fix: resolve conflict 2024-08-30 17:55:27 +05:30
AndrewandGitHub fa2756ff3f Merge pull request #1136 from OpenSignLabs/validation 2024-08-30 17:50:58 +05:30
prafull-opensignlabs 9d4841b4dc feat: add api testing token 2024-08-30 17:47:40 +05:30
RaktimaNXG 4b74b08825 fix: update tour messages of show public profile setup 2024-08-30 17:44:03 +05:30
RaktimaNXG 6457cfc958 refactor code 2024-08-30 12:02:59 +05:30
RaktimaNXG 30081c893b fix: tagline input box design 2024-08-30 11:59:34 +05:30
RaktimaNXG e69890a47e feat: add tour message in profile page 2024-08-30 11:42:11 +05:30
prafull-opensignlabsandGitHub 9d31162eec Merge pull request #1093 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/posthog-node-4.1.1
build(deps): bump posthog-node from 4.0.1 to 4.1.1 in /apps/OpenSignServer
2024-08-29 14:18:44 +05:30
prafull-opensignlabsandGitHub 0031cc7418 Merge pull request #1094 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/eslint-9.9.1
build(deps-dev): bump eslint from 9.9.0 to 9.9.1 in /apps/OpenSignServer
2024-08-29 10:57:32 +05:30
prafull-opensignlabsandGitHub 57132892b5 Merge pull request #1130 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/npm_and_yarn-271b7fd07e
build(deps): bump the npm_and_yarn group in /apps/OpenSign with 2 updates
2024-08-29 10:56:19 +05:30
prafull-opensignlabsandGitHub be084d1570 Merge pull request #1129 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/npm_and_yarn-07a849e1ae
build(deps): bump micromatch from 4.0.7 to 4.0.8 in /apps/OpenSignServer in the npm_and_yarn group
2024-08-29 10:55:41 +05:30
prafull-opensignlabsandGitHub 03d29a9ebd Merge pull request #1096 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/mongoose-8.5.4
build(deps): bump mongoose from 8.5.3 to 8.5.4 in /apps/OpenSignServer
2024-08-29 10:49:14 +05:30
dependabot[bot]andGitHub e9a837b534 build(deps): bump the npm_and_yarn group
Bumps the npm_and_yarn group in /apps/OpenSign with 2 updates: [micromatch](https://github.com/micromatch/micromatch) and [webpack](https://github.com/webpack/webpack).


Updates `micromatch` from 4.0.7 to 4.0.8
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8)

Updates `webpack` from 5.93.0 to 5.94.0
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.93.0...v5.94.0)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-29 05:18:15 +00:00
prafull-opensignlabsandGitHub 36c601f9fa Merge pull request #1095 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSignServer/axios-1.7.5
build(deps): bump axios from 1.7.4 to 1.7.5 in /apps/OpenSignServer
2024-08-29 10:48:06 +05:30
dependabot[bot]andGitHub af41234484 build(deps): bump micromatch
Bumps the npm_and_yarn group in /apps/OpenSignServer with 1 update: [micromatch](https://github.com/micromatch/micromatch).


Updates `micromatch` from 4.0.7 to 4.0.8
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.7...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-29 05:17:14 +00:00
prafull-opensignlabsandGitHub bcaf03a770 Merge pull request #1091 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/axios-1.7.5
build(deps): bump axios from 1.7.4 to 1.7.5 in /apps/OpenSign
2024-08-29 10:45:59 +05:30
prafull-opensignlabsandGitHub 9429b009ce Merge pull request #1120 from OpenSignLabs/rotatepage_document
fix:  handle conditions and throw errors for public template script loading
2024-08-29 10:44:55 +05:30
RaktimaNXG 3395785cbc refactor code 2024-08-28 17:12:17 +05:30
RaktimaNXG 97c048c7af fix: remove share option from public templateId 2024-08-28 17:07:22 +05:30
RaktimaNXG 726eb2ffe6 fix: templateid share option in mobile view 2024-08-28 11:54:55 +05:30
RaktimaNXG a9f22d3a10 fix: share templatId issue 2024-08-28 11:08:15 +05:30
prafull-opensignlabsandGitHub 5a6437e371 Merge pull request #1116 from OpenSignLabs/validation
fix: all contacts are not visible in request sign form's signers inputbox
2024-08-28 10:59:29 +05:30
prafull-opensignlabs 4ffc98b371 fix: all contacts are not visible in request sign form's signers inputbox 2024-08-28 10:54:56 +05:30
RaktimaNXG 16c73d4f91 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into rotatepage_document 2024-08-27 17:22:53 +05:30
RaktimaNXG 96aff5230f fix: handle condition to load public-template script without templatId 2024-08-27 17:16:21 +05:30
prafull-opensignlabsandGitHub b298571be8 Merge pull request #1106 from OpenSignLabs/validation
fix: api token not visible
2024-08-27 13:26:09 +05:30
prafull-opensignlabs 7b7b10c9a5 fix: api token visible 2024-08-27 13:22:48 +05:30
dependabot[bot]andGitHub 56beed5ad5 build(deps): bump mongoose from 8.5.3 to 8.5.4 in /apps/OpenSignServer
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.5.3 to 8.5.4.
- [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.5.3...8.5.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:37:01 +00:00
dependabot[bot]andGitHub 8c8ef0ea53 build(deps): bump axios from 1.7.4 to 1.7.5 in /apps/OpenSignServer
Bumps [axios](https://github.com/axios/axios) from 1.7.4 to 1.7.5.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.4...v1.7.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:36:48 +00:00
dependabot[bot]andGitHub 90377d98c0 build(deps-dev): bump eslint from 9.9.0 to 9.9.1 in /apps/OpenSignServer
Bumps [eslint](https://github.com/eslint/eslint) from 9.9.0 to 9.9.1.
- [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.9.0...v9.9.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:36:40 +00:00
dependabot[bot]andGitHub d167e474d8 build(deps): bump posthog-node in /apps/OpenSignServer
Bumps [posthog-node](https://github.com/PostHog/posthog-js-lite/tree/HEAD/posthog-node) from 4.0.1 to 4.1.1.
- [Release notes](https://github.com/PostHog/posthog-js-lite/releases)
- [Changelog](https://github.com/PostHog/posthog-js-lite/blob/main/posthog-node/CHANGELOG.md)
- [Commits](https://github.com/PostHog/posthog-js-lite/commits/posthog-node-v4.1.1/posthog-node)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:36:32 +00:00
dependabot[bot]andGitHub 0cfacfe057 build(deps): bump axios from 1.7.4 to 1.7.5 in /apps/OpenSign
Bumps [axios](https://github.com/axios/axios) from 1.7.4 to 1.7.5.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.4...v1.7.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 00:32:26 +00:00
RaktimaandGitHub fbda5231d0 Merge pull request #1089 from OpenSignLabs/validation
build(deps): update dependencies
2024-08-23 17:36:40 +05:30
prafull-opensignlabs bd4d333d59 build(deps): update dependencies 2024-08-23 17:32:53 +05:30
prafull-opensignlabs 07878207de fix: validation of api token 2024-08-23 17:06:53 +05:30
RaktimaandGitHub b2352877b9 Merge pull request #1088 from OpenSignLabs/validation
feat: add download btn in expired document
2024-08-23 13:08:36 +05:30
prafull-opensignlabs 669ba9e8cb feat: add download btn in expired document 2024-08-23 12:40:38 +05:30
AmolandGitHub 5fa8a76219 Merge pull request #1087 from OpenSignLabs/validation 2024-08-22 21:07:40 +05:30
prafull-opensignlabs 476dcf051c feat: implement autosave feature in signyourself 2024-08-22 20:28:49 +05:30
prafull-opensignlabsandGitHub db96dbba6a Merge pull request #1086 from OpenSignLabs/validation
feat: autosave document and template while drafting
2024-08-22 17:38:04 +05:30
prafull-opensignlabs e9c6509970 feat: autosave document and template while drafting 2024-08-22 17:23:53 +05:30
prafull-opensignlabsandGitHub b26c7dfa8f Merge pull request #1085 from OpenSignLabs/rotatepage_document
fix: resolve daisy-ui theme issue for public-sign script loading
2024-08-22 15:10:37 +05:30
RaktimaNXG 3f251761d6 fix: resolve daisy-ui theme issue for public sign script loading 2024-08-22 14:53:45 +05:30
prafull-opensignlabsandGitHub bc9d489ee8 Merge pull request #1084 from OpenSignLabs/validation
fix: wrong alert is visible after loader in quicksend popup
2024-08-22 13:03:10 +05:30
prafull-opensignlabs 271563ba9f refactor: correct variable 2024-08-22 13:01:17 +05:30
prafull-opensignlabs 82aba20e83 refactor: remove unnecessary code from add user 2024-08-22 12:56:18 +05:30
prafull-opensignlabs 12f60c9be5 fix: wrong alert is visible after loader in quicksend popup 2024-08-22 12:49:48 +05:30
AndrewandGitHub 1c98a354c7 Merge pull request #1082 from OpenSignLabs/validation 2024-08-21 18:53:30 +05:30
prafull-opensignlabs 9ca7aee859 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-21 18:14:05 +05:30
prafull-opensignlabs 937b306128 fix: remove unncessary query from buyaddonusers 2024-08-21 18:10:57 +05:30
prafull-opensignlabsandGitHub 69a2ab0ce2 Merge pull request #1079 from OpenSignLabs/rotatepage_document
feat: implement PDF page rotation feature
2024-08-21 17:16:58 +05:30
RaktimaNXG b720711217 refactor code 2024-08-21 16:20:25 +05:30
prafull-opensignlabs bf56348c62 fix: add users count 2024-08-21 14:39:36 +05:30
RaktimaNXG f993817cb6 fix: issue of onclick yes button rotate pdf page 2024-08-21 14:36:01 +05:30
RaktimaNXG 1c2b4794f3 fix: add copy option eside to widget for signature,stamp and text widgets 2024-08-21 13:55:54 +05:30
prafull-opensignlabs 523d210503 fix: quicksend validation 2024-08-21 12:57:08 +05:30
RaktimaNXG 39da8bd636 refactor code 2024-08-21 10:02:46 +05:30
RaktimaNXG fbea98bbe6 fix: add validation of rotation functionality, add rotation functionality for mobile view 2024-08-20 20:41:14 +05:30
prafull-opensignlabs 5d89f724f4 feat: validtion of quicksend 2024-08-20 15:56:00 +05:30
prafull-opensignlabsandGitHub 0313b6f3ab Merge pull request #1077 from OpenSignLabs/raktima-patch-1
fix: owner's subscription expired alert issue during sign document
2024-08-20 12:27:29 +05:30
RaktimaNXG fe92111dc9 fix: owner's subscription expired alert issue during sign document 2024-08-20 12:08:55 +05:30
prafull-opensignlabs 9a03bfb9e7 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-19 12:10:27 +05:30
prafull-opensignlabs 882db6acc9 fix: validation of generate token 2024-08-19 11:28:22 +05:30
RaktimaNXG 5596468b03 refactor code 2024-08-16 18:06:52 +05:30
RaktimaNXG eb66de1220 fix: issue in template flow using roate pdf page functionality 2024-08-16 18:02:29 +05:30
RaktimaNXG f97896f6a6 fix: translate rotate icon title 2024-08-16 17:26:14 +05:30
RaktimaNXG cfb47f880e fix: translate rotate icon title 2024-08-16 17:25:58 +05:30
RaktimaNXG eba87776a0 fix: resolve conflict 2024-08-16 17:14:30 +05:30
RaktimaNXG dda0e6d676 fix: handle pdfRequest sign flow rotate pdf page not allowed 2024-08-16 17:11:51 +05:30
prafull-opensignlabsandGitHub 8d514644c1 Merge pull request #1066 from OpenSignLabs/raktima-opensignlabs-patch-12
feat: add customizable font and color options for Date, Dropdown, Checkbox, and Radio Button widgets.
2024-08-16 15:07:14 +05:30
prafull-opensignlabsandGitHub 0870a34391 Merge pull request #1065 from OpenSignLabs/header_strip
feat: add a header strip to display an announcement about the public profile feature launch
2024-08-16 15:00:21 +05:30
RaktimaNXG 38118986a3 feat: add a header strip to display an announcement about the public profile feature launch 2024-08-16 14:13:51 +05:30
RaktimaNXG 1b4e5247ad feat: implement PDF page rotation feature 2024-08-16 14:10:29 +05:30
AmolandGitHub 3ef2eeef93 Merge pull request #1063 from OpenSignLabs/validation 2024-08-14 20:24:25 +05:30
prafull-opensignlabs 4d5682e79b fix: add user is not working 2024-08-14 20:09:19 +05:30
RaktimaNXG 24100d203f fix: setting icon space issue on widgets 2024-08-14 20:07:14 +05:30
RaktimaNXG 86946a2c21 fix: date setting modal design in mobile view 2024-08-14 20:01:11 +05:30
RaktimaNXG 54f3e4cc6f fix: remove comment from signPdf function 2024-08-14 19:34:55 +05:30
RaktimaNXG d9360b2d8c add comment 2024-08-14 19:29:19 +05:30
RaktimaNXG abb47118c0 fix: dropdown font size issue using pdf-lib 2024-08-14 19:24:23 +05:30
RaktimaNXG 9e4e55bb8d Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-12 2024-08-14 16:41:30 +05:30
AmolandGitHub 498f720507 Merge pull request #1058 from OpenSignLabs/validation 2024-08-14 16:36:05 +05:30
RaktimaNXG b9bc9cb75e Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-12 2024-08-14 14:34:40 +05:30
RaktimaNXG 3b65917422 fix: checkbox options position 2024-08-14 14:18:32 +05:30
prafull-opensignlabs 862dd78cd5 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-14 12:21:35 +05:30
prafull-opensignlabsandGitHub d0bc48d119 Merge pull request #1054 from OpenSignLabs/deps
build(deps): update dependencies
2024-08-14 11:49:27 +05:30
prafull-opensignlabs 37462ba282 build(deps): update dependencies 2024-08-14 11:46:22 +05:30
prafull-opensignlabs 99e7f5862a build(deps): update dependencies 2024-08-14 11:46:14 +05:30
RaktimaNXG 048e529bb5 fix: checkbox & radio button labels font color issue 2024-08-14 11:41:18 +05:30
AmolandGitHub 5b8b06f3a1 Merge pull request #1051 from OpenSignLabs/prafull-opensignlabs-patch-28 2024-08-13 19:27:42 +05:30
prafull-opensignlabsandGitHub 3c7d025802 refactor: Update env 2024-08-13 19:26:00 +05:30
prafull-opensignlabsandGitHub 4f8b46da14 Merge pull request #1049 from OpenSignLabs/prafull-opensignlabs-patch-27
refactor: Update env
2024-08-13 19:20:25 +05:30
prafull-opensignlabsandGitHub 0b386a882e refactor: Update env 2024-08-13 19:17:28 +05:30
RaktimaNXG 6329d58a67 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-12 2024-08-13 17:00:15 +05:30
prafull-opensignlabsandGitHub 0444e17848 Merge pull request #1047 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: something went wrong issue while signing document in public profile
2024-08-13 16:36:01 +05:30
RaktimaNXG c3d039fe45 fix: style of copy alert 2024-08-13 16:24:20 +05:30
RaktimaNXG 1d5d713854 refactor code 2024-08-13 16:07:37 +05:30
RaktimaNXG 4336c15ca4 resolve conflict 2024-08-13 15:58:17 +05:30
RaktimaNXG aac644d15c Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-13 15:55:52 +05:30
RaktimaNXG ab3dde17c7 remove times.ttf file to cdn file 2024-08-13 15:48:54 +05:30
AmolandGitHub 924ec33d73 docs: Update README.md with docker installation instructions 2024-08-13 15:37:31 +05:30
RaktimaNXG d798e02126 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-13 15:27:34 +05:30
RaktimaNXG af0d780ac0 fix: add customiize font size and color in checkbox, radio button,dropdown widgets 2024-08-13 15:25:06 +05:30
prafull-opensignlabs 45c2c863ef Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-13 15:13:43 +05:30
prafull-opensignlabsandGitHub bc08aeb5b7 Merge pull request #1046 from OpenSignLabs/fix_widgets
Fix: issue of unnecessary alert is occurred in request sign flow
2024-08-13 15:05:36 +05:30
AmolandGitHub c32067890c Merge pull request #1043 from OpenSignLabs/docker_beta
Feat: official docker images
2024-08-13 15:03:45 +05:30
prafull-opensignlabs 83a180e3b8 Fix: issue of unccesary alert is occured in request sign flow 2024-08-13 15:03:04 +05:30
prafull-opensignlabs 3372e16409 Fix: issue of unccesary alert is occured in request sign flow 2024-08-13 15:02:26 +05:30
prafull-opensignlabs a04fad9c1f Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-13 13:32:42 +05:30
prafull-opensignlabsandGitHub 7500797dd0 Merge pull request #1045 from OpenSignLabs/fix_widgets
Fix: special characters are not allowed in dropdown widget
2024-08-13 13:09:44 +05:30
prafull-opensignlabs 9427f4141f Fix: special characters are not allowed in dropdown 2024-08-13 13:07:33 +05:30
prafull-opensignlabsandGitHub 1440244c4d Merge pull request #1044 from OpenSignLabs/fix_reactquill
Fix: security issue of react-quill dependencies
2024-08-13 12:07:47 +05:30
prafull-opensignlabs 47c8b82cfa fix: remove warning of react-quill 2024-08-13 11:56:02 +05:30
prafull-opensignlabs 540f23fb41 fix: security issue of react-quill dependencies 2024-08-13 11:40:54 +05:30
RaktimaNXG 323c9f2d4f fix: add copy & share button for user's public profile url in profile page 2024-08-13 11:00:55 +05:30
prafull-opensignlabsandGitHub 1d648b525f Merge pull request #1035 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/react-i18next-15.0.1
build(deps): bump react-i18next from 15.0.0 to 15.0.1 in /apps/OpenSign
2024-08-13 10:16:11 +05:30
prafull-opensignlabsandGitHub 11c2771451 Merge pull request #1036 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/lottiefiles/dotlottie-react-0.8.7
build(deps): bump @lottiefiles/dotlottie-react from 0.8.6 to 0.8.7 in /apps/OpenSign
2024-08-13 10:15:22 +05:30
prafull-opensignlabsandGitHub 1733756b4a Merge pull request #1037 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/axios-1.7.3
build(deps): bump axios from 1.7.2 to 1.7.3 in /apps/OpenSign
2024-08-13 10:14:51 +05:30
prafull-opensignlabsandGitHub edf1933800 Merge pull request #1038 from OpenSignLabs/dependabot/npm_and_yarn/apps/OpenSign/tailwindcss-3.4.9
build(deps-dev): bump tailwindcss from 3.4.7 to 3.4.9 in /apps/OpenSign
2024-08-13 10:14:28 +05:30
prafull-opensignlabs 38d3165d14 refactor: update branch name 2024-08-12 19:35:12 +05:30
AmolandGitHub 5766b307c1 Merge pull request #1042 from OpenSignLabs/fix_widgets 2024-08-12 19:12:23 +05:30
prafull-opensignlabs 6497703d3b fix: signature is blurly in certificate 2024-08-12 19:10:17 +05:30
AmolandGitHub 60e48580ae Merge pull request #1041 from OpenSignLabs/raktima-opensignlabs-patch-11 2024-08-12 18:05:37 +05:30
RaktimaNXG 08b52f3618 fix: multi-language support issue in public profile 2024-08-12 18:02:32 +05:30
dependabot[bot]andGitHub 55001cc434 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.8.6 to 0.8.7.
- [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/@lottiefiles/dotlottie-react@0.8.7/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-08-12 12:30:33 +00:00
RaktimaandGitHub b84b864396 Merge pull request #1040 from OpenSignLabs/fix_widgets
fix: Hiding the text fields widget if entering the Special Characters
2024-08-12 17:59:09 +05:30
prafull-opensignlabsandGitHub 34835b8cdf Merge pull request #1039 from OpenSignLabs/file_upload
Fix: file upload is not working
2024-08-12 14:14:24 +05:30
prafull-opensignlabs df8bdbf5b3 fix: Hiding the text fields widget if entering the Special Characters 2024-08-12 14:10:41 +05:30
prafull-opensignlabs e775a832cb fix: file upload is not working 2024-08-12 13:08:41 +05:30
dependabot[bot]andGitHub b9c4bada23 build(deps-dev): bump tailwindcss from 3.4.7 to 3.4.9 in /apps/OpenSign
Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 3.4.7 to 3.4.9.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/v3.4.9/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v3.4.7...v3.4.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 00:32:13 +00:00
dependabot[bot]andGitHub 97f35f7ee6 build(deps): bump axios from 1.7.2 to 1.7.3 in /apps/OpenSign
Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 00:31:59 +00:00
dependabot[bot]andGitHub 0f90a06028 build(deps): bump react-i18next from 15.0.0 to 15.0.1 in /apps/OpenSign
Bumps [react-i18next](https://github.com/i18next/react-i18next) from 15.0.0 to 15.0.1.
- [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/react-i18next/compare/v15.0.0...v15.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 00:31:28 +00:00
AndrewandGitHub db4f2e7696 Merge pull request #1032 from OpenSignLabs/raktima-opensignlabs-patch-11 2024-08-10 16:45:50 +05:30
RaktimaNXG 69837b0515 fix: language support issue in public-profile 2024-08-10 16:23:10 +05:30
AndrewandGitHub 9811c49288 Merge pull request #1031 from OpenSignLabs/gmail_login 2024-08-09 20:25:35 +05:30
prafull-opensignlabs d709c99641 Fix: update translation message 2024-08-09 20:16:51 +05:30
prafull-opensignlabs a92666082f Fix: showing the wrong alert when updating the team. 2024-08-09 20:14:11 +05:30
AndrewandGitHub cc05b28a42 Merge pull request #1029 from OpenSignLabs/gmail_login 2024-08-09 20:00:59 +05:30
prafull-opensignlabsandGitHub a2c9953096 Merge pull request #1028 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: issue with empty emails when using customized mail in send-request flow
2024-08-09 19:59:49 +05:30
prafull-opensignlabs 75ef6934b4 fix: gmail login not working 2024-08-09 19:58:29 +05:30
RaktimaNXG aa4d3c039b fix: issue with empty emails when using customized mail in send-request flow 2024-08-09 18:41:28 +05:30
prafull-opensignlabs a8a89dc9bd Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-09 18:03:34 +05:30
prafull-opensignlabs a0305a745a fix: addon not updated correctly 2024-08-09 18:03:14 +05:30
prafull-opensignlabsandGitHub 290ff54638 Merge pull request #1027 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: unable to enter email issue in add signers form
2024-08-09 18:01:19 +05:30
RaktimaNXG dc4decffe4 fix: unable to enter email issue in add signer form 2024-08-09 17:57:53 +05:30
prafull-opensignlabs f06b702360 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-09 16:38:29 +05:30
prafull-opensignlabsandGitHub e2eca66db0 Merge pull request #1024 from OpenSignLabs/raktima-opensignlabs-patch-11
fix: error in auto-sign feature unexpected issue encountered - something went wrong
2024-08-09 16:29:24 +05:30
RaktimaNXG d20913023e fix: email condition issue in adduser file 2024-08-09 16:25:22 +05:30
RaktimaNXG f4466dc325 fix: resolve public url 2024-08-09 15:41:35 +05:30
RaktimaNXG 00e6ea5b3b fix: resolve warning 2024-08-09 15:31:57 +05:30
RaktimaNXG 50b0a3ea17 fix: resolve issue of support multi-language in public script file 2024-08-09 15:03:56 +05:30
RaktimaNXG 0072e3e5ba refactor code 2024-08-09 13:43:39 +05:30
prafull-opensignlabs 6f104c4ee6 fix: langauage support in add user 2024-08-09 13:05:16 +05:30
prafull-opensignlabs c721580516 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-09 11:23:10 +05:30
RaktimaNXG 2238f1fafc Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-09 10:30:41 +05:30
prafull-opensignlabs 00bc5a00c1 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into docker_beta 2024-08-09 10:17:56 +05:30
prafull-opensignlabs 1fe1381fe2 refactor: user valdation 2024-08-08 20:40:13 +05:30
prafull-opensignlabsandGitHub 6bc0470c69 Merge pull request #1026 from OpenSignLabs/prafull-opensignlabs-patch-26
fix: not able to update company
2024-08-08 20:01:06 +05:30
prafull-opensignlabsandGitHub a9ab25e9c2 fix: not able to update company 2024-08-08 19:57:01 +05:30
prafull-opensignlabsandGitHub fd5bc22a70 Merge pull request #1025 from OpenSignLabs/prafull-opensignlabs-patch-26
fix: not able update user profile
2024-08-08 19:29:36 +05:30
prafull-opensignlabsandGitHub 28fe1fafd6 fix: not able update user profile 2024-08-08 19:14:56 +05:30
RaktimaNXG 6695138244 fix: tour message translation issue of templates report 2024-08-08 17:58:20 +05:30
RaktimaNXG 3fb19cbe96 fix: failed to load message issue while sign document in signyour-self flow 2024-08-08 17:36:08 +05:30
RaktimaNXG 3f8a17c3ea fix: contactbook report title to name in heading 2024-08-08 16:31:26 +05:30
RaktimaNXG 9fa97f27f0 fix: error in auto-sign feature unexpected issue encountered - something went wrong 2024-08-08 15:55:11 +05:30
RaktimaNXG 0e98312d55 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-08 15:25:56 +05:30
AmolandGitHub 06057a1d35 Merge pull request #1023 from OpenSignLabs/prafull-opensignlabs-patch-25
fix: error while calling webhook
2024-08-08 15:01:04 +05:30
prafull-opensignlabsandGitHub 4373fddaa8 fix: Error while calling webhook 2024-08-08 14:58:38 +05:30
AmolandGitHub c45511d9a5 Merge pull request #1019 from OpenSignLabs/raktima-opensignlabs-patch-11
feat: Integrate language support for multiple languages in the application
2024-08-08 14:36:20 +05:30
RaktimaNXG 7d2b2494e5 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-08 13:23:18 +05:30
prafull-opensignlabs 870a25dad1 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into validation 2024-08-08 13:02:39 +05:30
RaktimaNXG d74dc2f4ca fix:improve readability by updating some French words 2024-08-08 12:54:05 +05:30
prafull-opensignlabs e4e5d5c6d7 Revert "refactor: change url"
This reverts commit 7e45f23b6c.
2024-08-08 12:11:18 +05:30
prafull-opensignlabs 7e45f23b6c refactor: change url 2024-08-08 12:03:46 +05:30
prafull-opensignlabs 719f61f612 refactor: change url 2024-08-08 12:00:07 +05:30
prafull-opensignlabs 908edda14b feat: validation to add user 2024-08-07 20:02:41 +05:30
RaktimaNXG 74792a9c21 refactor code 2024-08-07 16:08:02 +05:30
RaktimaNXG eb345b5ca2 fix: after filled any input value still show warning of empty field 2024-08-07 15:50:54 +05:30
RaktimaNXG 23c2b4b204 fix: remove console 2024-08-07 14:51:43 +05:30
RaktimaNXG 095e616e4c fix: refactor code 2024-08-07 14:47:37 +05:30
RaktimaNXG 89f4626fce checking error 2024-08-07 14:43:15 +05:30
RaktimaNXG c759d673f0 refactor code 2024-08-07 14:03:45 +05:30
RaktimaNXG 8d1be554d4 fix: input validation warning translation issue 2024-08-07 14:00:52 +05:30
RaktimaNXG 841c7df2f3 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into raktima-opensignlabs-patch-11 2024-08-06 20:25:56 +05:30
RaktimaNXG dd85ce7cfe fix: after create new user selected language change in en-USA 2024-08-06 20:25:26 +05:30
RaktimaNXG 4c7d771bb3 fix: apply language support in profile also and save in database, issue of text translate in guest login flow and other place 2024-08-06 17:02:16 +05:30
prafull-opensignlabs d6330060dc refactor: moved from yml to env 2024-08-05 21:16:13 +05:30
prafull-opensignlabs 2dcb35a34c fix: docker compose file depends on env file 2024-08-05 17:09:44 +05:30
prafull-opensignlabs 624ba95792 fix: connection refused while sending mail 2024-08-05 14:31:55 +05:30
prafull-opensignlabs ae24a80b95 fix: url variable in sendmail gmail 2024-08-05 13:12:52 +05:30
prafull-opensignlabs e7f1b1cc32 fix: vairable intiailzation error 2024-08-05 12:55:33 +05:30
prafull-opensignlabs e71940ecdc fix:add env in docker compose, save tenantcredit and datafiles is not working 2024-08-05 12:43:44 +05:30
RaktimaNXG 10083ac807 fix: detect from browser's language and set it in dropdown by default 2024-08-02 17:50:06 +05:30
prafull-opensignlabs 11023c6400 Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into docker_beta 2024-08-02 17:24:00 +05:30
RaktimaNXG 6d31314df6 fix: implement feature to detect lagunage from browser, resolve conflict 2024-08-02 17:22:25 +05:30
prafull-opensignlabs d5367194ab Merge branch 'docker_beta' of https://github.com/OpenSignLabs/OpenSign into docker_beta 2024-08-02 17:21:09 +05:30
prafull-opensignlabs cf179b2f9c fix: change docker images in docker compose file 2024-08-02 17:20:46 +05:30
AmolandGitHub 8c06c98917 build: Update Makefile 2024-08-02 17:00:52 +05:30
prafull-opensignlabs d4e0f05cef feat: update official docker images 2024-08-02 12:46:01 +05:30
RaktimaNXG dfcb4fe286 refactor-code 2024-08-01 21:05:00 +05:30
RaktimaNXG d546d5d0a7 feat: apply multisupport language for all type alert, buttons and tour-messages 2024-08-01 20:57:22 +05:30
RaktimaNXG c6e93cd3c8 feat: apply multisupport language for signing flow in signyour-self, template , placeholder 2024-07-31 21:26:08 +05:30
RaktimaNXG 779d5df264 feat: appply multi-support language functionality on report, managesign, webhook,api token 2024-07-30 20:02:32 +05:30
RaktimaNXG 77cd52ca04 feat: appply multi-support language functionality on login,signup-page,sidebar menu 2024-07-30 14:09:10 +05:30
RaktimaNXG bed2a77232 feat: implement feature of multisupport language 2024-07-29 20:32:59 +05:30
153 changed files with 69377 additions and 25501 deletions
+3 -3
View File
@@ -3,11 +3,11 @@
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
# Set it to the URL form where the app home page will be accessed
PUBLIC_URL=http://localhost:3000
PUBLIC_URL=https://localhost:3001
# Set it to true if you want to generate the Sourcemap for debugging
GENERATE_SOURCEMAP=false
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
REACT_APP_SERVERURL=http://localhost:8080/app
# REACT_APP_SERVERURL=http://localhost:8080/app
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
REACT_APP_APPID=opensign
@@ -46,7 +46,7 @@ USE_LOCAL=true
MAILGUN_API_KEY=
MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com
SMTP_ENABLE=
SMTP_ENABLE=true
SMTP_HOST=smtp.yourhost.com
SMTP_PORT=443
SMTP_USER_EMAIL=mailer@yourdomain.com
+1 -1
View File
@@ -2,7 +2,7 @@ name: ci
on:
push:
branches:
- 'staging'
- 'main'
jobs:
docker:
+7
View File
@@ -0,0 +1,7 @@
{$HOST_URL} {
reverse_proxy client:3000
handle_path /app/* {
reverse_proxy server:8080
rewrite * /app{uri}
}
}
+4 -2
View File
@@ -1,8 +1,10 @@
build:
@echo "Building with HOST_URL=${HOST_URL}"
cp .env.local_dev .env
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
docker compose up --build --force-recreate
HOST_URL=${HOST_URL} docker compose up --build --force-recreate
run:
@echo "Building with HOST_URL=${HOST_URL}"
cp .env.local_dev .env
docker compose up -d
docker compose up -d
+7 -1
View File
@@ -75,7 +75,13 @@ Welcome to OpenSign, the premier open source docusign alternative - document e-s
### Installation
Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructions on how to install OpenSign on your system.
The simplest way to install OpenSign on your own server is using official docker images by running the following command -
```
export HOST_URL=https://opensign.yourdomain.com && curl --remote-name-all https://raw.githubusercontent.com/OpenSignLabs/OpenSign/docker_beta/docker-compose.yml https://raw.githubusercontent.com/OpenSignLabs/OpenSign/docker_beta/Caddyfile https://raw.githubusercontent.com/OpenSignLabs/OpenSign/docker_beta/.env.local_dev && mv .env.local_dev .env.prod && docker compose up --force-recreate
```
Make sure that you have `Docker` and `git` installed before you run this command -
Please refer to the [Installation Guide](https://docs.opensignlabs.com/docs/self-host/docker/run-locally/) for detailed instructions on how to install OpenSign on your system.
---
+493 -603
View File
File diff suppressed because it is too large Load Diff
+17 -12
View File
@@ -4,12 +4,16 @@
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.8.6",
"@radix-ui/themes": "^3.1.1",
"@lottiefiles/dotlottie-react": "^0.8.9",
"@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.3",
"@react-pdf/renderer": "^3.4.4",
"@reduxjs/toolkit": "^2.2.7",
"axios": "^1.7.2",
"axios": "^1.7.5",
"file-saver": "^2.0.5",
"i18next": "^23.14.0",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.6.1",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
"nth-check": "^2.1.1",
@@ -29,12 +33,13 @@
"react-dom": "^18.2.0",
"react-gtm-module": "^2.0.11",
"react-helmet": "^6.1.0",
"react-i18next": "^15.0.1",
"react-konva": "^18.2.10",
"react-pdf": "^9.1.0",
"react-quill": "^2.0.0",
"react-quill-new": "^3.3.0",
"react-redux": "^9.1.2",
"react-rnd": "^10.4.11",
"react-router-dom": "^6.26.0",
"react-router-dom": "^6.26.1",
"react-scripts": "5.0.1",
"react-scrollbars-custom": "^4.1.1",
"react-select": "^5.8.0",
@@ -47,7 +52,7 @@
"regex-parser": "^2.3.0",
"serve": "^14.2.3",
"styled-components": "^5.3.0",
"web-vitals": "^4.2.2",
"web-vitals": "^4.2.3",
"ws": "^8.18.0"
},
"scripts": {
@@ -89,10 +94,10 @@
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@babel/runtime-corejs2": "^7.25.0",
"autoprefixer": "^10.4.19",
"@babel/runtime-corejs2": "^7.25.4",
"autoprefixer": "^10.4.20",
"babel-loader": "^9.1.3",
"clean-webpack-plugin": "^4.0.0",
"commitizen": "^4.3.0",
@@ -105,11 +110,11 @@
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.34.3",
"lint-staged": "^15.2.8",
"mini-css-extract-plugin": "^2.9.0",
"postcss": "^8.4.41",
"mini-css-extract-plugin": "^2.9.1",
"postcss": "^8.4.45",
"prettier": "^3.3.3",
"pretty-quick": "^4.0.0",
"tailwindcss": "^3.4.7",
"tailwindcss": "^3.4.10",
"terser-webpack-plugin": "^5.3.10",
"webpack-cli": "^5.1.4"
},
@@ -0,0 +1,641 @@
{
"header-news": "New feature: Public profiles are now live! Easily create and share public templates",
"header-news-btn": "Setup Now",
"create-account": "Create Account",
"login": "Login",
"language": "Language",
"name": "Name",
"phone": "Phone",
"phone-optional": "optional",
"email": "Email",
"company": "Company",
"job-title": "Job title",
"profile": "Profile",
"log-out": "Log Out",
"is-email-verified": "Is Email verified",
"public-profile": "Public profile",
"tagline": "Tagline ",
"has-requested-you-to-sign": "has requested you to sign",
"has-requested-you-to-review-and-sign": " has requested you to review and sign",
"input-required": "Please fill out this field.",
"sort-order": {
"Ascending": "Ascending",
"Descending": "Descending",
"Name": "Name",
"Date": "Date"
},
"context-menu": {
"Download": "Download",
"Rename": "Rename",
"Move": "Move",
"Delete": "Delete"
},
"action": "Action",
"add": "Add",
"add-new-folder": "Add a new folder",
"disable-documentId": "Disable DocumentId",
"edit": "Edit",
"change-password": "Change Password",
"save": "Save",
"cancel": "Cancel",
"upgrade-now": "Upgrade now",
"upgrade-to": "Upgrade to",
"plan": "Plan",
"subscribe-card-teamplan":"Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
"user-name-limit-char": "To have a username less than 8 character please subscribe",
"tour-content": "Don't show this again",
"pro": "PRO",
"team": "TEAM",
"user": "User",
"docs": "Docs",
"session-expired": "Your session has expired.",
"public-profile-help": "This is your public URL. Copy or share it with the signer, and you will be able to see all your publicly set templates.",
"tagline-help": "This Tagline will appear on your OpenSign public profile for ex. https://opensign.me/alex",
"welcome": "Welcome Back!",
"Login-to-your-account": "Login to your account",
"password": "Password",
"forgot-password": "Forgot Password?",
"loading": "Loading...",
"of": "of",
"sign-SSO": "Sign in with SSO",
"login-page": "Login Page",
"or": "OR",
"signup-page": "Signup page",
"password-length": "Password should be 8 characters long",
"password-case": "Password should contain uppercase letter, lowercase letter, digit",
"password-special-char": " Password should contain special character",
"agreee": "I agree to the",
"term": "Terms of Service",
"subscribe-to-opensign": "Subscribe to OpenSign newsletter",
"register": "Register",
"sidebar": {
"Dashboard": "Dashboard",
"Sign yourself": "Sign yourself",
"Request signatures": "Request signatures",
"Templates": "Templates",
"Templates-Children": {
"Create template": "Create template",
"Manage templates": "Manage templates"
},
"OpenSign™ Drive": "OpenSign™ Drive",
"Reports": "Reports",
"Reports-Children": {
"Need your sign": "Need your sign",
"In Progress": "In Progress",
"Completed": "Completed",
"Drafts": "Drafts",
"Declined": "Declined",
"Expired": "Expired",
"Contactbook": "Contactbook"
},
"Settings": "Settings",
"Settings-Children": {
"My Signature": "My Signature",
"API Token": "API Token",
"Webhook": "Webhook",
"Teams": "Teams",
"Team-update-successfully": "Team Updated successfully.",
"Users": "Users"
}
},
"dashboard-card": {
"Need your Signature": "Need your Signature",
"Out for signatures": "Out for Signatures"
},
"report-name": {
"Draft Documents": "Draft Documents",
"Need your sign": "Need your sign",
"In-progress documents": "In-progress documents",
"Completed Documents": "Completed Documents",
"Declined Documents": "Declined Documents",
"Expired Documents": "Expired Documents",
"Recently sent for signatures": "Recently sent for signatures",
"Recent signature requests": "Recent signature requests",
"Drafts": "Drafts",
"Contactbook": "Contactbook",
"Templates": "Templates",
"Teams": "Teams",
"Users": "Users"
},
"btnLabel": {
"sign": "sign",
"Sign": "SIGN",
"Resend": "Resend",
"Revoke": "Revoke",
"Delete": "Delete",
"Use": "Use",
"Quick send": "Quick send",
"Edit": "Edit",
"Share with team": "Share with team",
"Share": "Share",
"View": "View",
"option": "Option",
"Embed": "Embed"
},
"report-heading": {
"Sr.No": "Sr.No",
"Title": "Title",
"Email": "Email",
"Phone": "Phone",
"File": "File",
"Owner": "Owner",
"Signers": "Signers",
"Note": "Note",
"Folder": "Folder",
"Reason": "Reason",
"Parent Team": "Parent Team",
"Active": "Active",
"Role": "Role",
"Team": "Team",
"Name": "Name",
"Status": "Status",
"created-date": "Created Date",
"Type": "Type"
},
"report-help": {
"Draft Documents": "These are documents you have started but have not finalized for sending.",
"Need your sign": "This is a list of documents that are waiting for your signature",
"In-progress documents": "This is a list of documents you've sent to other parties for signature.",
"Completed Documents": "This is a list of documents that have been signed by all parties.",
"Declined Documents": "This is a list of documents that have been declined by one of the signers.",
"Expired Documents": "This is a list of documents that have reached their expiration date.",
"Contactbook": "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document.",
"Templates": "This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
},
"form-name": {
"Sign Yourself": "Sign Yourself",
"Request Signatures": "Request Signatures",
"New Template": "New Template"
},
"file-type": "pdf, png, jpg, jpeg",
"docx": "docx",
"file-selected": "file selected",
"template-title": "Template Title",
"document-title": "Document Title",
"description": "Description",
"time-to-complete": "Time To Complete (Days)",
"send-in-order": "Send In Order",
"send-in-order-help": {
"p1": "Choose how you want the signing requests to be sent to the document signers:",
"p2": "Selecting this option will send the signing request to the first signer initially. Once the first signer completes their part, the next signer in the sequence will receive the request. This process continues until all signers have signed the document. This method ensures that the document is signed in a specific order.",
"p3": "Selecting this option will send the signing links to all signers simultaneously. Every signer can sign the document at their convenience, regardless of whether other signers have completed their signatures. This method is faster but does not enforce any signing order among the participants.",
"p4": "Select the option that best suits the needs of your document processing."
},
"no": "No",
"auto-reminder": "Auto reminder",
"remind-once": " Remind once in every (Days)",
"next": "Next",
"select-folder": "Select Folder",
"OpenSign-drive": "OpenSign™ Drive",
"drive-document-status": {
"Completed": "Completed",
"Declined": "Declined",
"Draft": "Draft",
"Expired": "Expired",
"In Progress": "In Progress"
},
"select-folder-help": "If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder.",
"no-data": "No data found",
"save-here": "Save Here",
"back": "Back",
"add-folder": "Add folder",
"create-folder": "Create Folder",
"parent-folder": "Parent Folder",
"create": "Create",
"signers": "Signers",
"signers-help": "Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed.",
"add-signer": "Add Signer",
"contact-not-found": "Contact not found",
"add-yourself": "Add Yourself",
"submit": "Submit",
"reset": "Reset",
"my-signature": "My Signature",
"signature": "Signature",
"upload-image": " Upload image",
"clear": "Clear",
"upload-signature/Image": "Please upload signature/Image",
"initials": "Initials",
"API": "API",
"api-token": "API Token",
"regenerate-token": "Regenerate Live Token",
"generate-token": "Generate Live Token",
"view-docs": "View Docs",
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
"yes": "Yes",
"copied": "Copied",
"something-went-wrong-mssg": "Something went wrong, please try again later.",
"token-generated": "Token generated successfully.",
"webhook": "Webhook",
"update-webhook": "Update Webhook",
"add-webhook": "Add Webhook",
"webhook-added": "Webhook added successfully.",
"team-disabled": "Team disabled",
"Team enabled": "Team enabled",
"are-you-sure": "Are you sure you want to",
"disable": "disable",
"enable": "enable",
"this-team": " this team?",
"edit-team": "Edit Team",
"name-of-team": "Name of Team",
"prev": "Prev",
"no-data-avaliable": "No Data Available",
"add-team": "Add Team",
"page-not-found": "Page Not Found",
"users-from-teams": "users from Teams",
"user-status": "User status",
"activate": "activate",
"deactivate": "deactivate",
"this-user": " this user",
"delete-user": "Delete User",
"delete": "delete",
"add-user": "Add User",
"password-generateed": "Password will only be generated once; make sure to copy it.",
"Team status": "Team status",
"user-deactivated": "User deactivated.",
"user-activated": "User activated.",
"public": "Public",
"download": "Download",
"view": "View",
"make-template-public": "Make template public",
"make-template-private": "Make template private",
"make-template-public-alert": "Are you sure you want to make this template public?",
"make-template-private-alert": "Are you sure you want to make this template private? This will remove it from your public profile.",
"public-role": "Public role",
"public-url": "Public URL",
"public-url-copy-mssg": " Heres your public URL: Copy it or share it with the signer, and you will be able to see all your publicly set templates.",
"add-public-url-alert": "Please add your public URL, and you will be able to make a public template.",
"share-with-alert": "You cannot share a template if any roles already have contacts assigned. Please remove all contact assignments from the roles before sharing the template.",
"share-with": "Share with",
"team-not-found": "Team not found",
"roles": "Roles",
"delete-document": "Delete Document",
"delete-document-alert": "Are you sure you want to delete this document?",
"quick-send": "Quick send",
"add-new": "Add new",
"send": "Send",
"quick-send-alert-1": "All roles in this document are currently linked to contacts. To quick send copies of this template to multiple signers, please ensure that at least one role is not linked to any contact.",
"quick-send-alert-2": "Please ensure there's at least one signature widget added for all recipients.",
"quick-send-alert-3": "Please add at least one role to this template in order to 'quick send' copies of it to multiple signers.",
"quick-send-alert-4": "Quick send reached limit.",
"copy-link": "Copy link",
"copy": "Copy",
"revoke-document": "Revoke document",
"revoke-document-alert": "Are you sure you want to revoke this document?",
"resend-mail": "Resend Mail",
"resend-mail-help": "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Subject",
"body": "Body",
"add-contact": "Add Contact",
"add-signer-alert": "Contact already exist!",
"record-delete-alert": "Record deleted successfully!",
"record-revoke-alert": "Record revoked successfully!",
"mail-sent-alert": "Mail sent successfully.",
"document-sent-alert": "Document sent successfully!",
"template-share-alert": "Template Shared successfully.",
"template-public-alert-1": "You have successfully made the template public.",
"template-public-alert-2": "You have successfully made the template private.",
"template-public-alert-3": "You need to select a role for the public signers.",
"template-public-alert-4": "Please ensure there's at least one signature widget added for all signers.",
"template-public-alert-5": "Since this template has send-in-order enabled, the public role must be positioned at the top.",
"template-public-alert-6": "Please attach signers to all roles except for the public role user.",
"template-public-alert-7": "Please assign at least one public role to make this template public.",
"loader": " This might take some time",
"pages": "Pages",
"document-signed": "Document signed",
"close": "Close",
"validation-alert": "Validation alert",
"validate-alert-mssg": " The input does not meet the criteria set by the regular expression.",
"otp-verification": "OTP verification",
"enter-otp": "Enter OTP",
"verify": "Verify",
"verify-email-1": "Verify email",
"resend": "Resend",
"contact-details": "Contact Details",
"verify-email": "Please verify your email !",
"send-otp": " Send OTP",
"otp-placeholder": "Enter verification code received over email",
"loading-doc": "Loading Document..",
"widgets-name": {
"signature": "signature",
"stamp": "stamp",
"initials": "initials",
"name": "name",
"job title": "job title",
"company": "company",
"date": "date",
"text": "text",
"text input": "text input",
"checkbox": "checkbox",
"dropdown": "dropdown",
"radio button": "radio button",
"image": "image",
"email": "email"
},
"fields": "Fields",
"recipients": "Recipients",
"please-add": "Please add a",
"signed-by": "Signed By",
"more": "more",
"sent": "sent",
"print": "Print",
"certificate": "Certificate",
"decline": "Decline",
"finish": "Finish",
"mail": "Mail",
"sign-now": "Sign Now",
"successfully-signed": "Successfully signed!",
"email-mssg": " Recipients added here will get a copy of the signed document.",
"email-error-1": " please provide correct email address",
"email-error-2": "You can only send to ten recipients at a time.",
"Email-verified-alert-1": "Email is verified.",
"Email-verified-alert-2": "Email is already verified.",
"upload-stamp-image": "Upload stamp image",
"draw": "Draw",
"type": "Type",
"color-type": {
"red": "red",
"blue": "blue",
"black": "black",
"yellow": "yellow"
},
"my-initials": "My Initials",
"upload": "Upload",
"initial-teb": "Initials",
"signature-tab": "Signature",
"your-signature": "Your signature",
"copy-title": "Copy widget to",
"contact-delete-alert": "Are you sure you want to delete this contact?",
"reset-password-alert-1": "Reset password link has been sent to your email id",
"reset-password-alert-2": "Please setup email adapter ",
"reset-password-alert-3": "Reset Your Password",
"faild-animation": "Failed to load animation",
"apply": "Apply",
"copy-type": {
"All pages": "All pages",
"All pages but last": "All pages but last",
"All pages but first": "All pages but first",
"Next to current widget": "Next to current widget"
},
"options": "Options",
"minimun-check": "Minimun check",
"maximum-check": "Maximum check",
"default-value": "Default value",
"select": "Select",
"read-only": "Is read only",
"hide-labels": "Hide labels",
"checkbox": "Checkbox",
"alert": "Alert",
"zoom-in": "Zoom in",
"zoom-out": "Zoom out",
"document-signed-alert": "Congratulations! 🎉 This document has been successfully signed by you!",
"otp-sent-alert": "OTP sent on your email",
"fields-required": "Fields required",
"signature-widget-alert-1": "Please ensure there's at least one signature widget added",
"signature-widget-alert-2": "Please ensure all field is accurately filled and meets all requirements.",
"encrypted-pdf-alert": "Currently encrypted pdf files are not supported.",
"encrypted-pdf-alert-1": "PFX file password is invalid.",
"user-not-exist": "User does not exist",
"template-signature-field": "Please add at least one signature field for all roles.",
"template-role-alert": "Please add roles first",
"create-document": "Create Document",
"template-created-alert": "Do you want to create a document using the template you just created ?",
"signers-alert": "signers alert",
"template-creation-alert-1": "Please select signer for add placeholder!",
"ok": "Ok",
"radio-group": "Radio group",
"dropdown-options": "Dropdown options",
"add-role": "Add Role",
"role-ex": " e.g: Customer, Hr, Director, Manager, Student, etc",
"add/choose-signer": "Add/Choose Signer",
"choose-from-contacts": "Choose from contacts",
"select-signer": " Please select signer",
"edit-template": "Edit Template",
"widget-info": "Widget info",
"validation": "Validation",
"invalid-default-value": "invalid default value",
"hint": "Hint",
"color": "color",
"role-help": {
"p1": "What are template roles?",
"p2": "Begin by specifying each role needed for the completion of the document. Think about the parties involved in the signing process and what their responsibilities are. Common roles include HR for internal documents, Customer for agreements or Vendor for business agreements.",
"p3": "Why pre-attach users to some roles?",
"p4": "For roles that consistently involve the same individual (e.g., the CEO's signature on employee offer letters), you can pre-attach a user to a role within the template. This step is optional but recommended for efficiency and consistency across documents.",
"p5": "When do i specify the user attached to each role?",
"p6": "When you create a document from your template, you'll be prompted to attach users to each defined role. If a role already has a user attached, this will be pre-filled, but you can modify it as needed before sending out the document."
},
"add-recipients": "Add recipients",
"loading-mssg": "This might take some time",
"send-mail": "Send Mail",
"placeholder-alert-1": "Please ensure there's at least one signature widget added for all recipients.",
"placeholder-alert-2": "Please confirm that you have filled the text field.",
"placeholder-alert-3": " Are you sure you want to send out this document for signatures?",
"placeholder-alert-4": "You have successfully sent mails to all recipients!",
"placeholder-alert-5": "Do you want to sign documents right now ?",
"placeholder-alert-6": "Please setup mail adapter to send mail!",
"placeholder-alert-7": "Please select signer for add placeholder!",
"email-subject": "email subject",
"email-body": "email body",
"email-placeholder": "add body of email",
"reset-to-default": "Reset to default",
"cutomize-email": "Cutomize Email",
"upgrade-to-customize-email": "Upgrade to customize Email",
"sign-url": "Sign url",
"Mails Sent": "Mails Sent",
"document-alert": "Document Alert",
"owner-subscription-expired": "Owner's subscription has expired.",
"subscription-expired": "Subscription Expired",
"alert-message": "Alert message",
"document-decline": "Document decline",
"decline-alert-1": "Are you sure want to decline this document ?",
"decline-alert-2": "You have declined this document!",
"decline-alert-3": "You can not sign this document as it has been declined/revoked.",
"sign here": "Sign here",
"guest-email-alert": "verification code is sent to your email",
"get-otp-alert": "you will get a verification code via Email",
"user-already-exist": "User already exist.",
"server-error": "Internal server error !",
"fill-required-details!": "Please fill required details!",
"sign-up-form": "Sign up form",
"sign-up": "Sign up",
"folder-already-exist!": "folder already exist!",
"please-select-position!": "please select position!",
"default-sign-alert": "Are you sure you want to auto sign at requested all locations?",
"Your-Signature": "Your Signature",
"auto-sign-all": "Auto Sign All",
"generating-certificate": "Generating certificate",
"pdf-download": "PDF Download",
"generate-certificate-alert": " Your completion certificate is being generated. Please wait momentarily. If the download doesn't start shortly, click the button again.",
"folder-already-exist": "Folder already exist!",
"folder-created-successfully": "Folder created successfully!",
"fill-folder-name": "Please fill folder name",
"already-exists-this-username": "User already exists with this username!",
"verification-code-sent": "Verification mail has been sent to your E-mail!",
"registered-user-successfully": "Registered user successfully.",
"role-not-found": "Role not found.",
"do-not-access": "You don't have access, please contact the admin.",
"add-admin": "Add admin",
"opensign-setup": "OpenSign Setup",
"join-discord": "Join our discord server",
"admin-already-exist": "Admin already exist.",
"password-update-alert-1": "Password updated successfully.",
"password-update-alert-2": "Your current password is missing or incorrect.",
"password-update-alert-3": "Your current password is missing or incorrect.",
"password-update-alert-4": "Your password and confirmation password do not match.",
"current-password": "Current Password",
"new-password": "New Password",
"confirm-password": "Confirm Password",
"file-alert-1": "The selected file size is too large. Please select a file less than",
"file-alert-2": "Please select file.",
"file-alert-3": "Please wait while the document is being uploaded.",
"enter-pdf-password": "Enter Pdf Password",
"correct-password": "Please provide correct password",
"decrypting-pdf": " Decrypting pdf please wait...",
"invalid-otp": "Invalid Otp",
"user-not-found": "User not found!",
"enter-otp-alert": "Please Enter OTP!",
"get-verification-code": "Get verification code",
"get-verification-code-2": "You will get a verification code via Email",
"enter-verification-code": "Enter Verification Code",
"provide-your-details": "Provide your details",
"provide-email": "Please provide email.",
"additional-info": "Additional Info",
"signature-saved-alert": "Signature saved successfully.",
"fill-field": "Please fill out this field",
"error-template": "Error: Template not found!",
"error": "Error",
"invalid-document": "Error: invalid document!",
"document-not-found": "Document not Found!",
"document-signed-alert-1": "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers.",
"encrypted-pdf-not-support": "Currently encrypted pdf files are not supported.",
"document-signed-alert-4": " Congratulations! 🎉 This document has been successfully signed by all participants!",
"document-signed-alert-5": "This document has been signed by all Signers.",
"document-signed-alert-6": "This document has been declined by one or more recipient(s).",
"document-signed-alert-7": "This Document is no longer available.",
"document-signed-alert-8": "The document has already been dispatched for signing.",
"yet-to-sign": "Yet to sign",
"validation-alert-1": "The input does not meet the criteria set by the regular expression.",
"account-already-exists": "Account already exists!",
"choose-password": "Choose Password",
"enter-password": "Enter Password",
"sure": "sure",
"subscribed-alert": "You have already subscribed to plan!",
"monthly": "Monthly",
"yearly-upto": "Yearly (upto 66% off)",
"billed-yearly": "Billed Yearly",
"billed-monthly": "Billed Monthly",
"plansubscription-1": "Host it yourself for free",
"visit-github": "Visit Github",
"user-already-exist-name": "User already exists with this username!",
"do-not-access-contact-admin": "You don't have access, please contact the admin.",
"filed-required-correctly": "Please fill required details correctly.",
"admin-created": "Admin created",
"invalid-masterkey": "Invalid masterkey",
"opensign-Setup": "OpenSign Setup",
"master-key": " Master key",
"profile-update-alert": "Profile updated successfully.",
"date": "Date",
"report-not-found": "Report Not Found",
"please-wait": "please wait",
"select-foler": "Select Folder",
"generated-on": "Generated On",
"text-field": "Text field",
"font-size": "Font size",
"widget-status": {
"Required": "Required",
"Optional": "Optional"
},
"all-right": "All Rights Reserved",
"version": "version",
"social-media": {
"github": "Github",
"linked-in": "LinkedIn",
"twitter": "Twitter",
"discord": "Discord"
},
"tour-mssg": {
"home-layout-1": "You have logged in successfully! Let's take a look.",
"home-layout-2": "To upload documents for self-signing or to request others signatures, simply select the respective buttons.",
"home-layout-3": "You are ready to start using OpenSign! If you need support feel free to contact us.",
"generate-token": "Upgrade now to generate production API token.",
"opensign-drive-1": "Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.",
"opensign-drive-2": "Click the add button to create a new folder or document.",
"opensign-drive-3": "Sort your documents by Date or Name using this menu.",
"opensign-drive-4": "Click on this menu to display the documents in list view.",
"opensign-drive-5": "The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.",
"opensign-drive-6": "Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.",
"opensign-drive-7": "Right-click on any folder to see options. Choose Rename to change the folders name or click on the folder to navigate through its contents.",
"pdf-request-file-1": "List of signers who still need to sign the document .",
"pdf-request-file-2": "Click any of the placeholders appearing on the document to sign. You will then see options to draw your signature, type it, or upload an image .",
"pdf-request-file-3": "Click the Back, Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .",
"pdf-request-file-4": "List of signers who have already signed the document .",
"pdf-request-file-5": "You can click 'Auto Sign All' to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .",
"placeholder-sign-1": "Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.",
"placeholder-sign-2": "Clicking 'Add recipients' button will allow you to add more signers.",
"placeholder-sign-3": "Drag or click on a field to add it to the document.",
"placeholder-sign-4": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
"placeholder-sign-5": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
"report-1": "Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like HR Manager and New Employee. Each time you use the template, you can assign the New Employee role to different incoming staff members, while the HR Manager role remains constant, facilitating a seamless onboarding process for each recruit. ",
"redirect": "Click the 'Use' button to create a new document from an existing template.",
"bulksend": "To quickly create and send multiple documents using an existing template, click the 'Quick Send' button.",
"option": "This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
"signyour-self-1": "Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.",
"signyour-self-2": "Drag and drop anywhere in this area. You can resize and move it later.",
"template-placeholder-1": "Clicking 'Add role' button will allow you to add various signer roles. You can attach users to each role in subsequent steps.",
"template-placeholder-2": "Once roles are added, select a role from list to add a place-holder where he is supposed to sign. The placeholder will appear in the same colour as the role name once you drop it on the document.",
"template-placeholder-3": "Drag or click on a field to add it to the document.",
"template-placeholder-4": "Drag the placeholder for a role anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.",
"template-placeholder-5": "Clicking 'Next' will store the current template. After saving, youll be prompted to create a new document from this template if you wish.",
"webhook-1": "Upgrade now to set webhook",
"Need your Signature": "Clicking on this card will take you to the list of documents awaiting your review.",
"Out for signatures": "Clicking on this card will take you to a list of documents awaiting signature.",
"Recent signature requests": "This is a list of documents that are waiting for your signature.",
"Recently sent for signatures": "This is a list of documents you've sent to other parties for signature.",
"Drafts": "This are documents you have started but have not finalized for sending.",
"public-template": "This video demonstrates how to set up your personalized public profile, such as https://opensign.me/your-username. Youll also learn how to customize your tagline and make your templates available for public signing."
},
"enter-email-plaholder": "Add an email address and hit enter",
"success-email-alert": "Email sent successfully!",
"expired-doc-title": "Expired Document",
"expired-on-mssg": "This document expired on {{expiredDate}} and is no longer available to sign.",
"signature-validate-alert": "Please confirm that you have selected at least {{minRequiredCount}} checkboxes.",
"signature-validate-alert-2": "Ensure this field is accurately filled and meets all requirements.",
"remaing-users": "Remaining users",
"Role": "Role",
"additional-users": "Please purchase add-on users.",
"Quantity-of-users": "Quantity of users",
"Price": "Price",
"Total-price-for-next-time": "Total price for next time",
"Proceed": "Proceed",
"Select": "Select",
"Add-seats": "Add Seats",
"user-name-exist": "user name already exist",
"format": "format",
"select-date-format": "Select a date format",
"quantity-of-credits": "Quantity of premium credits",
"remaining-credits": "Premium credits available:",
"remaining-credits-help":"Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
"additional-credits": "Please purchase premium credits",
"quota-err-quicksend": "Quota Reached, You don't have enough credits.",
"buy-credits": "Buy Premium Credits",
"rotate-right":"Rotate right",
"rotate-left":"Rotate left",
"rotate-alert-mssg":"All widgets on this page will be lost. Are you sure you want to proceed?",
"templateid":"Template-Id",
"bulk-send-subcription-alert":"Please upgrade to Professional or Team plan to use bulk send.",
"generate-test-token":"Generate Test Token",
"regenerate-test-token":"Regenerate Test Token",
"help-test-token":"This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once youve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
"help-api-token":"This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
"reason":"Reason",
"decline-by":"Declined/revoked by",
"document-declined": "Document declined",
"quota-mail-info-head":"Monthly request signatures email limit",
"quota-mail-info": "You can send upto 15 signature request emails every month. Upgrade now to send unlimited signing requests directly.",
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
"quota-mail-tip":"Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
"quota-mail-head":"Quota Reached",
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.",
"sent-this-month":"Sent this month"
}
@@ -0,0 +1,640 @@
{
"header-news": "Nouvelle fonctionnalité : les profils publics sont désormais en ligne ! Créez et partagez facilement des modèles publics",
"header-news-btn": "Configurer maintenant",
"create-account": "Créer un compte",
"login": "Se Connecter",
"language": "Langue",
"name": "Nom et Prénom",
"phone": "Téléphone",
"phone-optional": "(facultatif)",
"email": "E-mail",
"company": "Organisation",
"job-title": "Votre Fonction",
"profile": "Profil",
"is-email-verified": "L'e-mail est-il vérifié",
"log-out": "Déconnexion",
"public-profile": "Profil public",
"tagline": "Slogan",
"has-requested-you-to-sign": " vous a demandé de signer",
"has-requested-you-to-review-and-sign": "vous a demandé de consulter et de signer",
"input-required": "Veuillez remplir ce champ.",
"sort-order": {
"Ascending": "Ascendant",
"Descending": "Descendant",
"Name": "Nom et Prénom",
"Date": "Date"
},
"context-menu": {
"Download": "Télécharger",
"Rename": "Renommer",
"Move": "Se déplacer",
"Delete": "Supprimer"
},
"action": "Action",
"add": "Ajouter",
"add-new-folder": "Ajouter un nouveau dossier",
"disable-documentId": "Désactiver l'ID du document",
"edit": "Modifier",
"change-password": "Changer votre mot de passe",
"save": "Sauvegarder",
"cancel": "Annuler",
"upgrade-now": "Mettre à jour maintenant",
"upgrade-to": "Mettre à niveau vers",
"pro": "PRO",
"plan": "Offre",
"subscribe-card-teamplan":"Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
"tour-content": "Ne plus afficher",
"team": "Équipe",
"docs": "Documents",
"session-expired": "Votre session a expiré.",
"user": "Utilisateur",
"public-profile-help": "Il s'agit de votre URL publique. Copiez-le ou partagez-le avec le signataire et vous pourrez voir tous vos modèles définis publiquement.",
"tagline-help": "Ce surnom apparaîtra sur votre profil public OpenSign par exemple. https://opensign.me/alex",
"welcome": "Content de te revoir!",
"Login-to-your-account": "Se connecter à son compte",
"password": "Mot de passe",
"forgot-password": "Mot de passe oublié?",
"loading": "Chargement...",
"of": "de",
"sign-SSO": "Connectez-vous avec SSO",
"login-page": "Page de connexion",
"or": "OU",
"signup-page": "Page d'inscription",
"password-length": "Le mot de passe doit être de plus de 8 caractères",
"password-case": "Le mot de passe doit contenir une lettre majuscule, minuscule et un chiffre ",
"password-special-char": "Le mot de passe doit contenir un caractère spécial",
"agreee": "Je suis d'accord avec le",
"term": "Conditions d'utilisation",
"subscribe-to-opensign": "Abonnez-vous à la newsletter OpenSign",
"register": "S'enregistrer ",
"sidebar": {
"Dashboard": "Tableau de bord",
"Sign yourself": "signez vous-même",
"Request signatures": "Demander des signatures",
"Templates": "Modèles",
"Templates-Children": {
"Create template": "Créer un modèle",
"Manage templates": "Gérer les modèles"
},
"OpenSign™ Drive": "Lecteur OpenSign™",
"Reports": "Rapports",
"Reports-Children": {
"Need your sign": "Besoin de votre signature",
"In Progress": "En cours",
"Completed": "Complété",
"Drafts": "Brouillons",
"Declined": "Signature refusé",
"Expired": "Expiré",
"Contactbook": "Carnet de contacts"
},
"Settings": "Paramètres",
"Settings-Children": {
"My Signature": "Ma signature",
"API Token": "Jeton API",
"Webhook": "Webhook",
"Teams": "Équipe",
"Team-update-successfully": "Équipe mise à jour avec succès.",
"Users": "Utilisateurs"
}
},
"dashboard-card": {
"Need your Signature": "Besoin de votre signature",
"Out for signatures": "En attente de Signature"
},
"report-name": {
"Draft Documents": "Brouillons",
"Need your sign": "Besoin de votre signature",
"In-progress documents": "Documents en cours",
"Completed Documents": "Documents complétés",
"Declined Documents": "Documents refusés",
"Expired Documents": "Documents expirés",
"Recently sent for signatures": "Récemment envoyé pour signature",
"Recent signature requests": "Demandes de signature récentes",
"Drafts": "Brouillons",
"Contactbook": "Carnet de contacts",
"Templates": "Modèles",
"Teams": "Équipe",
"Users": "Utilisateurs"
},
"report-heading": {
"Sr.No": "Numéro",
"Title": "Titre",
"Email": "E-mail",
"Phone": "Téléphone",
"File": "Fichier",
"Owner": "Propriétaire",
"Signers": "Signataires",
"Note": "Note",
"Folder": "Dossier",
"Reason": "Raison",
"Parent Team": "Équipe de parents",
"Active": "Actif",
"Role": "Rôle",
"Team": "Équipe",
"Name": "Nom",
"Status": "Statut",
"created-date": "Date de création",
"Type": "Saisir"
},
"btnLabel": {
"sign": "Signer",
"Resend": "Relancer",
"Revoke": "Révoquer",
"Delete": "Supprimer",
"Use": "Utiliser",
"Quick send": "Envoi rapide",
"Edit": "Modifier",
"Share with team": "Partager avec l'équipe",
"Share": "Partager",
"View": "Voir",
"option": "Option",
"Embed": "Intégrer"
},
"report-help": {
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
"Need your sign": "Voici une liste de documents ou votre signature est requise",
"In-progress documents": "Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
"Completed Documents": "Il s'agit d'une liste de documents signés par toutes les parties.",
"Declined Documents": "Il s'agit d'une liste de documents qui ont été refusés par l'un des signataires.",
"Expired Documents": "Il s'agit d'une liste de documents qui ont atteint leur date d'expiration.",
"Contactbook": "Il s'agit d'une liste de contacts/signataires que vous avez ajoutés. Ceux-ci apparaîtront sous forme de suggestions lorsque vous tenterez d'ajouter des signataires à un nouveau document.",
"Templates": "Il s'agit d'une liste de modèles à votre disposition pour créer des documents. Vous pouvez cliquer sur le bouton 'Utiliser' pour créer un nouveau document à l'aide d'un modèle, modifier le document et ajouter des signataires à l'étape suivante."
},
"form-name": {
"Sign Yourself": "signez vous-même",
"Request Signatures": "Demander des signatures",
"New Template": "Nouveau modèle"
},
"file-type": "pdf, png, jpg, jpeg",
"docx": "docx",
"file-selected": "fichier sélectionné",
"template-title": "Titre du modèle",
"document-title": "Titre du document",
"description": "Description",
"time-to-complete": "Temps de réalisation (jours)",
"send-in-order": "Envoyer dans l'ordre ?",
"send-in-order-help": {
"p1": "Choisissez la manière dont vous souhaitez que les demandes de signature soient envoyées aux signataires du document :",
"p2": "La sélection de cette option enverra initialement la demande de signature au premier signataire. Une fois que le premier signataire a terminé sa partie, le prochain signataire de la séquence recevra la demande. Ce processus se poursuit jusqu'à ce que tous les signataires aient signé le document. Cette méthode garantit que le document est signé dans un ordre spécifique.",
"p3": "La sélection de cette option enverra les liens de signature à tous les signataires simultanément. Chaque signataire peut signer le document à sa convenance, que d'autres signataires aient ou non complété leur signature. Cette méthode est plus rapide mais nimpose aucun ordre de signature entre les participants.",
"p4": "Sélectionnez l'option qui correspond le mieux aux besoins de votre traitement de documents."
},
"no": "Non",
"auto-reminder": "Rappel automatique",
"remind-once": "Rappeler une fois tous les (jours)",
"next": "Suivant",
"select-folder": "Sélectionner le dossier",
"OpenSign-drive": "Lecteur OpenSign™",
"drive-document-status": {
"Completed": "Complété",
"Declined": "Refuser",
"Draft": "Brouillon",
"Expired": "Expiré",
"In Progress": "En cours"
},
"select-folder-help": "Si vous ne sélectionnez pas de dossier, votre document signé sera enregistré dans le dossier du lecteur principal OpenSign.",
"no-data": "Aucune données disponible",
"save-here": "Enregistrer ici",
"back": "Retour",
"add-folder": "Ajouter le dossier",
"create-folder": "Créer le dossier",
"parent-folder": "Dossier Parent",
"create": "Créer",
"signers": "Signataires",
"signers-help": "Commencez à saisir le nom d'un contact pour voir les signataires suggérés par vos contacts enregistrés ou en ajouter de nouveaux. Organisez l'ordre de signature en ajoutant des signataires dans l'ordre souhaité. Utilisez le bouton « + » pour inclure les signataires et le « x » pour les supprimer. Chaque signataire recevra un e-mail invité à signer le document dans l'ordre indiqué.",
"add-signer": "Ajouter un signataire",
"contact-not-found": "Contact introuvable",
"add-yourself": "Ajoutez-vous",
"submit": "Soumettre",
"reset": "Réinitialiser",
"my-signature": "Ma signature",
"signature": "Signature",
"upload-image": "Télécharger une image",
"clear": "Vider",
"upload-signature/Image": "télécharger-signature/Image",
"initials": "Initiales",
"API": "API",
"api-token": "Jeton API",
"regenerate-token": "Régénérer en direct jeton",
"generate-token": "Générer en direct jeton",
"view-docs": "Afficher les documents",
"generate-token-alert": "Êtes-vous sûr de vouloir régénérer le jeton, votre ancien jeton sera supprimer?",
"yes": "Oui",
"copied": "Copié",
"wrong-mssg": "Quelque chose c'est mal passé. Merci d'essayer plus tard.",
"token-generated": "Jeton généré avec succès.",
"webhook": "Webhook",
"update-webhook": "Mettre à jour le webhook",
"add-webhook": "Ajouter un webhook",
"webhook-added": "Webhook ajouté avec succès.",
"team-disabled": "Équipe: Désactivée",
"team-enabled": "Équipe activée",
"are-you-sure": "Êtes-vous sûr de vouloir",
"disable": "désactiver",
"enable": "activer",
"this-team": "cette équipe?",
"edit-team": "Modifier l'équipe",
"name-of-team": "Nom de l'équipe",
"prev": "précédent",
"no-data-avaliable": "pas de données disponibles",
"add-team": "Ajouter une équipe",
"page-not-found": "Page non trouvée",
"users-from-teams": "utilisateurs de votre Équipe",
"user-status": "Statut de l'utilisateur",
"activate": "Activer",
"deactivate": "désactiver",
"this-user": "cet utilisateur ",
"delete-user": "Supprimer l'utilisateur",
"delete": "supprimer",
"add-user": "Ajouter un utilisateur",
"password-generateed": "Le mot de passe ne sera généré qu'une seule fois ; assurez-vous de le copier.",
"Team status": "Statut de l'équipe",
"user-deactivated": "Utilisateur désactivé.",
"user-activated": "Activé par l'utilisateur.",
"public": "Public",
"download": "Téléchargement",
"view": "voir",
"make-template-public": "Rendre le modèle public",
"make-template-private": "Rendre le modèle privé",
"make-template-public-alert": "Êtes-vous sûr de vouloir rendre ce modèle public ?",
"make-template-private-alert": "Êtes-vous sûr de vouloir rendre ce modèle privé ? Cela le supprimera de votre profil public.",
"public-role": "Rôle public",
"public-url": "URL publique",
"public-url-copy-mssg": "Voici votre URL publique : copiez-la ou partagez-le avec le signataire et vous pouvoir voir tous vos paramètres publics modèles.",
"add-public-url-alert": "Veuillez ajouter votre URL publique et vous pourra rendre public modèle.",
"share-with-alert": "Vous ne pouvez pas partager un modèle si des contacts sont déjà attribués à des rôles. Veuillez supprimer toutes les attributions de contacts des rôles avant de partager le modèle.",
"share-with": "Partager avec",
"team-not-found": "Équipe introuvable",
"roles": "Les rôles",
"delete-document": "Supprimer un document",
"delete-document-alert": "Êtes-vous sûr de vouloir supprimer ce document ?",
"quick-send": "Envoi rapide",
"add-new": "Ajouter un nouveau",
"send": "envoyer",
"quick-send-alert-1": "Tous les rôles dans ce document sont actuellement liés à des contacts. Pour envoyer rapidement des copies de ce modèle à plusieurs signataires, veuillez vous assurer qu'au moins un rôle n'est lié à aucun contact.",
"quick-send-alert-2": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
"quick-send-alert-3": "Veuillez ajouter au moins un rôle à ce modèle afin d'en « envoyer rapidement » des copies à plusieurs signataires.",
"quick-send-alert-4": "L'envoi rapide a atteint la limite.",
"copy-link": "Copier le lien",
"copy": "Copier",
"revoke-document": "Révoquer le document",
"revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?",
"resend-mail": "Renvoyer le courrier",
"resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
"subject": "Sujet",
"body": "Corps",
"add-contact": "Ajouter le contact",
"add-signer-alert": "Le contact existe déjà !",
"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.",
"document-sent-alert": "Document envoyé avec succès !",
"template-share-alert": "Modèle partagé avec succès.",
"template-public-alert-1": "Vous avez rendu le modèle public avec succès.",
"template-public-alert-2": "Vous avez réussi à rendre le modèle privé.",
"template-public-alert-3": "Vous devez sélectionner un rôle pour les signataires publics.",
"template-public-alert-4": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les signataires.",
"template-public-alert-5": "Étant donné que l'envoi dans l'ordre est activé sur ce modèle, le rôle public doit être positionné en haut.",
"template-public-alert-6": "Veuillez associer des signataires à tous les rôles, à l'exception du rôle d'utilisateur public.",
"template-public-alert-7": "Veuillez attribuer au moins un rôle public pour rendre ce modèle public.",
"loader": "Cela pourrait prendre un certain temps",
"pages": "Pages",
"document-signed": "Document signé",
"close": "Fermer",
"validation-alert": "Alerte de validation",
"validate-alert-mssg": "L'entrée ne répond pas aux critères définis par l'expression régulière.",
"otp-verification": "Vérification OTP",
"enter-otp": "Entrez OTP",
"verify": "Vérifier",
"verify-email-1": "Vérifier l'e-mail",
"resend": "Renvoyer",
"contact-details": "Coordonnées",
"verify-email": "Veuillez vérifier votre email!",
"send-otp": "envoyer un code à usage unique",
"otp-placeholder": "Entrez le code de vérification reçu par e-mail",
"loading-doc": "Chargement du document..",
"widgets-name": {
"signature": "signature",
"stamp": "tampon",
"initials": "initiales",
"name": "nom et Prénom",
"job title": "votre fonction",
"company": "organisation",
"date": "date",
"text": "texte",
"text input": "saisie de texte",
"checkbox": "case à cocher",
"dropdown": "dérouler",
"radio button": "bouton radio",
"image": "image",
"email": "e-mail"
},
"fields": "Des champs",
"recipients": "Destinataires",
"please-add": "Veuillez ajouter un",
"signed-by": "Signé par",
"more": "plus",
"sent": "Envoyé",
"print": "imprimer",
"certificate": "Certificat",
"decline": "refusé",
"finish": "terminé",
"mail": "Mail",
"sign-now": "Signez maintenant",
"successfully-signed": "Signé avec succès !",
"email-mssg": "Les destinataires ajoutés ici recevront une copie du document signé.",
"email-error-1": "veuillez fournir une adresse e-mail correcte",
"email-error-2": "Vous ne pouvez envoyer qu'à dix destinataires à la fois.",
"Email-verified-alert-1": "L'e-mail est vérifié.",
"Email-verified-alert-2": "L'e-mail est déjà vérifié.",
"upload-stamp-image": "Télécharger l'image du tampon",
"draw": "Dessiner",
"type": "Taper",
"color-type": {
"red": "rouge",
"blue": "bleu",
"black": "noir",
"yellow": "jaune"
},
"my-initials": "Mes initiales",
"upload": "Télécharger",
"initial-teb": "Initiales",
"signature-tab": "Signature",
"your-signature": "Votre signature",
"copy-title": "Copier le widget vers",
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
"reset-password": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
"reset-password-alert-3": "Réinitialisez votre mot de passe",
"faild-animation": "Échec du chargement de l'animation",
"apply": "Appliquer",
"copy-type": {
"All pages": "Toutes les pages",
"All pages but last": "Toutes les pages sauf la dernière",
"All pages but first": "Toutes les pages sauf la première",
"Next to current widget": "À côté du widget actuel"
},
"options": "Possibilités",
"minimun-check": "Vérification minimale",
"maximum-check": "Contrôle maximum",
"default-value": "Valeur par défaut",
"select": "Sélectionner",
"read-only": "Est en lecture seule",
"hide-labels": "Masquer les étiquettes",
"checkbox": "Case à cocher",
"alert": "Alerte",
"zoom-in": "Agrandir",
"zoom-out": "Dézoomer",
"document-signed-alert": "Félicitations ! 🎉 Votre document à bien été signer!",
"otp-sent-alert": "Le code OTP a bien été envoyer sur votre adresse Email",
"fields-required": "Champs obligatoires",
"signature-widget-alert-1": "Veuillez vous assurer qu'au moins un widget de signature a été ajouté",
"signature-widget-alert-2": "Veuillez vous assurer que tous les champs sont remplis avec précision et répondent à toutes les exigences.",
"encrypted-pdf-alert": "Les fichiers PDF actuellement cryptés ne sont pas pris en charge.",
"encrypted-pdf-alert-1": "Le mot de passe du fichier PFX n'est pas valide.",
"user-not-exist": "L'utilisateur n'existe pas",
"template-signature-field": "Veuillez ajouter au moins un champ de signature pour tous les rôles.",
"template-role-alert": "Veuillez d'abord ajouter des rôles",
"create-document": "Créer un document",
"template-created-alert": "Voulez-vous créer un document en utilisant le modèle que vous vient de créer ?",
"signers-alert": "alerte des signataires",
"template-creation-alert-1": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
"ok": "D'accord",
"radio-group": "Groupe de radio",
"dropdown-options": "Options déroulantes",
"add-role": "Ajouter un rôle",
"role-ex": "Ex : Client, RH, Directeur, Manager, Étudiant, etc.",
"add/choose-signer": "Ajouter/Choisir un signataire",
"choose-from-contacts": "Choisissez parmis les contacts",
"select-signer": "Veuillez sélectionner le signataire",
"edit-template": "Modifier le modèle",
"widget-info": "Informations sur les widgets",
"validation": "Validation",
"invalid-default-value": "valeur par défaut invalide",
"hint": "Indice",
"color": "couleur",
"role-help": {
"p1": "Que sont les rôles modèles ?",
"p2": "Commencez par spécifier chaque rôle nécessaire à la finalisation du document. Pensez aux parties impliquées dans le processus de signature et à leurs responsabilités. Les rôles courants incluent les RH pour les documents internes, le Client pour les accords ou le Fournisseur pour les accords commerciaux.",
"p3": "Pourquoi pré-attacher les utilisateurs à certains rôles ?",
"p4": "Pour les rôles qui impliquent systématiquement la même personne (par exemple, la signature du PDG sur les lettres d'offre des employés), vous pouvez pré-attacher un utilisateur à un rôle dans le modèle. Cette étape est facultative mais recommandée pour des raisons d'efficacité et de cohérence entre les documents.",
"p5": "Quand dois-je spécifier l'utilisateur attaché à chaque rôle ?",
"p6": "Lorsque vous créez un document à partir de votre modèle, vous serez invité à attacher des utilisateurs à chaque rôle défini. Si un rôle est déjà associé à un utilisateur, celui-ci sera pré-rempli, mais vous pourrez le modifier si nécessaire avant d'envoyer le document."
},
"add-recipients": "Ajouter des destinataires",
"loading-mssg": "Cela pourrait prendre du temps",
"send-mail": "Envoyer un mail",
"placeholder-alert-1": "Veuillez vous assurer qu'au moins un widget de signature est ajouté pour tous les destinataires.",
"placeholder-alert-2": "Veuillez confirmer que vous avez rempli le champ de texte.",
"placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ",
"placeholder-alert-4": "Vous avez envoyé avec succès des mails à tous les",
"placeholder-alert-5": "Voulez-vous signer des documents maintenant ?",
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !",
"placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
"email-subject": "Sujet du courriel",
"email-body": "corps de l'e-mail",
"email-placeholder": "Ajouter le corps de l'e-mail",
"reset-to-default": "Réinitialiser aux valeurs par défaut",
"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 !",
"document-alert": "Alerte document",
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
"subscription-expired": "Abonnement expiré",
"alert-message": "Message d'alerte",
"document-decline": "Document-refusé",
"decline-alert-1": "Etes-vous sûr de vouloir refuser ce document ?",
"decline-alert-2": "Vous avez refusé ce document !",
"decline-alert-3": "Vous ne pouvez pas signer ce document car il a été refusé/révoqué.",
"sign here": "Signer ici",
"guest-email-alert": "le code de vérification a été envoyé sur votre e-mail",
"get-otp-alert": "vous recevrez un code de vérification par e-mail",
"user-already-exist": "L'utilisateur existe déjà.",
"server-error": "Erreur interne du serveur !",
"fill-required-details!": "Veuillez remplir les informations requises !",
"sign-up-form": "Formulaire d'inscription",
"sign-up": "S'inscrire",
"folder-already-exist!": "le dossier existe déjà !",
"please-select-position!": "Veuillez sélectionner un poste !",
"default-sign-alert": "Êtes-vous sûr de vouloir vous connecter automatiquement à tous les emplacements demandés ? ",
"Your-Signature": "Votre signature",
"auto-sign-all": "Tout signer automatiquement",
"generating-certificate": "Génération du certificat",
"pdf-download": "Téléchargement PDF",
"generate-certificate-alert": " Votre certificat d'achèvement est en cours de génération. Veuillez patienter quelques instants. Si le téléchargement ne démarre pas sous peu, cliquez à nouveau sur le bouton. ",
"folder-already-exist": "Le dossier existe déjà !",
"folder-created-successfully": "Dossier créé avec succès !",
"fill-folder-name": "Veuillez indiquer le nom du dossier",
"already-exists-this-username": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
"verification-code-sent": "Un e-mail de vérification a été envoyé à votre adresse e-mail !",
"registered-user-successfully": "Utilisateur enregistré avec succès.",
"role-not-found": "Rôle introuvable.",
"do-not-access": "Vous n'y avez pas accès, veuillez contacter l'administrateur.",
"add-admin": "Ajouter un administrateur",
"opensign-setup": "Configuration d'OpenSign",
"join-discord": "Rejoignez notre serveur Discord",
"admin-already-exist": "L'administrateur existe déjà.",
"password-update-alert-1": "Mot de passe mis à jour avec succès.",
"password-update-alert-2": "Votre mot de passe actuel est manquant ou incorrect.",
"password-update-alert-3": "Votre mot de passe actuel est manquant ou incorrect.",
"password-update-alert-4": "Votre mot de passe et votre mot de passe de confirmation ne correspondent pas.",
"current-password": "mot de passe actuel",
"new-password": "nouveau mot de passe",
"confirm-password": "Confirmez le mot de passe",
"file-alert-1": "La taille du fichier sélectionné est trop grande. Veuillez sélectionner un fichier inférieur à",
"file-alert-2": "Veuillez sélectionner un fichier.",
"file-alert-3": "Veuillez patienter pendant le téléchargement du document.",
"enter-pdf-password": "Entrez le mot de passe PDF",
"correct-password": "Veuillez fournir un mot de passe correct",
"decrypting-pdf": "Décryptage du pdf, veuillez patienter...",
"invalid-otp": "OTP invalide",
"user-not-found": "Utilisateur non trouvé!",
"enter-otp-alert": "Veuillez saisir OTP !",
"get-verification-code": "Obtenir le code de vérification",
"get-verification-code-2": "Vous recevrez un code de vérification par e-mail",
"enter-verification-code": "Entrez le code de vérification",
"provide-your-details": "Fournissez vos coordonnées",
"provide-email": "Veuillez fournir un e-mail.",
"additional-info": "Information additionnelle",
"signature-saved-alert": "Signature enregistrée avec succès.",
"fill-field": "Veuillez remplir ce champ",
"error-template": "Erreur : modèle introuvable ! ",
"error": "Erreur",
"invalid-document": "Erreur : document invalide !",
"document-not-found": "Document introuvable !",
"document-signed-alert-1": "Vous avez signé le document avec succès. Vous pouvez télécharger ou imprimer une copie du document partiellement signé. Une copie du document signé numériquement sera envoyée au propriétaire par e-mail une fois qu'il sera signé par tous les signataires.",
"encrypted-pdf-not-support": "Les fichiers PDF actuellement cryptés ne sont pas pris en charge. ",
"document-signed-alert-4": "Toutes nos félicitations! 🎉 Ce document a été signé avec succès par tous les participants !",
"document-signed-alert-5": "Ce document a été signé par tous les signataires.",
"document-signed-alert-6": "Ce document a été refusé par un ou plusieurs destinataire(s).",
"document-signed-alert-7": "Ce document n'est plus disponible.",
"document-signed-alert-8": "Le document a déjà été envoyé pour signature.",
"yet-to-sign": "Reste à signer",
"validation-alert-1": "L'entrée ne répond pas aux critères définis par l'expression régulière.",
"account-already-exists": "Le compte existe déjà!",
"choose-password": "Choisissez un mot de passe",
"enter-password": "Entrer le mot de passe",
"sure": "bien sûr",
"subscribed-alert": "Vous êtes déjà abonné au forfait !",
"monthly": "Mensuel",
"yearly-upto": "Annuel (jusqu'à 66 % de réduction)",
"billed-yearly": "Facturé annuellement",
"billed-monthly": "Facturé mensuellement",
"plansubscription-1": "Hébergez-le vous-même gratuitement",
"visit-github": "Visitez Github",
"user-already-exist-name": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
"do-not-access-contact-admin": "Vous n'y avez pas accès, veuillez contacter l'administrateur.",
"filed-required-correctly": "Veuillez remplir correctement les informations requises.",
"admin-created": "Administrateur créé",
"invalid-masterkey": "Clé principale invalide",
"opensign-Setup": "Configuration d'OpenSign",
"master-key": "La clef maitresse",
"profile-update-alert": "Mise à jour du profil réussie.",
"date": "Date",
"report-not-found": "Rapport introuvable",
"please-wait": "S'il vous plaît, attendez",
"select-foler": "Sélectionner le dossier",
"generated-on": "Généré le",
"text-field": "Champ de texte",
"font-size": "Taille de police",
"widget-status": {
"Required": "Requis",
"Optional": "Facultative"
},
"all-right": "Tous droits réservés",
"version": "version",
"social-media": {
"github": "Github",
"linked-in": "LinkedIn",
"twitter": "Twitter",
"discord": "Discorde"
},
"tour-mssg": {
"home-layout-1": "Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.",
"home-layout-2": "Pour télécharger des documents à auto-signature ou pour demander la signature d'autres personnes, sélectionnez simplement les boutons correspondants.",
"home-layout-3": "Vous êtes prêt à commencer à utiliser OpenSign ! Si vous avez besoin d'aide, n'hésitez pas à nous contacter.",
"generate-token": "Effectuez la mise à niveau maintenant pour générer un jeton API de production.",
"opensign-drive-1": "Cliquez sur les liens du fil d'Ariane pour naviguer facilement dans la hiérarchie des dossiers et afficher les documents dans chaque dossier.",
"opensign-drive-2": "Cliquez sur le bouton Ajouter pour créer un nouveau dossier ou document.",
"opensign-drive-3": "Triez vos documents par date ou par nom à l'aide de ce menu.",
"opensign-drive-4": "Cliquez sur ce menu pour afficher les documents sous forme de liste.",
"opensign-drive-5": "La liste des documents s'affiche selon l'option de tri sélectionnée. Les icônes à côté de chaque document indiquent son état actuel.",
"opensign-drive-6": "Faites un clic droit sur un document pour voir les options telles que Télécharger, Renommer, Déplacer et Supprimer. Cliquez sur le document pour l'ouvrir.",
"opensign-drive-7": "Faites un clic droit sur n'importe quel dossier pour voir les options. Choisissez 'Renommer' pour changer le nom du dossier ou cliquez sur le dossier pour naviguer dans son contenu.",
"pdf-request-file-1": "Liste des signataires qui doivent encore signer le document.",
"pdf-request-file-2": "Cliquez sur l'un des espaces réservés apparaissant sur le document pour signer. Vous verrez alors des options pour dessiner votre signature, la saisir ou télécharger une image.",
"pdf-request-file-3": "Cliquez sur les boutons Précédent, Refuser ou Terminer pour parcourir votre document. Utilisez le menu points de suspension pour des options supplémentaires, notamment le bouton Télécharger.",
"pdf-request-file-4": "Liste des signataires ayant déjà signé le document.",
"pdf-request-file-5": "Vous pouvez cliquer sur 'Signer automatiquement tout' pour signer automatiquement à tous les emplacements que vous êtes censé signer. Assurez-vous de lire correctement le document avant de cliquer sur ce bouton.",
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra de la même couleur que le nom du destinataire une fois que vous le déposerez sur le document.",
"placeholder-sign-2": "Cliquer sur le bouton 'Ajouter des destinataires' vous permettra d'ajouter plus de signataires.",
"placeholder-sign-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
"placeholder-sign-4": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
"placeholder-sign-5": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
"report-1": "Cliquez sur le bouton 'Ajouter' pour créer un nouveau modèle. Les modèles sont des documents réutilisables conçus pour générer rapidement de nouveaux documents avec la même structure et différents signataires. Par exemple, un modèle RH pour l'intégration peut avoir des rôles prédéfinis tels que 'Responsable RH' et ' Nouvel employé'. Chaque fois que vous utilisez le modèle, vous pouvez attribuer le rôle 'Nouvel employé' à différents membres du personnel entrants, tandis que le rôle 'Responsable RH' reste constant, facilitant ainsi un processus d'intégration fluide pour chaque recrue",
"redirect": "Cliquez sur le bouton 'Utiliser' pour créer un nouveau document à partir d'un modèle existant.",
"bulksend": "Pour créer et envoyer rapidement plusieurs documents à l'aide d'un modèle existant, cliquez sur le bouton 'Envoi rapide'.",
"option": "Ce menu révèle plus d'options telles que Modifier et Supprimer. Utilisez le bouton « Modifier » pour ajouter des rôles de signataire, modifier les champs et mettre à jour votre modèle. Les modifications s'appliqueront à tous les futurs documents créés à partir de ce modèle mais n'affecteront pas les documents existants. Utilisez le bouton Supprimer, vous pouvez supprimer le modèle ",
"signyour-self-1": "Sélectionnez et faites glisser vos widgets préférés sur le PDF pour personnaliser votre document avant de le signer. Choisissez les endroits parfaits pour chaque modification afin d'adapter le document à vos besoins.",
"signyour-self-2": "Faites glisser et déposer n'importe où dans cette zone. Vous pourrez la redimensionner et la déplacer plus tard.",
"template-placeholder-1": "Cliquer sur le bouton 'Ajouter un rôle' vous permettra d'ajouter différents rôles de signataire. Vous pourrez attacher des utilisateurs à chaque rôle dans les étapes suivantes.",
"template-placeholder-2": "Une fois les rôles ajoutés, sélectionnez un rôle dans la liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra dans la même couleur que le nom du rôle une fois que vous l'aurez déposé sur le document.",
"template-placeholder-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
"template-placeholder-4": "Faites glisser l'espace réservé pour un rôle n'importe où sur le document. N'oubliez pas qu'il apparaîtra dans la même couleur que le nom du destinataire pour une référence facile.",
"template-placeholder-5": "Cliquer sur 'Suivant' stockera le modèle actuel. Après l'enregistrement, vous serez invité à créer un nouveau document à partir de ce modèle si vous le souhaitez.",
"webhook-1": "Mettez à niveau maintenant pour définir le webhook",
"Need your Signature": "En cliquant sur cette carte, vous accéderez à la liste des documents en attente de révision.",
"Out for signatures": "En cliquant sur cette carte, vous accéderez à une liste de documents en attente de signature.",
"Recent signature requests": "Voici une liste de documents qui attendent votre signature.",
"Recently sent for signatures": "Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
"Drafts": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
"public-template": "Cette vidéo montre comment configurer votre profil public personnalisé, tel que 'https://opensign.me/your-username'. Vous apprendrez également comment personnaliser votre slogan et rendre vos modèles disponibles pour la signature publique."
},
"enter-email-plaholder": "Ajoutez une adresse email et appuyez sur Entrée",
"success-email-alert": "E-mail envoyé avec succès!",
"expired-doc-title": "Document expiré",
"expired-on-mssg": "Ce document a expiré le {{expiredDate}} et n'est plus disponible pour signature.",
"signature-validate-alert": "Veuillez confirmer que vous avez coché au moins {{minRequiredCount}} cases.",
"signature-validate-alert-2": "Assurez-vous que ce champ est rempli avec précision et répond à toutes les exigences.",
"user-name-exist": "le nom d'utilisateur existe déjà",
"remaing-users": "Utilisateurs restants",
"Role": "Rôle",
"additional-users": "Veuillez acheter des utilisateurs supplémentaires.",
"Quantity-of-users": "Quantité d'utilisateurs",
"Price": "Prix",
"Total-price-for-next-time": "Prix total pour la prochaine fois",
"Proceed": "Procéder",
"Select": "sélectionner",
"Add-seats": "Ajouter des sièges",
"format": "format",
"select-date-format": "Sélectionnez un format de date",
"quantity-of-credits":"Quantité de crédits de prime",
"remaining-credits":"Crédits premium disponibles :",
"additional-credits":"Veuillez acheter des crédits premium",
"remaining-credits-help":"Utilisez des crédits premium pour la signature de documents API, l'envoi groupé ou l'intégration d'OpenSign sur votre site Web. Il vous reste {{allowedcredits}} crédits inclus et {{addoncredits}} crédits achetés supplémentaires.",
"quota-err-quicksend": "Quota atteint, vous n'avez pas assez de crédits.",
"buy-credits": "Acheter des crédits premium",
"rotate-right" :"Faire pivoter à droite",
"rotate-left" :"Faire pivoter à gauche",
"rotate-alert-mssg" :"Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?",
"templateid":"ID de modèle",
"bulk-send-subcription-alert":"Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.",
"generate-test-token": "Générer jeton de test",
"regenerate-test-token":"Régénérer le jeton de test",
"help-test-token":"Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à lun de nos forfaits payants pour générer un jeton de production.",
"help-api-token":"Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.",
"reason" :"Raison",
"decline-by" :"Refusé/révoqué par",
"document-declined":"document refusé",
"quota-mail-info-head":"Limite mensuelle d'e-mails de signatures de demandes",
"quota-mail-info": "Vous pouvez envoyer jusqu'à 15 e-mails de demande de signature chaque mois. Mettez à niveau maintenant pour envoyer directement des demandes de signature illimitées.",
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
"quota-mail-tip-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
"quota-mail-head":"Quota atteint",
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.",
"sent-this-month":"envoyé ce mois-ci"
}
File diff suppressed because one or more lines are too long
Binary file not shown.
+2 -3
View File
@@ -20,6 +20,7 @@ import SSOVerify from "./pages/SSOVerify";
import Loader from "./primitives/Loader";
import TeamList from "./pages/TeamList";
import UserList from "./pages/UserList";
import { serverUrl_fn } from "./constant/appinfo";
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
@@ -53,9 +54,7 @@ function App() {
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const baseurl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const baseurl = serverUrl_fn();
try {
localStorage.setItem("baseUrl", `${baseurl}/`);
localStorage.setItem("parseAppId", appId);
+18 -10
View File
@@ -1,8 +1,10 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const AddSigner = (props) => {
const { t } = useTranslation();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
@@ -167,17 +169,17 @@ const AddSigner = (props) => {
}
}
} else {
alert("Contact already exist!");
alert(t("add-signer-alert"));
setIsLoader(false);
}
} catch (err) {
console.log("err in fetch contact", err);
setIsLoader(false);
alert("something went wrong, please try again later");
alert(t("something-went-wrong-mssg"));
}
} else {
setIsLoader(false);
alert("something went wrong, please try again later");
alert(t("something-went-wrong-mssg"));
}
};
@@ -214,14 +216,14 @@ const AddSigner = (props) => {
className="op-checkbox op-checkbox-sm"
/>
<label htmlFor="addYourself" className="ml-2 mb-0">
Add Yourself
{t("add-yourself")}
</label>
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label htmlFor="name" className="block text-xs font-semibold">
Name
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
@@ -231,27 +233,33 @@ const AddSigner = (props) => {
onChange={(e) => setName(e.target.value)}
required
disabled={addYourself}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="email" className="block text-xs font-semibold">
Email
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
onChange={(e) =>
setEmail(e.target.value?.toLowerCase()?.replace(/\s/g, ""))
}
required
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label htmlFor="phone" className="block text-xs font-semibold">
Phone
{t("phone")}
</label>
<input
type="text"
@@ -264,14 +272,14 @@ const AddSigner = (props) => {
</div>
<div className="mt-4 flex gap-x-2 justify-start">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
<button
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Reset
{t("reset")}
</button>
</div>
</form>
+8 -4
View File
@@ -2,8 +2,10 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const AddTeam = (props) => {
const { t } = useTranslation();
const [formdata, setFormdata] = useState({
name: "",
team: ""
@@ -140,7 +142,7 @@ const AddTeam = (props) => {
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
@@ -148,6 +150,8 @@ const AddTeam = (props) => {
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
@@ -157,7 +161,7 @@ const AddTeam = (props) => {
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Parent Team
{t("report-heading.Parent Team")}
</label>
<select
value={formdata.team}
@@ -175,14 +179,14 @@ const AddTeam = (props) => {
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Reset
{t("reset")}
</div>
</div>
</form>
+330 -176
View File
@@ -2,8 +2,9 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import Title from "./Title";
import Loader from "../primitives/Loader";
import { copytoData } from "../constant/Utils";
import { copytoData, fetchSubscriptionInfo } from "../constant/Utils";
import { isEnableSubscription } from "../constant/const";
import { useTranslation } from "react-i18next";
function generatePassword(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
@@ -17,6 +18,7 @@ function generatePassword(length) {
}
const AddUser = (props) => {
const { t } = useTranslation();
const [formdata, setFormdata] = useState({
name: "",
phone: "",
@@ -25,15 +27,64 @@ const AddUser = (props) => {
password: "",
role: ""
});
const [amount, setAmount] = useState({ quantity: 1, price: 0 });
const [planInfo, setPlanInfo] = useState({
priceperUser: 0,
price: 0,
totalAllowedUser: 0,
adminId: ""
});
const [isFormLoader, setIsFormLoader] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [teamList, setTeamList] = useState([]);
const [allowedUser, setAllowedUser] = useState(0);
const [err, setErr] = useState("");
const role = ["OrgAdmin", "Editor", "User"];
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
useEffect(() => {
getTeamList();
// eslint-disable-next-line
}, []);
const getTeamList = async () => {
if (isEnableSubscription) {
try {
setIsLoader(true);
const resSub = await fetchSubscriptionInfo();
if (!resSub.error) {
setPlanInfo((prev) => ({
...prev,
priceperUser: resSub.price,
totalAllowedUser: resSub.totalAllowedUser,
adminId: resSub.adminId
}));
setAmount((prev) => ({ ...prev, price: resSub.price }));
const res = await Parse.Cloud.run("allowedusers");
if (props.setFormHeader) {
if (res > 0) {
props.setFormHeader(t("add-user"));
} else {
props.setFormHeader(
resSub.adminId !== extUser?.objectId
? "Unauthorized"
: t("Add-seats")
);
}
}
setAllowedUser(res);
} else {
setAllowedUser(0);
}
} catch (err) {
setAllowedUser(0);
console.log("Err in alloweduser", err);
setErr(err.message);
} finally {
setIsLoader(false);
}
}
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
const teamRes = await Parse.Cloud.run("getteams", { active: true });
if (teamRes.length > 0) {
@@ -65,14 +116,12 @@ const AddUser = (props) => {
e.preventDefault();
e.stopPropagation();
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
setIsLoader(true);
setIsFormLoader(true);
const res = await checkUserExist();
if (res) {
props.setIsAlert({ type: "danger", msg: "User already exist." });
setIsLoader(false);
setTimeout(() => {
props.setIsAlert({ type: "success", msg: "" });
}, 1000);
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
setIsFormLoader(false);
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
} else {
try {
const extUser = new Parse.Object("contracts_Users");
@@ -155,14 +204,8 @@ const AddUser = (props) => {
props.handleUserData(parseData);
}
setIsLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
}
} catch (err) {
console.log("err ", err);
@@ -202,20 +245,17 @@ const AddUser = (props) => {
}
props.handleUserData(parseData);
}
setIsLoader(false);
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setIsFormLoader(false);
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
}
}
} catch (err) {
console.log("err", err);
setIsLoader(false);
props.setIsAlert({ type: "danger", msg: "something went wrong." });
setIsFormLoader(false);
props.setIsAlert({
type: "danger",
msg: t("something-went-wrong-mssg")
});
} finally {
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
}
@@ -224,173 +264,287 @@ const AddUser = (props) => {
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setFormdata({
name: "",
email: "",
phone: "",
team: "",
role: ""
});
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
if (props.closePopup) {
props.closePopup();
}
};
const handleChange = (e) => {
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
const handleChange = (event) => {
let { name, value } = event.target;
if (name === "email") {
value = value?.toLowerCase()?.replace(/\s/g, "");
}
setFormdata((prev) => ({ ...prev, [name]: value }));
};
const copytoclipboard = (text) => {
copytoData(text);
props.setIsAlert({ type: "success", msg: "Copied" });
props.setIsAlert({ type: "success", msg: t("copied") });
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
};
const handlePricePerUser = (e) => {
const quantity = e.target.value;
const price = e.target?.value > 0 ? planInfo.priceperUser * quantity : 0;
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
};
const handleAddOnSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsFormLoader(true);
try {
const resAddon = await Parse.Cloud.run("buyaddonusers", {
users: amount.quantity
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
if (_resAddon.status === "success") {
setAllowedUser(amount.quantity);
setPlanInfo((obj) => ({ ...obj, totalAllowedUser: _resAddon.addon }));
}
}
} catch (err) {
console.log("Err in buy addon", err);
props.setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
} finally {
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 2000);
setIsFormLoader(false);
}
};
return (
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
<Title title={"Add User"} />
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box">
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
<Title title={t("add-user")} />
{isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Password
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
></i>
{!isLoader ? (
<>
{err ? (
<div className="flex justify-center items-center h-[70px] text-[18px]">
{err}
</div>
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
Password will only be generated once; make sure to copy it.
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder="optional"
value={formdata.phone}
onChange={(e) => handleChange(e)}
// required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
{isEnableSubscription && (
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Team<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.team}
onChange={(e) => handleChange(e)}
name="team"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
required
>
<option defaultValue={""} value={""}>
select
</option>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
) : (
<div className="w-full mx-auto">
{!isEnableSubscription || allowedUser > 0 ? (
<form onSubmit={handleSubmit}>
{isEnableSubscription && (
<p className="flex justify-center mb-1">
{t("remaing-users")}{" "}
<span
className={`${
allowedUser < 2 ? "op-text-accent" : "op-text-primary"
} font-medium ml-1`}
>
{allowedUser} {t("of")} {planInfo.totalAllowedUser}
</span>
</p>
)}
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={formdata.email}
onChange={(e) => handleChange(e)}
required
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-3">
<label className="block text-xs text-gray-700 font-semibold">
{t("password")}
</label>
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
<div className="break-all">{formdata?.password}</div>
<i
onClick={() => copytoclipboard(formdata?.password)}
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
></i>
</div>
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
{t("password-generateed")}
</div>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("phone")}
{/* <span className="text-[red] text-[13px]"> *</span> */}
</label>
<input
type="text"
name="phone"
placeholder="optional"
value={formdata.phone}
onChange={(e) => handleChange(e)}
// required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
{isEnableSubscription && (
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("team")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<select
name="team"
value={formdata.team}
onChange={(e) => handleChange(e)}
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{teamList.length > 0 &&
teamList.map((x) => (
<option key={x.objectId} value={x.objectId}>
{x.Name}
</option>
))}
</select>
</div>
)}
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{t("Role")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.role}
onChange={(e) => handleChange(e)}
name="role"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
>
<option defaultValue={""} value={""}>
{t("Select")}
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
{t("submit")}
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
{t("cancel")}
</div>
</div>
</form>
) : (
<>
{planInfo.adminId !== extUser?.objectId ? (
<div className="mb-3 mt-1 flex justify-center text-center items-center font-medium break-all">
{t("unauthorized-modal", {
adminName: extUser?.CreatedBy?.name,
adminEmail: extUser?.CreatedBy?.email
})}
</div>
) : (
<form onSubmit={handleAddOnSubmit}>
<p className="flex justify-center text-center mx-2 mb-3 text-base op-text-accent font-medium">
{t("additional-users")}
</p>
<div className="mb-3 flex justify-between">
<label
htmlFor="quantity"
className="block text-xs text-gray-700 font-semibold"
>
{t("Quantity-of-users")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="number"
name="quantity"
value={amount.quantity}
onChange={(e) => handlePricePerUser(e)}
className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
required
/>
</div>
<div className="mb-3 flex justify-between">
<label className="block text-xs text-gray-700 font-semibold">
{t("Price")} (1 * {planInfo.priceperUser})
</label>
<div className="w-1/4 flex justify-center items-center text-sm">
USD {amount.price}
</div>
</div>
<hr className="text-base-content mb-3" />
<button className="op-btn op-btn-primary w-full">
{t("Proceed")}
</button>
</form>
)}
</>
)}
</div>
)}
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Role<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={formdata.role}
onChange={(e) => handleChange(e)}
name="role"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
required
>
<option defaultValue={""} value={""}>
select
</option>
{role.length > 0 &&
role.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button type="submit" className="op-btn op-btn-primary">
Submit
</button>
<div
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Cancel
</div>
</div>
</form>
</div>
</>
) : (
<div className="w-full h-[200px] inset-0 flex justify-center items-center z-50">
<Loader />
</div>
)}
</div>
);
};
+342 -115
View File
@@ -2,8 +2,15 @@ import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import SuggestionInput from "./shared/fields/SuggestionInput";
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 { fetchSubscription } from "../constant/Utils";
import { useNavigate } from "react-router-dom";
const BulkSendUi = (props) => {
const { t } = useTranslation();
const navigate = useNavigate();
const [forms, setForms] = useState([]);
const [formId, setFormId] = useState(2);
const formRef = useRef(null);
@@ -11,6 +18,23 @@ const BulkSendUi = (props) => {
const [isSubmit, setIsSubmit] = useState(false);
const [allowedForm, setAllowedForm] = useState(0);
const [isSignatureExist, setIsSignatureExist] = useState();
const [isBulkAvailable, setIsBulkAvailable] = useState(false);
const quantityList = [500, 1000, 5000, 50000];
const [amount, setAmount] = useState({
price: (75.0).toFixed(2),
quantity: 500,
priceperbulksend: 0.15,
totalcredits: 0
});
const [isQuotaReached, setIsQuotaReached] = useState(false);
const [isLoader, setIsLoader] = useState(false);
const [isFreePlan, setIsFreePlan] = useState(false);
const [admin, setAdmin] = useState({
objectId: "",
name: "",
email: "",
isAdmin: true
});
const allowedSigners = 50;
useEffect(() => {
signatureExist();
@@ -18,14 +42,72 @@ const BulkSendUi = (props) => {
}, []);
//function to check atleast one signature field exist
const signatureExist = () => {
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
const signatureExist = async () => {
if (isEnableSubscription) {
setIsLoader(true);
try {
const subscription = await fetchSubscription();
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (
subscription?.adminId &&
extUser?.objectId === subscription?.adminId
) {
setAdmin((obj) => ({
...obj,
objectId: subscription?.adminId,
isAdmin: true
}));
} else {
setAdmin((obj) => ({
...obj,
isAdmin: false,
name: extUser?.CreatedBy?.name,
email: extUser?.CreatedBy?.email
}));
}
setAdmin((obj) => ({ ...obj, objectId: subscription?.adminId }));
if (subscription?.plan === "freeplan") {
setIsFreePlan(true);
}
const resCredits = await Parse.Cloud.run("allowedcredits");
if (resCredits) {
const allowedcredits = resCredits?.allowedcredits || 0;
const addoncredits = resCredits?.addoncredits || 0;
const totalcredits = allowedcredits + addoncredits;
if (totalcredits > 0) {
if (subscription?.plan !== "freeplan") {
setIsBulkAvailable(true);
}
}
setAmount((obj) => ({ ...obj, totalcredits: totalcredits }));
}
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
setIsLoader(false);
} catch (err) {
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
console.log("Err", err);
}
} else {
setIsBulkAvailable(true);
setAdmin((obj) => ({ ...obj, isAdmin: true }));
const getPlaceholder = props.item?.Placeholders;
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
placeholderObj?.placeHolder?.some((holder) =>
holder?.pos?.some((posItem) => posItem?.type === "signature")
)
);
setIsSignatureExist(checkIsSignatureExistt);
setIsLoader(false);
}
};
useEffect(() => {
if (scrollOnNextUpdate && formRef.current) {
@@ -74,29 +156,33 @@ const BulkSendUi = (props) => {
const handleAddForm = (e) => {
e.preventDefault();
// Check if the quick send limit has been reached
if (forms?.length < allowedForm) {
if (props?.Placeholders.length > 0) {
let newForm = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
newForm = [
...newForm,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms([...forms, { Id: formId, fields: newForm }]);
}
setFormId(formId + 1);
setScrollOnNextUpdate(true);
if (isEnableSubscription && forms.length >= amount.totalcredits) {
setIsQuotaReached(true);
} else {
// If the limit has been reached, throw an error with the appropriate message
alert("Quick send reached limit.");
if (forms?.length < allowedForm) {
if (props?.Placeholders.length > 0) {
let newForm = [];
props?.Placeholders?.forEach((element) => {
if (!element.signerObjId) {
newForm = [
...newForm,
{
fieldId: element.Id,
email: "",
label: element.Role,
signer: {}
}
];
}
});
setForms([...forms, { Id: formId, fields: newForm }]);
}
setFormId(formId + 1);
setScrollOnNextUpdate(true);
} else {
// If the limit has been reached, throw an error with the appropriate message
alert(t("quick-send-alert-4"));
}
}
};
@@ -160,13 +246,9 @@ const BulkSendUi = (props) => {
: [...existSigner]
});
} else {
Documents.push({
...props.item,
Placeholders: updatedPlaceholders
});
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
}
});
//console.log("Documents ", Documents);
await batchQuery(Documents);
};
@@ -178,9 +260,7 @@ const BulkSendUi = (props) => {
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = {
Documents: JSON.stringify(Documents)
};
const params = { Documents: JSON.stringify(Documents) };
try {
const res = await axios.post(functionsUrl, params, { headers: headers });
// console.log("res ", res);
@@ -194,87 +274,234 @@ const BulkSendUi = (props) => {
setIsSubmit(false);
}
};
const handleAddOnQuickSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsSubmit(true);
try {
const resAddon = await Parse.Cloud.run("buycredits", {
credits: amount.quantity
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
if (_resAddon.status === "success") {
setIsBulkAvailable(true);
handleCloseQuotaReached();
setAmount((obj) => ({
...obj,
quantity: 500,
priceperbulksend: 0.15,
price: (75.0).toFixed(2),
totalcredits: obj?.totalcredits + _resAddon.addon
}));
}
}
} catch (err) {
console.log("Err in buy addon", err);
alert(t("something-went-wrong-mssg"));
} finally {
setIsSubmit(false);
}
};
const handlePricePerQuick = async (e) => {
const quantity = e.target?.value;
const price =
quantity > 0
? (Math.round(quantity * amount.priceperbulksend * 100) / 100).toFixed(
2
)
: 500 * amount.priceperbulksend;
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
};
const handleCloseQuotaReached = () => {
setIsQuotaReached(false);
};
const handleNavigation = () => {
navigate("/subscription");
};
return (
<>
{isSubmit && (
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
{isLoader ? (
<div className="w-full h-[100px] flex justify-center items-center z-[999]">
<Loader />
</div>
)}
{props.Placeholders?.length > 0 ? (
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<form onSubmit={handleSubmit}>
<div className="min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div className="flex flex-col" key={field.fieldId}>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(index, signer, fieldIndex)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
>
<i className="fa-light fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-light fa-plus"></i> <span>Add new</span>
</button>
<button
type="submit"
className="op-btn op-btn-secondary focus:outline-none"
>
<i className="fa-light fa-paper-plane"></i>{" "}
<span>Send</span>
</button>
</div>
</form>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
All roles in this document are currently linked to contacts. To
&apos;quick send&apos; copies of this template to multiple
signers, please ensure that at least one role is not linked to
any contact.
</div>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please ensure there&#39;s at least one signature widget added for
all recipients.
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
Please add at least one role to this template in order to &apos;quick
send&apos; copies of it to multiple signers.
</div>
<>
{isSubmit && (
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
{isBulkAvailable ? (
<>
{props.Placeholders?.length > 0 ? (
isSignatureExist ? (
<>
{props.Placeholders?.some((x) => !x.signerObjId) ? (
<div>
<form onSubmit={handleSubmit}>
<div className="min-h-max max-h-[250px] overflow-y-auto">
{forms?.map((form, index) => (
<div
key={form.Id}
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
>
{form?.fields?.map((field, fieldIndex) => (
<div
className="flex flex-col"
key={field.fieldId}
>
<label>{field.label}</label>
<SuggestionInput
required
type="email"
value={field.value}
index={fieldIndex}
onChange={(signer) =>
handleInputChange(
index,
signer,
fieldIndex
)
}
/>
</div>
))}
{forms?.length > 1 && (
<button
onClick={() => handleRemoveForm(index)}
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
>
<i className="fa-light fa-trash"></i>
</button>
)}
<div ref={formRef}></div>
</div>
))}
</div>
<div className="flex flex-col mx-4 mb-4 gap-3">
{isEnableSubscription && (
<button
onClick={handleAddForm}
className="op-btn op-btn-primary focus:outline-none"
>
<i className="fa-light fa-plus"></i>{" "}
<span>{t("add-new")}</span>
</button>
)}
<button
type="submit"
className="op-btn op-btn-accent focus:outline-none"
>
<i className="fa-light fa-paper-plane"></i>{" "}
<span>{t("send")}</span>
</button>
</div>
</form>
<ModalUi
isOpen={isQuotaReached}
handleClose={() => handleCloseQuotaReached()}
>
<div className="p-4 flex justify-center items-center flex-col gap-y-3">
<p className="text-center text-base-content">
{t("quota-err-quicksend")}
</p>
<button
onClick={() => setIsBulkAvailable(false)}
className=" op-btn op-btn-primary w-[200px]"
>
{t("buy-credits")}
</button>
</div>
</ModalUi>
</div>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
{t("quick-send-alert-1")}
</div>
)}
</>
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
{t("quick-send-alert-2")}
</div>
)
) : (
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
{t("quick-send-alert-3")}
</div>
)}
</>
) : (
<>
{isFreePlan ? (
<div className="w-full h-[130px] flex flex-col justify-center items-center text-center p-4">
<p className="text-base font-medium mb-2.5">
{t("bulk-send-subcription-alert")}
</p>
<button
onClick={() => handleNavigation()}
className="op-btn op-btn-primary"
>
{t("upgrade-now")}
</button>
</div>
) : (
<>
{admin?.isAdmin ? (
<form onSubmit={handleAddOnQuickSubmit} className="p-3">
<p className="flex justify-center text-center mx-2 mb-3 text-base op-text-accent font-medium">
{t("additional-credits")}
</p>
<div className="mb-3 flex justify-between">
<label
htmlFor="quantity"
className="block text-xs text-gray-700 font-semibold"
>
{t("quantity-of-credits")}
<span className="text-[red] text-[13px]">*</span>
</label>
<select
value={amount.quantity}
onChange={(e) => handlePricePerQuick(e)}
name="quantity"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-1/4 text-xs"
required
>
{quantityList.length > 0 &&
quantityList.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="mb-3 flex justify-between">
<label className="block text-xs text-gray-700 font-semibold">
{t("Price")} (1 * {amount.priceperbulksend})
</label>
<div className="w-1/4 flex justify-center items-center text-sm">
USD {amount.price}
</div>
</div>
<hr className="text-base-content mb-3" />
<button className="op-btn op-btn-primary w-full mt-2">
{t("Proceed")}
</button>
</form>
) : (
<div className="mx-8 mt-4 mb-8 flex justify-center text-center items-center font-medium break-all">
{t("unauthorized-modal", {
adminName: admin?.name,
adminEmail: admin?.email
})}
</div>
)}
</>
)}
</>
)}
</>
)}
</>
);
+5 -2
View File
@@ -2,7 +2,9 @@ import React, { useEffect, useState } from "react";
import Package from "../../package.json";
import axios from "axios";
import { openInNewTab } from "../constant/Utils";
import { useTranslation } from "react-i18next";
const Footer = () => {
const { t } = useTranslation();
const [showButton, setShowButton] = useState(false);
const [version, setVersion] = useState("");
useEffect(() => {
@@ -48,9 +50,10 @@ const Footer = () => {
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
<aside>
<p>
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
{t("all-right")} &copy; {new Date().getFullYear()} &nbsp;
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
{appName} ( {t("version")}:{" "}
{version ? version : `${Package.version} `})
</span>
</p>
</aside>
+167 -129
View File
@@ -6,24 +6,30 @@ import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize";
import {
checkIsSubscribed,
checkIsSubscribedTeam,
getAppLogo,
openInNewTab
openInNewTab,
saveLanguageInLocal
} from "../constant/Utils";
import { isEnableSubscription, isStaging } from "../constant/const";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import ModalUi from "../primitives/ModalUi";
import QuotaCard from "../primitives/QuotaCard";
const Header = ({ showSidebar }) => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const { width } = useWindowSize();
let username = localStorage.getItem("username");
const username = localStorage.getItem("username") || "";
const image = localStorage.getItem("profileImg") || dp;
const [isOpen, setIsOpen] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(true);
const [isPro, setIsPro] = useState(false);
const [isTeam, setIsTeam] = useState(false);
const [isTeam, setIsTeam] = useState({ plan: "", isValid: false });
const [applogo, setAppLogo] = useState(
localStorage.getItem("appLogo") || " "
);
const [emailUsed, setEmailUsed] = useState(0);
const [isModal, setIsModal] = useState(false);
const toggleDropdown = () => {
setIsOpen(!isOpen);
@@ -34,9 +40,8 @@ const Header = ({ showSidebar }) => {
}, []);
async function checkSubscription() {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
const getIsTeam = await checkIsSubscribedTeam();
if (getIsSubscribe) {
const subscribe = await checkIsSubscribed();
if (subscribe.isValid) {
const applogo = await getAppLogo();
if (applogo?.logo) {
setAppLogo(applogo?.logo);
@@ -44,20 +49,24 @@ const Header = ({ showSidebar }) => {
setAppLogo(localStorage.getItem("appLogo") || "");
}
}
setIsPro(getIsSubscribe);
setIsTeam(getIsTeam);
setIsSubscribe(getIsSubscribe);
setIsTeam(subscribe);
setIsSubscribe(subscribe.isValid);
try {
const extUser = await Parse.Cloud.run("getUserDetails");
const MonthlyFreeEmails = extUser?.get("MonthlyFreeEmails") || 0;
setEmailUsed(MonthlyFreeEmails);
} catch (err) {
console.log("err in while fetching monthlyfreeEmails", err);
}
}
}
const closeDropdown = () => {
const closeDropdown = async () => {
setIsOpen(false);
if (Parse?.User?.current()) {
try {
Parse.User.logOut();
} catch (err) {
console.log("Err", err);
}
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
@@ -67,7 +76,7 @@ const Header = ({ showSidebar }) => {
let appid = localStorage.getItem("parseAppId");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
@@ -100,128 +109,157 @@ const Header = ({ showSidebar }) => {
window.open("https://console.opensignlabs.com/");
}
};
const handleNavigation = () => {
navigate("/subscription");
};
const handleMailUsed = () => {
setIsModal(!isModal);
};
return (
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
<button
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
onClick={showSidebar}
>
<i className="fa-light fa-bars text-xl text-base-content"></i>
</button>
</div>
<div className="flex-1 ml-2">
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
<img
className="object-contain h-full w-auto"
src={applogo}
alt="img"
/>
</div>
</div>
<div id="profile-menu" className="flex-none gap-2">
{!isSubscribe && (
<div>
<button
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
onClick={() => navigate("/subscription")}
>
Upgrade Now
</button>
</div>
)}
{!isTeam && isPro && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
PRO
</div>
)}
{isTeam && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
TEAM
</div>
)}
<div>
<FullScreenButton />
</div>
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
<div>
{isEnableSubscription && (
<div className="shadow py-1 text-center bg-[#CAE4FA] text-[14px] p-1">
{t("header-news")}
<span
className="cursor-pointer font-medium underline text-blue-800"
onClick={() => navigate("/profile")}
>
{" " + t("header-news-btn") + "."}
</span>
</div>
)}
<div className="op-navbar bg-base-100 shadow">
<div className="flex-none">
<button
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
onClick={showSidebar}
>
<i className="fa-light fa-bars text-xl text-base-content"></i>
</button>
</div>
<div className="flex-1 ml-2">
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
<img
className="w-[35px] h-[35px] object-contain"
src={image}
className="object-contain h-full w-auto"
src={applogo}
alt="img"
/>
</div>
)}
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer text-base-content text-sm"
>
{username && username}
</div>
<div id="profile-menu" className="flex-none gap-2">
{!isSubscribe && (
<div>
<button
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
onClick={() => handleNavigation()}
>
{t("upgrade-now")}
</button>
</div>
)}
{isTeam.isValid && !validplan[isTeam.plan] && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
{t("pro")}
</div>
)}
{validplan[isTeam.plan] && (
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
{t("team")}
</div>
)}
<div>
<FullScreenButton />
</div>
)}
<div className="op-dropdown op-dropdown-end" id="profile-menu">
<div
tabIndex={0}
role="button"
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
>
<i
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
>
<img
className="w-[35px] h-[35px] object-contain"
src={image}
alt="img"
/>
</div>
)}
{width >= 768 && (
<div
onClick={toggleDropdown}
className="cursor-pointer text-base-content text-sm"
>
{username && username}
</div>
)}
<div className="op-dropdown op-dropdown-end" id="profile-menu">
<div
tabIndex={0}
role="button"
onClick={toggleDropdown}
className="fa-light fa-angle-down text-base-content"
></i>
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
>
<i
tabIndex={0}
role="button"
onClick={toggleDropdown}
className="fa-light fa-angle-down text-base-content"
></i>
</div>
<ul
tabIndex={0}
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
>
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
<span>
<i className="fa-light fa-book"></i> {t("docs")}
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<span>
<i className="fa-light fa-user"></i> {t("profile")}
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<span>
<i className="fa-light fa-lock"></i> {t("change-password")}
</span>
</li>
{isEnableSubscription && (
<li onClick={() => handleConsoleRedirect()}>
<span>
<i className="fa-light fa-id-card"></i> Console
</span>
</li>
)}
{isEnableSubscription && isTeam?.plan === "freeplan" && (
<li className="cursor-pointer" onClick={handleMailUsed}>
<span>
<i className="fa-light fa-envelope"></i>
{emailUsed}/15 {t("sent-this-month")}
</span>
</li>
)}
<li onClick={closeDropdown}>
<span>
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
{t("log-out")}
</span>
</li>
</ul>
</div>
<ul
tabIndex={0}
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
>
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
<span>
<i className="fa-light fa-book"></i> Docs
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/profile");
}}
>
<span>
<i className="fa-light fa-user"></i> Profile
</span>
</li>
<li
onClick={() => {
setIsOpen(false);
navigate("/changepassword");
}}
>
<span>
<i className="fa-light fa-lock"></i> Change Password
</span>
</li>
<li
onClick={() => {
handleConsoleRedirect();
}}
>
<span>
<i className="fa-light fa-id-card"></i> Console
</span>
</li>
<li onClick={closeDropdown}>
<span>
<i className="fa-light fa-arrow-right-from-bracket"></i> Log Out
</span>
</li>
</ul>
</div>
</div>
<ModalUi isOpen={isModal}>
<QuotaCard isPaidInfo={true} handlClose={handleMailUsed} />
</ModalUi>
</div>
);
};
+19 -7
View File
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import Parse from "parse";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
// import FacebookLogin from "react-facebook-login";
const LoginFacebook = ({
@@ -9,6 +10,7 @@ const LoginFacebook = ({
thirdpartyLoader,
setThirdpartyLoader
}) => {
const { t } = useTranslation();
const [isModal, setIsModal] = useState(false);
const [userDetails, setUserDetails] = useState({
Phone: "",
@@ -106,10 +108,10 @@ const LoginFacebook = ({
alert(userSignUp.message);
}
} else {
alert("Internal server error !");
alert(t("server-error"));
}
} else {
alert("Please fill required details!");
alert(t("fill-required-details!"));
}
};
return (
@@ -136,7 +138,7 @@ const LoginFacebook = ({
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Sign up form</h5>
<h5 className="modal-title">{t("sign-up-form")}</h5>
<span>
<span></span>
</span>
@@ -145,7 +147,8 @@ const LoginFacebook = ({
<form>
<div className="form-group">
<label htmlFor="Phone" className="col-form-label">
Phone <span className="text-[red] text-[13px]">*</span>
{t("phone")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
@@ -158,12 +161,17 @@ const LoginFacebook = ({
Phone: e.target.value
})
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="form-group">
<label htmlFor="Company" className="col-form-label">
Company <span className="text-[red] text-[13px]">*</span>
{t("company")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
@@ -176,6 +184,10 @@ const LoginFacebook = ({
Company: e.target.value
})
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -185,14 +197,14 @@ const LoginFacebook = ({
onClick={() => handleSubmitbtn()}
className="btn btn-info mr-[10px]"
>
Sign up
{t("sign-up")}
</button>
<button
type="button"
className="btn btn-secondary w-[90px]"
onClick={() => setIsModal(false)}
>
Cancel
{t("cancel")}
</button>
</div>
</form>
+33 -30
View File
@@ -4,7 +4,8 @@ import { jwtDecode } from "jwt-decode";
import { useScript } from "../hook/useScript";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import { saveLanguageInLocal } from "../constant/Utils";
/*
* `GoogleSignInBtn`as it's name indicates it render google sign in button
* and in this `useScript` in which we have created for generate google sign button
@@ -17,6 +18,7 @@ const GoogleSignInBtn = ({
thirdpartyLoader,
setThirdpartyLoader
}) => {
const { t, i18n } = useTranslation();
const [isModal, setIsModal] = useState(false);
const googleBtn = useRef();
const [userDetails, setUserDetails] = useState({
@@ -45,8 +47,10 @@ const GoogleSignInBtn = ({
});
});
const clearStorage = async () => {
if (Parse.User.current()) {
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
@@ -56,6 +60,7 @@ const GoogleSignInBtn = ({
let userSettings = localStorage.getItem("userSettings");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
@@ -74,10 +79,7 @@ const GoogleSignInBtn = ({
const data = jwtDecode(response.credential);
// console.log("data ", data);
if (data.sub && data.email) {
const details = {
Email: data.email,
Name: data.name
};
const details = { Email: data.email, Name: data.name };
setUserDetails({ ...userDetails, ...details });
const Gdetails = {
Id: data.sub,
@@ -91,26 +93,18 @@ const GoogleSignInBtn = ({
}
};
const checkExtUser = async (details) => {
// const extUser = new Parse.Query("contracts_Users");
// extUser.equalTo("Email", details.Gmail);
// const extRes = await extUser.first();
const params = { email: details.Gmail };
const extRes = await Parse.Cloud.run("getUserDetails", params);
// console.log("extRes ", extRes);
if (extRes) {
const params = { ...details, Phone: extRes?.get("Phone") || "" };
// const params = { ...details, Phone: extRes?.get("Phone") || "" };
const params = { ...details, extUserId: extRes.objectId };
const payload = await Parse.Cloud.run("googlesign", params);
// console.log("payload ", payload);
if (payload && payload.sessiontoken) {
// setThirdpartyLoader(true);
// const billingDate =
// extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
// console.log("billingDate expired", billingDate > new Date());
const LocalUserDetails = {
name: details.Name,
email: details.Gmail,
phone: extRes?.get("Phone") || "",
company: extRes.get("Company")
phone: payload?.phone || "",
company: payload.company
};
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
thirdpartyLoginfn(payload.sessiontoken);
@@ -162,16 +156,19 @@ const GoogleSignInBtn = ({
payload &&
payload.message.replace(/ /g, "_") === "Internal_server_err"
) {
alert("Internal server error !");
alert(t("server-error"));
}
} else {
alert("Please fill required details!");
alert(t("fill-required-details!"));
}
};
const handleCloseModal = () => {
const handleCloseModal = async () => {
setIsModal(false);
Parse.User.logOut();
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let defaultmenuid = localStorage.getItem("defaultmenuid");
@@ -180,7 +177,7 @@ const GoogleSignInBtn = ({
let appid = localStorage.getItem("parseAppId");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
@@ -196,11 +193,11 @@ const GoogleSignInBtn = ({
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<ModalUi showClose={false} isOpen={isModal} title={t("sign-up-form")}>
<form className="px-4 py-3 text-base-content">
<div className="mb-3">
<label htmlFor="Phone" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
{t("phone")} <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="tel"
@@ -213,12 +210,14 @@ const GoogleSignInBtn = ({
Phone: e.target.value
})
}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
{t("company")} <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
@@ -231,12 +230,14 @@ const GoogleSignInBtn = ({
Company: e.target.value
})
}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title <span className="text-[13px] text-[red]">*</span>
{t("job-title")} <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
@@ -249,6 +250,8 @@ const GoogleSignInBtn = ({
Destination: e.target.value
})
}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -258,14 +261,14 @@ const GoogleSignInBtn = ({
className="op-btn op-btn-primary"
onClick={() => handleSubmitbtn()}
>
Sign up
{t("sign-up")}
</button>
<button
type="button"
className="op-btn op-btn-ghost"
onClick={handleCloseModal}
>
Cancel
{t("cancel")}
</button>
</div>
</form>
@@ -0,0 +1,38 @@
import React from "react";
import ModalUi from "../primitives/ModalUi";
import { useTranslation } from "react-i18next";
function RotateAlert(props) {
const { t } = useTranslation();
return (
<ModalUi
isOpen={props.isRotate}
title={t("Rotation-alert")}
handleClose={() => props.setIsRotate({ status: false, degree: 0 })}
>
{" "}
<div className="p-[20px] h-full">
<p>{t("rotate-alert-mssg")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<div className="flex gap-1">
<button
onClick={() => props.setIsRotate({ status: false, degree: 0 })}
type="button"
className="op-btn op-btn-ghost shadow-md"
>
{t("no")}
</button>
<button
onClick={() => props.handleRemoveWidgets()}
type="button"
className="op-btn op-btn-primary"
>
{t("yes")}
</button>
</div>
</div>
</ModalUi>
);
}
export default RotateAlert;
@@ -1,9 +1,11 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
const DashboardButton = (props) => {
const navigate = useNavigate();
const { t } = useTranslation();
function openReport() {
if (props.Data && props.Data.Redirect_type) {
@@ -35,7 +37,9 @@ const DashboardButton = (props) => {
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className=" text-lg ml-3 text-base-content">{props.Label}</div>
<div className=" text-lg ml-3 text-base-content">
{t(`sidebar.${props.Label}`)}
</div>
</div>
</div>
);
@@ -4,9 +4,11 @@ import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
import { useNavigate } from "react-router-dom";
import Tooltip from "../../primitives/Tooltip";
import { useTranslation } from "react-i18next";
const DashboardCard = (props) => {
const navigate = useNavigate();
const { t } = useTranslation();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [response, setresponse] = useState("");
@@ -311,6 +313,7 @@ const DashboardCard = (props) => {
}
}
}
return (
<div
onClick={() => openReport()}
@@ -328,8 +331,11 @@ const DashboardCard = (props) => {
} text-[25px] lg:text-[30px]`}
></i>
</span>
<div className="font-medium">
<div className="text-base lg:text-lg"> {props.Label}</div>
<div className="text-base lg:text-lg">
{t(`dashboard-card.${props.Label}`)}
</div>
<div className="text-2xl font-light">
{loading ? <div className="loader-01"></div> : setFormat(response)}
</div>
@@ -339,7 +345,8 @@ const DashboardCard = (props) => {
<Tooltip
id={props.Label}
iconColor={"white"}
message={props?.Data?.tourMessage}
message={t(`tour-mssg.${props.Label}`)}
// {props?.Data?.tourMessage}
/>
</div>
</div>
@@ -4,7 +4,9 @@ import ReportTable from "../../primitives/GetReportDisplay";
import reportJson from "../../json/ReportJson";
import axios from "axios";
import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next";
function DashboardReport(props) {
const { t } = useTranslation();
// console.log("props ", props)
const [List, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true);
@@ -141,7 +143,7 @@ function DashboardReport(props) {
) : (
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
<div className="text-center">
<p className="text-xl text-black">Report Not Found</p>
<p className="text-xl text-black">{t("report-not-found")}</p>
</div>
</div>
)}
@@ -1,4 +1,5 @@
import React, { Suspense, lazy } from "react";
import { useTranslation } from "react-i18next";
const DashboardButton = lazy(() => import("./DashboardButton"));
const DashboardCard = lazy(() => import("./DashboardCard"));
const DashboardReport = lazy(() => import("./DashboardReport"));
@@ -10,13 +11,14 @@ const buttonList = [
icon: "fa-light fa-pen-nib"
},
{
label: "Request signature",
label: "Request signatures",
redirectId: "8mZzFxbG1z",
redirectType: "Form",
icon: "fa-light fa-paper-plane"
}
];
const GetDashboard = (props) => {
const { t } = useTranslation();
const Button = ({ label, redirectId, redirectType, icon }) => (
<DashboardButton
Icon={icon}
@@ -37,7 +39,7 @@ const GetDashboard = (props) => {
<Suspense
fallback={
<div className="h-[150px] w-full flex justify-center items-center">
loading...
{t("loading")}
</div>
}
>
@@ -8,8 +8,10 @@ import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import ModalUi from "../../primitives/ModalUi";
import FolderModal from "../shared/fields/FolderModal";
import { useTranslation } from "react-i18next";
function DriveBody(props) {
const { t } = useTranslation();
const [rename, setRename] = useState("");
const [renameValue, setRenameValue] = useState("");
const inputRef = useRef(null);
@@ -42,7 +44,7 @@ function DriveBody(props) {
props.setFolderName((prev) => [...prev, folderData]);
props.setIsLoading({
isLoad: true,
message: "This might take some time"
message: t("loading-mssg")
});
props.setDocId(data.objectId);
props.setPdfData([]);
@@ -90,7 +92,7 @@ function DriveBody(props) {
console.log("Err ", err);
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage: t("something-went-wrong-mssg")
});
});
}
@@ -191,7 +193,7 @@ function DriveBody(props) {
console.log("Err ", err);
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage: t("something-went-wrong-mssg")
});
});
};
@@ -258,7 +260,7 @@ function DriveBody(props) {
setIsOpenMoveModal(false);
} else {
alert("folder already exist!");
alert(t("folder-already-exist!"));
setIsOpenMoveModal(false);
}
};
@@ -348,14 +350,14 @@ function DriveBody(props) {
</td>
<td>{createddate}</td>
<td>Pdf</td>
<td>{status}</td>
<td>{t(`drive-document-status.${status}`)}</td>
<td>
<i
onClick={(e) => {
e.stopPropagation();
handleMenuItemClick("Download", data);
}}
className="fa-light fa-download mr-[8px] op-text-primary"
className="fa-light fa-download mr-[8px] op-text-primary cursor-pointer"
aria-hidden="true"
></i>
</td>
@@ -368,7 +370,11 @@ function DriveBody(props) {
{/* folder */}
<div
data-tut={props.dataTutSeventh}
onClick={() => handleOnclikFolder(data)}
onClick={() => {
if (!rename) {
handleOnclikFolder(data);
}
}}
className="cursor-pointer"
>
<svg
@@ -381,7 +387,6 @@ function DriveBody(props) {
{rename === data.objectId ? (
<input
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
@@ -393,9 +398,8 @@ function DriveBody(props) {
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
/>
) : (
<span className="fileName">{data.Name}</span>
@@ -421,7 +425,11 @@ function DriveBody(props) {
</ContextMenu.Root>
</div>
) : (
<HoverCard.Root openDelay={0} closeDelay={100}>
<HoverCard.Root
open={rename ? false : undefined}
openDelay={0}
closeDelay={100}
>
<HoverCard.Trigger asChild>
<div>
<ContextMenu.Root>
@@ -430,7 +438,11 @@ function DriveBody(props) {
{/* pdf */}
<div
data-tut={props.dataTutSixth}
onClick={() => checkPdfStatus(data)}
onClick={() => {
if (!rename) {
checkPdfStatus(data);
}
}}
className="cursor-pointer"
>
<svg
@@ -445,7 +457,6 @@ function DriveBody(props) {
autoFocus={true}
type="text"
onFocus={() => {
inputRef.current.setSelectionRange(0, 0);
const input = inputRef.current;
if (input) {
input.select();
@@ -455,9 +466,8 @@ function DriveBody(props) {
onKeyDown={(e) => handleEnterPress(e, data)}
ref={inputRef}
defaultValue={renameValue}
// value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
/>
) : (
<span className="fileName">{data.Name}</span>
@@ -503,7 +513,9 @@ function DriveBody(props) {
className="ContextMenuItem"
>
<i className={menu.icon}></i>
<span className="ml-[8px]">{menu.type}</span>
<span className="ml-[8px]">
{t(`context-menu.${menu.type}`)}
</span>
</ContextMenu.Item>
);
})}
@@ -514,18 +526,28 @@ function DriveBody(props) {
</HoverCard.Trigger>
<HoverCard.Portal>
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
<strong className="text-[13px]">Title: </strong>
<strong className="text-[13px]">
{t("report-heading.Title")}:{" "}
</strong>
<span className="text-[12px] font-medium mb-0"> {data.Name}</span>
<br />
<strong className="text-[13px]">Status: </strong>
<span className="text-[12px] font-medium"> {status}</span>
<strong className="text-[13px]">
{t("report-heading.Status")}:{" "}
</strong>
<span className="text-[12px] font-medium">
{t(`drive-document-status.${status}`)}
</span>
<br />
<strong className="text-[13px]">Created Date: </strong>
<strong className="text-[13px]">
{t("report-heading.created-date")}:{" "}
</strong>
<span className="text-[12px] font-medium">{createddate}</span>
<br />
{signerExist && (
<>
<strong className="text-[13px]">Signers: </strong>
<strong className="text-[13px]">
{t("report-heading.Signers")}:{" "}
</strong>
{signersName()}
</>
)}
@@ -545,11 +567,11 @@ function DriveBody(props) {
<Table striped bordered hover>
<thead>
<tr>
<th>Name</th>
<th>Created Date</th>
<th>Type</th>
<th>Status</th>
<th>Action</th>
<th>{t("report-heading.Name")}</th>
<th>{t("report-heading.created-date")}</th>
<th>{t("report-heading.Type")}</th>
<th>{t("report-heading.Status")}</th>
<th>{t("action")}</th>
</tr>
</thead>
<tbody>
@@ -582,25 +604,25 @@ function DriveBody(props) {
)}
<ModalUi
isOpen={isDeleteDoc}
title={"Delete Document"}
title={t("delete-document")}
handleClose={() => setIsDeleteDoc(false)}
>
<div className="h-full p-[20px] text-base-content">
<p>Are you sure you want to delete this document?</p>
<p>{t("delete-document-alert")}</p>
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
<button
onClick={() => handleDeleteDocument(selectDoc)}
type="button"
className="op-btn op-btn-primary mr-2"
>
Yes
{t("yes")}
</button>
<button
onClick={() => setIsDeleteDoc(false)}
type="button"
className="op-btn op-btn-neutral"
>
No
{t("no")}
</button>
</div>
</ModalUi>
@@ -1,10 +1,12 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
const AddRoleModal = (props) => {
const { t } = useTranslation();
return (
<ModalUi
title={"Add Role"}
title={t("add-role")}
isOpen={props.isModalRole}
handleClose={props.handleCloseRoleModal}
>
@@ -21,19 +23,19 @@ const AddRoleModal = (props) => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
/>
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
e.g: Customer, Hr, Director, Manager, Student, etc...
{t("role-ex")}..
</p>
<div>
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
<button type="submit" className="op-btn op-btn-primary">
Add
{t("add")}
</button>
<button
onClick={props.handleCloseRoleModal}
type="button"
className="op-btn op-btn-ghost ml-2"
>
Close
{t("close")}
</button>
</div>
</form>
@@ -1,16 +1,18 @@
import React from "react";
import { useTranslation } from "react-i18next";
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
const { t } = useTranslation();
const confirmToaddDefaultSign = () => {
if (xyPostion.length > 0) {
setDefaultSignAlert({
isShow: true,
alertMessage:
"Are you sure you want to auto sign at requested all locations?"
alertMessage: t("default-sign-alert")
});
} else {
setDefaultSignAlert({
isShow: true,
alertMessage: "please select position!"
alertMessage: t("please-select-position!")
});
}
};
@@ -18,10 +20,10 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
return (
<div data-tut="reactourThird">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signature
{t("signature")}
</div>
<div className="flex flex-col items-center mt-[10px] font-semibold">
<p className="text-base-content">Your Signature</p>
<p className="text-base-content">{t("Your-Signature")}</p>
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
<img
alt="signature"
@@ -34,7 +36,7 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
onClick={() => confirmToaddDefaultSign()}
>
Auto Sign All
{t("auto-sign-all")}
</button>
</div>
</div>
@@ -3,16 +3,18 @@ import LoaderWithMsg from "../../primitives/LoaderWithMsg";
import { contractDocument } from "../../constant/Utils";
import HandleError from "../../primitives/HandleError";
import { useLocation, useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
function DraftDocument() {
const { t } = useTranslation();
const navigate = useNavigate();
const query = useQuery();
const docId = query.get("docId");
const [isLoading, setIsLoading] = useState({
isLoader: true,
message: "This might take some time"
message: t("loading-mssg")
});
useEffect(() => {
getDocumentDetails();
@@ -52,7 +54,7 @@ function DraftDocument() {
navigate(`/recipientSignPdf/${data.objectId}`);
}
//checking if document has completed and signyour-self flow
else if (!signerExist && !isPlaceholder) {
else if ((!signerExist && !isPlaceholder) || data?.IsSignyourself) {
navigate(`/signaturePdf/${data.objectId}`);
}
//checking if document has declined by someone
@@ -3,7 +3,11 @@ import { isEnableSubscription } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import Upgrade from "../../primitives/Upgrade";
import { useTranslation } from "react-i18next";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
function DropdownWidgetOption(props) {
const { t } = useTranslation();
const [dropdownOptionList, setDropdownOptionList] = useState([
"option-1",
"option-2"
@@ -155,9 +159,12 @@ function DropdownWidgetOption(props) {
>
<div>
<label className="text-[13px] font-semibold">
Name<span className="text-[red] text-[13px]"> *</span>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
defaultValue={dropdownName}
value={dropdownName}
@@ -166,7 +173,7 @@ function DropdownWidgetOption(props) {
/>
<label className="text-[13px] font-semibold mt-[5px]">
Options
{t("options")}
</label>
<div className="flex flex-col">
{dropdownOptionList.map((option, index) => (
@@ -196,6 +203,10 @@ function DropdownWidgetOption(props) {
/>
)}
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
type="text"
@@ -222,12 +233,16 @@ function DropdownWidgetOption(props) {
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
Minimun check
{t("minimun-check")}
</label>
{!props.isSubscribe && isEnableSubscription && (
<Upgrade />
)}
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
defaultValue={0}
value={minCount}
@@ -245,9 +260,13 @@ function DropdownWidgetOption(props) {
!props.isSubscribe ? "text-[gray]" : ""
} text-[13px] font-semibold`}
>
Maximum check
{t("maximum-check")}
</label>
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
defaultValue={0}
value={maxCount}
@@ -268,7 +287,7 @@ function DropdownWidgetOption(props) {
{["dropdown", radioButtonWidget].includes(props.type) && (
<>
<label className="text-[13px] font-semibold mt-[5px]">
Default value
{t("default-value")}
</label>
<select
onChange={(e) => setDefaultValue(e.target.value)}
@@ -278,7 +297,7 @@ function DropdownWidgetOption(props) {
placeholder="select default value"
>
<option value="" disabled hidden className="text-[13px]">
Select...
{t("select")}...
</option>
{dropdownOptionList.map((data, ind) => {
return (
@@ -313,6 +332,55 @@ function DropdownWidgetOption(props) {
</div>
</>
)}
<div className="flex items-center mt-3 mb-3">
<span>{t("font-size")} :</span>
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
props.fontSize ||
props.currWidgetsDetails?.options?.fontSize ||
"12"
}
onChange={(e) => props.setFontSize(e.target.value)}
>
{fontsizeArr.map((size, ind) => {
return (
<option className="text-[13px]" value={size} key={ind}>
{size}
</option>
);
})}
</select>
<div className="flex flex-row gap-1 items-center ml-4 ">
<span>{t("color")} : </span>
<select
value={
props.fontColor ||
props.currWidgetsDetails?.options?.fontColor ||
"black"
}
onChange={(e) => props.setFontColor(e.target.value)}
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
>
{fontColorArr.map((color, ind) => {
return (
<option value={color} key={ind}>
{t(`color-type.${color}`)}
</option>
);
})}
</select>
<span
style={{
background:
props.fontColor ||
props.currWidgetsDetails?.options?.fontColor ||
"black"
}}
className="w-5 h-[19px] ml-1"
></span>
</div>
</div>
{["checkbox", radioButtonWidget].includes(props.type) && (
<div className="flex flex-row gap-5 my-2 items-center text-center">
{!props.isSignYourself && (
@@ -325,7 +393,7 @@ function DropdownWidgetOption(props) {
onChange={(e) => setIsReadOnly(e.target.checked)}
/>
<label className="ml-1 mb-0" htmlFor="isreadonly">
Is read only
{t("read-only")}
</label>
</div>
)}
@@ -339,12 +407,13 @@ function DropdownWidgetOption(props) {
/>
<label className="ml-1 mb-0" htmlFor="ishidelabel">
Hide labels
{t("hide-labels")}
</label>
</div>
</div>
)}
</div>
<div
className={`${
props.type === "checkbox" && !props.isSignYourself
@@ -358,7 +427,7 @@ function DropdownWidgetOption(props) {
type="submit"
className="op-btn op-btn-primary"
>
Save
{t("save")}
</button>
{props.currWidgetsDetails?.options?.values?.length > 0 && (
<button
@@ -369,7 +438,7 @@ function DropdownWidgetOption(props) {
resetState();
}}
>
Cancel
{t("cancel")}
</button>
)}
</form>
@@ -2,9 +2,11 @@ import React, { useState, useEffect } from "react";
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import { useTranslation } from "react-i18next";
// import SelectFolder from "../../premitives/SelectFolder";
const EditTemplate = ({ template, onSuccess }) => {
const { t } = useTranslation();
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: template?.Name || "",
@@ -21,8 +23,8 @@ const EditTemplate = ({ template, onSuccess }) => {
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
}
};
const handleStrInput = (e) => {
@@ -62,7 +64,7 @@ const EditTemplate = ({ template, onSuccess }) => {
<form onSubmit={handleSubmit}>
<div className="mb-[0.35rem]">
<label htmlFor="name" className="text-[13px]">
File
{t("report-heading.File")}
</label>
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
{getFileName(template.URL)}
@@ -70,7 +72,7 @@ const EditTemplate = ({ template, onSuccess }) => {
</div>
<div className="mb-[0.35rem]">
<label htmlFor="name" className="text-[13px]">
Name
{t("name")}
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
@@ -78,13 +80,15 @@ const EditTemplate = ({ template, onSuccess }) => {
name="Name"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
</div>
<div className="mb-[0.35rem]">
<label htmlFor="Note" className="text-[13px]">
Note
{t("report-heading.Note")}
</label>
<input
type="text"
@@ -97,7 +101,7 @@ const EditTemplate = ({ template, onSuccess }) => {
</div>
<div className="mb-[0.35rem]">
<label htmlFor="Description" className="text-[13px]">
Description
{t("description")}
</label>
<input
type="text"
@@ -109,7 +113,7 @@ const EditTemplate = ({ template, onSuccess }) => {
/>
</div>
<div className="mb-[0.35rem]">
<label className="text-[13px]">Send In Order</label>
<label className="text-[13px]">{t("send-in-order")}</label>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
type="radio"
@@ -119,7 +123,7 @@ const EditTemplate = ({ template, onSuccess }) => {
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div className="text-[12px]">Yes</div>
<div className="text-[12px]">{t("yes")}</div>
</div>
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
<input
@@ -130,7 +134,7 @@ const EditTemplate = ({ template, onSuccess }) => {
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-[12px]">No</div>
<div className="text-[12px]">{t("no")}</div>
</div>
</div>
{isEnableSubscription && (
@@ -140,7 +144,8 @@ const EditTemplate = ({ template, onSuccess }) => {
isSubscribe ? "font-semibold" : "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{t("auto-reminder")}
{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
@@ -162,7 +167,7 @@ const EditTemplate = ({ template, onSuccess }) => {
{isSubscribe && formData?.AutomaticReminders === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
{t("remind-once")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
@@ -171,13 +176,17 @@ const EditTemplate = ({ template, onSuccess }) => {
name="RemindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
)}
<div className="mt-[1rem] flex justify-start">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
</div>
</form>
@@ -1,5 +1,5 @@
import React from "react";
import { Quill } from "react-quill";
import { Quill } from "react-quill-new";
// Custom Undo button icon component for Quill editor. You can import it directly
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
@@ -79,7 +79,6 @@ export const formats = [
"blockquote",
"background",
"list",
"bullet",
"indent",
"link",
"image",
+10 -9
View File
@@ -1,35 +1,36 @@
import React from "react";
import Tooltip from "../../primitives/Tooltip";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import ReactQuill from "react-quill-new";
import "../../styles/quill.css";
import EditorToolbar, { module1, formats } from "./EditorToolbar";
import { useTranslation } from "react-i18next";
export function EmailBody(props) {
const { t } = useTranslation();
return (
<form className="flex flex-col text-base-content text-lg font-normal">
<div className="m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
<label className="text-sm ml-2">
Subject <Tooltip message={"email subject"} />
{t("subject")} <Tooltip message={t("email-subject")} />
</label>
<input
required
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
value={props.requestSubject}
onChange={(e) => props.setRequestSubject(e.target.value)}
placeholder='${senderName} has requested you to sign "${documentName}"'
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
/>
<label className="text-sm ml-2 mt-3">
Body <Tooltip message={"email body"} />
{t("body")} <Tooltip message={t("email-body")} />
</label>
<div className="px-3 py-2 w-full focus:outline-none text-xs">
<div className="px-1 py-2 w-full focus:outline-none text-xs">
<EditorToolbar containerId="toolbar1" />
<ReactQuill
theme="snow"
value={props.requestBody}
// value={typedText}
placeholder="add body of email "
// readOnly={loading}
// onChangeSelection={handleTextSelection} // Listen for text selection
placeholder={t("email-placeholder")}
ref={props.editorRef}
modules={module1}
formats={formats}
@@ -1,38 +1,40 @@
import React, { useState } from "react";
import { saveAs } from "file-saver";
import axios from "axios";
import { getBase64FromUrl } from "../../constant/Utils";
import { handleDownloadPdf, handleToPrint } from "../../constant/Utils";
import { themeColor, emailRegex } from "../../constant/const";
import printModule from "print-js";
import Loader from "../../primitives/Loader";
import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
function EmailComponent({
isEmail,
pdfUrl,
setIsEmail,
setSuccessEmail,
pdfName,
pdfDetails,
sender,
setIsAlert,
extUserId,
activeMailAdapter
}) {
const { t } = useTranslation();
const [emailList, setEmailList] = useState([]);
const [emailValue, setEmailValue] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [emailErr, setEmailErr] = useState(false);
const [isDownloading, setIsDownloading] = useState("");
const isAndroid = /Android/i.test(navigator.userAgent);
//function for send email
const sendEmail = async () => {
const pdfName = pdfDetails[0]?.Name;
setIsLoading(true);
let sendMail;
for (let i = 0; i < emailList.length; i++) {
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
@@ -68,36 +70,25 @@ function EmailComponent({
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage: t("something-went-wrong-mssg")
});
}
}
if (sendMail && sendMail.data.result.status === "success") {
if (sendMail?.data?.result?.status === "success") {
setSuccessEmail(true);
setIsEmail(false);
setTimeout(() => {
setSuccessEmail(false);
setIsEmail(false);
setEmailValue("");
setEmailList([]);
}, 1500);
setIsLoading(false);
} else if (sendMail && sendMail.data.result.status === "error") {
setIsLoading(false);
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
setEmailValue("");
setEmailList([]);
} else {
setIsLoading(false);
setIsEmail(false);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage: t("something-went-wrong-mssg")
});
setEmailValue("");
setEmailList([]);
@@ -111,7 +102,7 @@ function EmailComponent({
};
//function for get email value
const handleEmailValue = (e) => {
const value = e.target.value;
const value = e.target.value?.toLowerCase()?.replace(/\s/g, "");
setEmailErr(false);
setEmailValue(value);
};
@@ -138,43 +129,6 @@ function EmailComponent({
}
}
};
// function for print signed pdf
const handleToPrint = async (event) => {
event.preventDefault();
const pdf = await getBase64FromUrl(pdfUrl);
const isAndroidDevice = navigator.userAgent.match(/Android/i);
const isAppleDevice =
(/iPad|iPhone|iPod/.test(navigator.platform) ||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
!window.MSStream;
if (isAndroidDevice || isAppleDevice) {
const byteArray = Uint8Array.from(
atob(pdf)
.split("")
.map((char) => char.charCodeAt(0))
);
const blob = new Blob([byteArray], { type: "application/pdf" });
const blobUrl = URL.createObjectURL(blob);
window.open(blobUrl, "_blank");
} else {
printModule({ printable: pdf, type: "pdf", base64: true });
}
};
//handle download signed pdf
const handleDownloadPdf = () => {
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
};
const sanitizeFileName = (pdfName) => {
// Replace spaces with underscore
return pdfName.replace(/ /g, "_");
};
const isAndroid = /Android/i.test(navigator.userAgent);
return (
<div>
{/* isEmail */}
@@ -184,36 +138,43 @@ function EmailComponent({
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2]/70">
<Loader />
<span className="text-[12px] text-base-content">
This might take some time
{t("loader")}
</span>
</div>
)}
{isDownloading === "pdf" && (
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
<Loader />
</div>
)}
<div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content">
<span className="text-base-content font-semibold">
Successfully signed!
{t("successfully-signed")}
</span>
<div className="flex flex-row">
{!isAndroid && (
<button
onClick={handleToPrint}
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
>
<i className="fa-light fa-print" aria-hidden="true"></i>
Print
{t("print")}
</button>
)}
<button
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
onClick={() => handleDownloadPdf()}
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa-light fa-download" aria-hidden="true"></i>
Download
{t("download")}
</button>
</div>
</div>
<div className="h-full p-[20px]">
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
Recipients added here will get a copy of the signed document.
{t("email-mssg")}
</p>
{emailList.length > 0 ? (
<div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]">
@@ -245,6 +206,10 @@ function EmailComponent({
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
onBlur={() => emailValue && handleEnterPress("add")}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
)}
@@ -257,15 +222,19 @@ function EmailComponent({
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary"
onChange={handleEmailValue}
onKeyDown={handleEnterPress}
placeholder="Add an email address and hit enter"
placeholder={t("enter-email-plaholder")}
onBlur={() => emailValue && handleEnterPress("add")}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
)}
{emailErr && (
<p className="text-xs text-[red] ml-1.5 mt-0.5">
please provide correct email address
{t("email-error-1")}
</p>
)}
<button
@@ -278,10 +247,8 @@ function EmailComponent({
</button>
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
<span className="font-bold">Note: </span>
<span className="text-[15px]">
You can only send to ten recipients at a time.
</span>
<span className="font-bold">{t("report-heading.Note")}: </span>
<span className="text-[15px]">{t("email-error-2")}</span>
</div>
<hr className="w-full my-[15px] bg-base-content" />
<button
@@ -289,7 +256,7 @@ function EmailComponent({
className="op-btn op-btn-secondary"
onClick={() => emailList.length > 0 && sendEmail()}
>
Send
{t("send")}
</button>
<button
type="button"
@@ -300,7 +267,7 @@ function EmailComponent({
setEmailList([]);
}}
>
Close
{t("close")}
</button>
</div>
</ModalUi>
+132 -68
View File
@@ -9,6 +9,7 @@ import "../../styles/signature.css";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import ModalUi from "../../primitives/ModalUi";
import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next";
function Header({
isPdfRequestFiles,
@@ -33,8 +34,14 @@ function Header({
setIsEmail,
completeBtnTitle,
setIsEditTemplate,
isPublicTemplate
isPublicTemplate,
clickOnZoomIn,
clickOnZoomOut,
handleRotationFun,
isDisableRotate,
templateId
}) {
const { t } = useTranslation();
const filterPrefill =
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
const isMobile = window.innerWidth < 767;
@@ -46,7 +53,6 @@ function Header({
const currentDecline = { currnt: "Sure", isDeclined: true };
setIsDecline(currentDecline);
};
return (
<div className="flex py-[5px]">
{isMobile && isShowHeader ? (
@@ -71,7 +77,7 @@ function Header({
allPages={allPages}
changePage={changePage}
/>
{pdfUrl && alreadySign ? (
{isCompleted || alreadySign ? (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
@@ -97,7 +103,7 @@ function Header({
className="fa-light fa-arrow-down mr-[3px]"
aria-hidden="true"
></i>
Download
{t("download")}
</div>
</DropdownMenu.Item>
{isCompleted && (
@@ -115,7 +121,7 @@ function Header({
className="fa-light fa-award mr-[3px]"
aria-hidden="true"
></i>
Certificate
{t("certificate")}
</div>
</DropdownMenu.Item>
)}
@@ -129,7 +135,7 @@ function Header({
className="fa-light fa-envelope mr-[3px]"
aria-hidden="true"
></i>
Mail
{t("mail")}
</div>
</DropdownMenu.Item>
)}
@@ -144,7 +150,7 @@ function Header({
className="fa-light fa-print mr-[3px]"
aria-hidden="true"
></i>
Print
{t("print")}
</div>
</DropdownMenu.Item>
</DropdownMenu.Content>
@@ -162,7 +168,7 @@ function Header({
onClick={() => handleDeclinePdfAlert()}
className="text-[red] border-none font-[650] text-[14px] mr-2"
>
Decline
{t("decline")}
</div>
)}
{isPlaceholder ? (
@@ -177,21 +183,15 @@ function Header({
} op-link no-underline font-[650] text-[14px]`}
data-tut="headerArea"
>
{completeBtnTitle ? completeBtnTitle : "Send"}
{completeBtnTitle ? completeBtnTitle : t("send")}
</div>
) : (
<div
data-tut="reactourThird"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
} else if (isPdfRequestFiles) {
embedWidgetsData();
}
}}
onClick={() => embedWidgetsData()}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
>
Finish
{t("finish")}
</div>
)}
<DropdownMenu.Root>
@@ -205,7 +205,7 @@ function Header({
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="bg-white shadow-md rounded-full px-3 py-2"
className="bg-white shadow-md rounded-md px-3 py-2"
sideOffset={5}
>
<DropdownMenu.Item
@@ -213,7 +213,7 @@ function Header({
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
pdfDetails[0]?.URL,
setIsDownloading
)
}
@@ -222,13 +222,59 @@ function Header({
className="fa-light fa-arrow-down mr-[3px]"
aria-hidden="true"
></i>
<span className="font-[500]">Download</span>
<span className="font-[500]">{t("download")}</span>
</DropdownMenu.Item>
{!isDisableRotate && (
<>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => handleRotationFun(90)}
>
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
<span className="font-[500]">
{t("rotate-right")}
</span>
</DropdownMenu.Item>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => handleRotationFun(-90)}
>
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
<span className="font-[500]">
{t("rotate-left")}
</span>
</DropdownMenu.Item>
</>
)}
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => clickOnZoomIn()}
>
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
<span className="font-[500]">{t("zoom-in")}</span>
</DropdownMenu.Item>
<DropdownMenu.Item
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
onClick={() => clickOnZoomOut()}
>
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
<span className="font-[500]">{t("zoom-out")}</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</div>
)}
{isPublicTemplate && (
<div
data-tut="reactourThird"
onClick={() => embedWidgetsData()}
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
>
{t("sign-now")}
</div>
)}
</div>
)}
</div>
@@ -249,13 +295,14 @@ function Header({
<div>
{filterPrefill.length === 0 ? (
<span className="text-[13px] text-[#f5405e]">
Add {signersdata.length - filterPrefill.length}{" "}
recipients signature
{t("add")} {signersdata.length - filterPrefill.length}{" "}
{t("recipients")} {t("widgets-name.signature")}
</span>
) : (
<span className="text-[13px] text-[#f5405e]">
Add {signersdata.length - filterPrefill.length} more
recipients signature
{t("add")} {signersdata.length - filterPrefill.length}{" "}
{t("more")}
{t("recipients")} {t("widgets-name.signature")}
</span>
)}
</div>
@@ -275,7 +322,7 @@ function Header({
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
Back
{t("back")}
</button>
<button
disabled={isMailSend && true}
@@ -286,8 +333,8 @@ function Header({
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
? t("sent")
: t("send")}
</button>
</div>
</>
@@ -303,7 +350,7 @@ function Header({
className="fa-light fa-print py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Print</span>
<span className="hidden lg:block">{t("print")}</span>
</button>
{isCompleted && (
<button
@@ -317,7 +364,7 @@ function Header({
className="fa-light fa-award py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Certificate</span>
<span className="hidden lg:block">{t("certificate")}</span>
</button>
)}
<button
@@ -331,43 +378,66 @@ function Header({
className="fa-light fa-download py-[3px]"
aria-hidden="true"
></i>
<span className="hidden lg:block">Download</span>
<span className="hidden lg:block">{t("download")}</span>
</button>
</div>
) : (
<div className="flex" data-tut="reactourFifth">
<button
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
Back
</button>
{!templateId && (
<button
onClick={() => window.history.go(-2)}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
{t("back")}
</button>
)}
{currentSigner && (
<>
{templateId && (
<button
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
<span className="hidden lg:block">{t("download")}</span>
</button>
)}
<button
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
onClick={() => handleDeclinePdfAlert()}
>
Decline
{t("decline")}
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
onClick={() => embedWidgetsData()}
>
Finish
</button>
<button
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
onClick={() =>
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
}
>
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
<span className="hidden lg:block">Download</span>
{t("finish")}
</button>
{!templateId && (
<button
type="button"
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
onClick={() =>
handleDownloadPdf(
pdfDetails,
pdfUrl,
setIsDownloading
)
}
>
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
<span className="hidden lg:block">{t("download")}</span>
</button>
)}
</>
)}
</div>
@@ -383,7 +453,9 @@ function Header({
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
>
<i className="fa-light fa-award" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Certificate</span>
<span className="hidden lg:block ml-1">
{t("certificate")}
</span>
</button>
)}
<button
@@ -392,7 +464,7 @@ function Header({
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
>
<i className="fa-light fa-print" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Print</span>
<span className="hidden lg:block ml-1">{t("print")}</span>
</button>
<button
type="button"
@@ -402,7 +474,7 @@ function Header({
}
>
<i className="fa-light fa-download" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Download</span>
<span className="hidden lg:block ml-1">{t("download")}</span>
</button>
<button
type="button"
@@ -410,7 +482,7 @@ function Header({
onClick={() => setIsEmail(true)}
>
<i className="fa-light fa-envelope" aria-hidden="true"></i>
<span className="hidden lg:block ml-1">Mail</span>
<span className="hidden lg:block ml-1">{t("mail")}</span>
</button>
</div>
) : isPublicTemplate ? (
@@ -420,7 +492,7 @@ function Header({
className="op-btn op-btn-primary op-btn-sm shadow"
onClick={() => embedWidgetsData()}
>
Sign Now
{t("sign-now")}
</button>
</div>
) : (
@@ -430,18 +502,14 @@ function Header({
type="button"
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
>
Back
{t("back")}
</button>
<button
type="button"
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
onClick={() => {
if (!pdfUrl) {
embedWidgetsData();
}
}}
onClick={() => embedWidgetsData()}
>
Finish
{t("finish")}
</button>
</div>
)}
@@ -456,18 +524,14 @@ function Header({
isOpen={isDownloading === "certificate"}
title={
isDownloading === "certificate"
? "Generating certificate"
: "PDF Download"
? t("generating-certificate")
: t("pdf-download")
}
handleClose={() => setIsDownloading("")}
>
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
{isDownloading === "certificate"}{" "}
<p>
Your completion certificate is being generated. Please wait
momentarily. If the download doesn&apos;t start shortly, click the
button again.
</p>
<p>{t("generate-certificate-alert")}</p>
</div>
</ModalUi>
</div>
+26 -31
View File
@@ -1,48 +1,43 @@
import React from "react";
import { useTranslation } from "react-i18next";
function PdfZoom(props) {
const onClickZoomIn = () => {
props.setScale(props.scale + 0.1 * props.scale);
props.setZoomPercent(props.zoomPercent + 10);
};
const onClickZoomOut = () => {
if (props.zoomPercent > 0) {
if (props.zoomPercent === 10) {
props.setScale(1);
} else {
props.setScale(props.scale - 0.1 * props.scale);
}
props.setZoomPercent(props.zoomPercent - 10);
}
};
const handleReset = () => {
props.setScale(1);
props.setZoomPercent(0);
};
const { t } = useTranslation();
return (
// md:mt-[41px] xl:mt-[63px]mt=[]
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[50px] 2xl:mt-[63px]">
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[43px] 2xl:mt-[63px]">
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={onClickZoomIn}
title="Zoom in"
onClick={() => props.clickOnZoomIn()}
title={t("zoom-in")}
>
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={handleReset}
title="Reset"
>
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
</span>
{!props?.isDisableRotate && (
<>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={() => props.handleRotationFun(90)}
title={t("rotate-right")}
>
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
title={t("rotate-left")}
onClick={() => props.handleRotationFun(-90)}
>
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px]"></i>
</span>
</>
)}
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
onClick={onClickZoomOut}
onClick={() => props.clickOnZoomOut()}
style={{
cursor: props.zoomPercent > 0 ? "pointer" : "default"
}}
title="Zoom out"
title={t("zoom-out")}
>
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
</span>
+334 -282
View File
@@ -4,6 +4,8 @@ import PlaceholderBorder from "./PlaceholderBorder";
import { Rnd } from "react-rnd";
import {
defaultWidthHeight,
fontColorArr,
fontsizeArr,
getContainerScale,
handleCopyNextToWidget,
isTabAndMobile,
@@ -15,6 +17,8 @@ import {
import PlaceholderType from "./PlaceholderType";
import moment from "moment";
import "../../styles/opensigndrive.css";
import ModalUi from "../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
const selectFormat = (data) => {
switch (data) {
@@ -89,9 +93,10 @@ const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
function Placeholder(props) {
const { t } = useTranslation();
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
const [isDateModal, setIsDateModal] = useState(false);
const [containerScale, setContainerScale] = useState();
const [selectDate, setSelectDate] = useState({
date:
@@ -111,6 +116,7 @@ function Placeholder(props) {
: ""
});
const [dateFormat, setDateFormat] = useState([]);
const [clickonWidget, setClickonWidget] = useState({});
const [startDate, setStartDate] = useState(
props.pos.type === "date" &&
getDefaultdate(
@@ -200,20 +206,6 @@ function Placeholder(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectDate]);
//it detect outside click of date dropdown menu
useEffect(() => {
const closeMenuOnOutsideClick = (e) => {
if (isShowDateFormat && !e.target.closest("#menu-container")) {
setIsShowDateFormat(!isShowDateFormat);
}
};
document.addEventListener("click", closeMenuOnOutsideClick);
return () => {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
}, [isShowDateFormat]);
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
const handleWidgetIdandPopup = () => {
if (props.setSelectWidgetId) {
@@ -338,7 +330,6 @@ function Placeholder(props) {
//condition for only placeholder and template flow
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
@@ -369,7 +360,6 @@ function Placeholder(props) {
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
@@ -378,7 +368,6 @@ function Placeholder(props) {
props.setIsSelectId(checkIndex || 0);
} else if (props.data && props.pos.type === textWidget) {
props.setTempSignerId(props.uniqueId);
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
const checkIndex = props.xyPostion.findIndex(
(data) => data.Id === props.data.Id
@@ -430,7 +419,11 @@ function Placeholder(props) {
props.setXyPostion,
props.data && props.data.Id,
false,
data?.format
data?.format,
null,
null,
props.fontSize || props.pos?.options?.fontSize || "12",
props.fontColor || props.pos?.options?.fontColor || "black"
);
setSelectDate({ date: date, format: data?.format });
};
@@ -522,17 +515,12 @@ function Placeholder(props) {
)}
{/* setting icon only for date widgets */}
{props.pos.type === "date" && selectDate && (
<div
id="menu-container"
style={{
zIndex: "99",
top: "-19px",
right: props.isPlaceholder ? "60px" : "44px"
}}
className={`${isShowDateFormat ? "show" : ""} dropdown icon`}
<i
onClick={(e) => {
setIsShowDateFormat(!isShowDateFormat);
props.setCurrWidgetsDetails(props.pos);
setIsDateModal(!isDateModal);
e.stopPropagation();
setClickonWidget(props.pos);
if (props.data) {
props.setSignKey(props.pos.key);
props.setUniqueId(props.data.Id);
@@ -543,8 +531,9 @@ function Placeholder(props) {
}
}}
onTouchEnd={(e) => {
props.setCurrWidgetsDetails(props.pos);
e.stopPropagation();
setIsShowDateFormat(!isShowDateFormat);
setIsDateModal(!isDateModal);
if (props.data) {
props.setSignKey(props.pos.key);
props.setUniqueId(props.data.Id);
@@ -554,44 +543,15 @@ function Placeholder(props) {
props.setIsSelectId(checkIndex || 0);
}
}}
>
<i
className="fa-light fa-gear icon"
style={{
color: "#188ae2",
fontSize: "14px"
}}
></i>
<div
className={
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
}
aria-labelledby="dropdownMenuButton"
aria-expanded={isShowDateFormat ? "true" : "false"}
>
{dateFormat.map((data, ind) => {
return (
<span
key={ind}
onTouchEnd={(e) => {
e.stopPropagation();
setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data);
handleSaveDate(data);
}}
onClick={() => {
setIsShowDateFormat(!isShowDateFormat);
setSelectDate(data);
handleSaveDate(data);
}}
className="dropdown-item text-[13px]"
>
{data?.date ? data?.date : "nodata"}
</span>
);
})}
</div>
</div>
style={{
zIndex: "99",
top: "-18px",
right: props.isPlaceholder ? "50px" : "30px",
color: "#188ae2",
fontSize: "14px"
}}
className="fa-light fa-gear icon"
></i>
)}
{/* copy icon for all widgets */}
<i
@@ -705,228 +665,320 @@ function Placeholder(props) {
return "all-scroll";
}
};
return (
<Rnd
id={props.pos.key}
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
key={props.pos.key}
lockAspectRatio={
![
textWidget,
"email",
"name",
"company",
"job title",
textInputWidget
].includes(props.pos.type) &&
(props.pos.Width
? props.pos.Width / props.pos.Height
: defaultWidthHeight(props.pos.type).width /
defaultWidthHeight(props.pos.type).height)
}
enableResizing={{
top: false,
right: false,
bottom: false,
left: false,
topRight: false,
bottomRight:
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId &&
props.pos.type !== "checkbox" &&
props.pos.type !== radioButtonWidget
? true
: false
: props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId &&
true,
bottomLeft: false,
topLeft: false
}}
bounds="parent"
className="signYourselfBlock"
style={{
border: "1px solid #007bff",
borderRadius: "2px",
textAlign:
props.pos.type !== "name" &&
props.pos.type !== "company" &&
props.pos.type !== "job title" &&
"center",
cursor: getCursor(),
zIndex:
props.pos.type === "date"
? props.pos.key === props.selectWidgetId
? 99 + 1
: 99
: props?.pos?.zIndex
? props.pos.zIndex
: "5",
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
}}
onDrag={() => {
setDraggingEnabled(true);
props.handleTabDrag && props.handleTabDrag(props.pos.key);
}}
size={{
width:
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself)
}}
onResizeStart={() => {
setDraggingEnabled(true);
props.setIsResize && props.setIsResize(true);
}}
onResizeStop={(e, direction, ref) => {
props.setIsResize && props.setIsResize(false);
props.handleSignYourselfImageResize &&
props.handleSignYourselfImageResize(
ref,
props.pos.key,
props.xyPostion,
props.setXyPostion,
props.index,
containerScale,
props.scale,
props.data && props.data.Id,
props.isResize
);
}}
disableDragging={
props.isNeedSign
? true
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
? false
: !isDraggingEnabled
}
onDragStop={(event, dragElement) =>
props.handleStop &&
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
}
position={{
x: xPos(props.pos, props.isSignYourself),
y: yPos(props.pos, props.isSignYourself)
}}
onResize={(e, direction, ref) => {
setPlaceholderBorder({
w: ref.offsetWidth / (props.scale * containerScale),
h: ref.offsetHeight / (props.scale * containerScale)
});
}}
onClick={() => handleOnClickPlaceholder()}
>
{props.isShowBorder &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId ? (
<BorderResize right={-12} top={-11} />
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
props.data?.signerObjId === props.signerObjId &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" ? (
<BorderResize />
) : (
<></>
)
) : (
<>
<Rnd
id={props.pos.key}
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
key={props.pos.key}
lockAspectRatio={
![
textWidget,
"email",
"name",
"company",
"job title",
textInputWidget
].includes(props.pos.type) &&
(props.pos.Width
? props.pos.Width / props.pos.Height
: defaultWidthHeight(props.pos.type).width /
defaultWidthHeight(props.pos.type).height)
}
enableResizing={{
top: false,
right: false,
bottom: false,
left: false,
topRight: false,
bottomRight:
props.data && props.isNeedSign
? props.data?.signerObjId === props.signerObjId &&
props.pos.type !== "checkbox" &&
props.pos.type !== radioButtonWidget
? true
: false
: props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId &&
true,
bottomLeft: false,
topLeft: false
}}
bounds="parent"
className="signYourselfBlock"
style={{
border: "1px solid #007bff",
borderRadius: "2px",
textAlign:
props.pos.type !== "name" &&
props.pos.type !== "company" &&
props.pos.type !== "job title" &&
"center",
cursor: getCursor(),
zIndex:
props.pos.type === "date"
? props.pos.key === props.selectWidgetId
? 99 + 1
: 99
: props?.pos?.zIndex
? props.pos.zIndex
: "5",
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
}}
onDrag={() => {
setDraggingEnabled(true);
props.handleTabDrag && props.handleTabDrag(props.pos.key);
}}
size={{
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself)
}}
onResizeStart={() => {
setDraggingEnabled(true);
props.setIsResize && props.setIsResize(true);
}}
onResizeStop={(e, direction, ref) => {
props.setIsResize && props.setIsResize(false);
props.handleSignYourselfImageResize &&
props.handleSignYourselfImageResize(
ref,
props.pos.key,
props.xyPostion,
props.setXyPostion,
props.index,
containerScale,
props.scale,
props.data && props.data.Id,
props.isResize
);
}}
disableDragging={
props.isNeedSign
? true
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
? false
: !isDraggingEnabled
}
onDragStop={(event, dragElement) =>
props.handleStop &&
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
}
position={{
x: xPos(props.pos, props.isSignYourself),
y: yPos(props.pos, props.isSignYourself)
}}
onResize={(e, direction, ref) => {
setPlaceholderBorder({
w: ref.offsetWidth / (props.scale * containerScale),
h: ref.offsetHeight / (props.scale * containerScale)
});
}}
onClick={() => handleOnClickPlaceholder()}
>
{props.isShowBorder &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId && <BorderResize />
)}
props.pos.key === props.selectWidgetId ? (
<BorderResize right={-12} top={-11} />
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
props.data?.signerObjId === props.signerObjId &&
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" ? (
<BorderResize />
) : (
<></>
)
) : (
props.pos.type !== radioButtonWidget &&
props.pos.type !== "checkbox" &&
props.pos.key === props.selectWidgetId && <BorderResize />
)}
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
<PlaceholderBorder
setDraggingEnabled={setDraggingEnabled}
pos={props.pos}
isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
scale={props.scale}
containerScale={containerScale}
placeholderBorder={placeholderBorder}
/>
)}
{isTabAndMobile ? (
<div
className="flex items-stretch"
style={{
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10"
}}
onTouchEnd={() => handleOnClickPlaceholder()}
onClick={() => handleOnClickPlaceholder()}
>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
<PlaceholderBorder
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
pos={props.pos}
isPlaceholder={props.isPlaceholder}
getCheckboxRenderWidth={getCheckboxRenderWidth}
scale={props.scale}
containerScale={containerScale}
placeholderBorder={placeholderBorder}
/>
)}
{isTabAndMobile ? (
<div
className="flex items-stretch"
style={{
left: xPos(props.pos, props.isSignYourself),
top: yPos(props.pos, props.isSignYourself),
width:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posWidth(props.pos, props.isSignYourself),
height:
props.pos.type === radioButtonWidget ||
props.pos.type === "checkbox"
? "auto"
: props.posHeight(props.pos, props.isSignYourself),
zIndex: "10"
}}
onTouchEnd={() => handleOnClickPlaceholder()}
onClick={() => handleOnClickPlaceholder()}
>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
/>
</div>
) : (
<>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
/>
</>
)}
</Rnd>
<ModalUi isOpen={isDateModal} title={t("widget-info")} showClose={false}>
<div className="h-[100%] p-[20px]">
<div className="flex flex-row items-center">
<span>{t("format")} : </span>
<div className="flex">
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
defaultValue={""}
onChange={(e) => {
const selectedIndex = e.target.value;
e.stopPropagation();
setSelectDate(dateFormat[selectedIndex]);
}}
>
<option value="" disabled>
{t("select-date-format")}
</option>
{dateFormat.map((data, ind) => {
return (
<option className="text-[13px]" value={ind} key={ind}>
{data?.date ? data?.date : "nodata"}
</option>
);
})}
</select>
</div>
<span className="text-xs text-gray-400 ml-1">
{selectDate.format}
</span>
</div>
<div className="flex items-center mt-4 md:mt-2">
<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)}
>
{fontsizeArr.map((size, ind) => {
return (
<option className="text-[13px]" value={size} key={ind}>
{size}
</option>
);
})}
</select>
<div className="flex flex-row gap-1 items-center ml-2 md:ml-4 ">
<span>{t("color")}: </span>
<select
value={
props.fontColor || clickonWidget.options?.fontColor || "black"
}
onChange={(e) => props.setFontColor(e.target.value)}
className="ml-[4px] md:ml[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
>
{fontColorArr.map((color, ind) => {
return (
<option value={color} key={ind}>
{t(`color-type.${color}`)}
</option>
);
})}
</select>
<span
style={{
background:
props.fontColor || props.pos.options?.fontColor || "black"
}}
className="w-5 h-[19px] ml-1"
></span>
</div>
</div>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
type="button"
className="op-btn op-btn-primary"
onClick={() => {
setIsDateModal(false);
handleSaveDate(selectDate);
props.setFontColor("");
props.setFontSize("");
}}
>
{t("save")}
</button>
</div>
) : (
<>
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
<PlaceholderType
pos={props.pos}
xyPostion={props.xyPostion}
index={props.index}
setXyPostion={props.setXyPostion}
data={props.data}
setSignKey={props.setSignKey}
isShowDropdown={props?.isShowDropdown}
isPlaceholder={props.isPlaceholder}
isSignYourself={props.isSignYourself}
signerObjId={props.signerObjId}
handleUserName={props.handleUserName}
setDraggingEnabled={setDraggingEnabled}
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
isNeedSign={props.isNeedSign}
setSelectDate={setSelectDate}
selectDate={selectDate}
setValidateAlert={props.setValidateAlert}
setStartDate={setStartDate}
startDate={startDate}
handleSaveDate={handleSaveDate}
xPos={props.xPos}
/>
</>
)}
</Rnd>
</ModalUi>
</>
);
}
@@ -1,10 +1,21 @@
import React, { useState } from "react";
import ModalUi from "../../primitives/ModalUi";
import { randomId, textWidget } from "../../constant/Utils";
import {
handleCopyNextToWidget,
randomId,
textWidget
} from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function PlaceholderCopy(props) {
const copyType = ["All pages", "All pages but last", "All pages but first"];
const [selectCopyType, setSelectCopyType] = useState("");
const { t } = useTranslation();
const copyType = [
{ id: 1, type: "All pages" },
{ id: 2, type: "All pages but last" },
{ id: 3, type: "All pages but first" },
{ id: 4, type: "Next to current widget" }
];
const [selectCopyType, setSelectCopyType] = useState(1);
//function for get copy placeholder position
const getCopyPlaceholderPosition = (
type,
@@ -18,7 +29,7 @@ function PlaceholderCopy(props) {
(data) => data.xPosition !== currentPlaceholder.xPosition
);
//copy all page placeholder at requested position except first page
if (newPageNumber === 1 && type === "first") {
if (newPageNumber === 1 && type === 3) {
if (filterPosition && filterPosition.length > 0) {
return {
pageNumber: newPageNumber,
@@ -27,7 +38,7 @@ function PlaceholderCopy(props) {
}
}
//copy all page placeholder at requested position except last page
else if (newPageNumber === props.allPages && type === "last") {
else if (newPageNumber === props.allPages && type === 2) {
if (existPlaceholderPosition) {
return {
pageNumber: newPageNumber,
@@ -170,25 +181,65 @@ function PlaceholderCopy(props) {
//function for getting selected type placeholder copy
const handleApplyCopy = () => {
if (selectCopyType === "All pages") {
copyPlaceholder("all");
} else if (selectCopyType === "All pages but last") {
copyPlaceholder("last");
} else if (selectCopyType === "All pages but first") {
copyPlaceholder("first");
if (selectCopyType === 4) {
const signerPosition = props.xyPostion;
let currentXYposition;
const signerId = props.signerObjId ? props.signerObjId : props.Id;
if (signerId) {
const filterSignerPosition = signerPosition.filter(
(item) => item?.Id === signerId
);
currentXYposition = filterSignerPosition[0].placeHolder.filter(
(data) => data.pageNumber === props?.pageNumber
);
//get current placeholder position data which user want to copy
currentXYposition = currentXYposition[0].pos.find(
(position) => position.key === props.signKey
);
//function to create new widget next to just widget
handleCopyNextToWidget(
currentXYposition,
props.widgetType,
props.xyPostion,
props.pageNumber,
props.setXyPostion,
props?.Id
);
} else {
const getIndex = props?.xyPostion.findIndex(
(data) => data.pageNumber === props.pageNumber
);
const placeholderPosition = props?.xyPostion[getIndex];
//get current placeholder position data which user want to copy
currentXYposition = placeholderPosition.pos.find(
(pos) => pos.key === props.signKey
);
//function to create new widget next to just widget
handleCopyNextToWidget(
currentXYposition,
props.widgetType,
props.xyPostion,
getIndex,
props.setXyPostion
);
}
} else {
copyPlaceholder(selectCopyType);
}
};
const handleUniqueId = () => {
if (props.widgetType === textWidget) {
const signerId = props.signerObjId ? props.signerObjId : props.Id;
if (signerId && props.widgetType === textWidget) {
props.setUniqueId(props?.tempSignerId);
props.setTempSignerId("");
}
props.setIsPageCopy(false);
setSelectCopyType(1);
};
return (
<ModalUi
isOpen={props.isPageCopy}
title={"Copy to all pages"}
title={t("copy-title")}
handleClose={() => handleUniqueId()}
>
<div className="h-full p-[20px] text-base-content">
@@ -199,12 +250,11 @@ function PlaceholderCopy(props) {
<input
className="mr-[8px] op-radio op-radio-xs"
type="radio"
value={data}
onChange={() => setSelectCopyType(data)}
checked={selectCopyType === data}
value={data.id}
onChange={() => setSelectCopyType(data.id)}
checked={selectCopyType === data.id}
/>
{data}
{t(`copy-type.${data.type}`)}
</label>
</div>
);
@@ -220,14 +270,14 @@ function PlaceholderCopy(props) {
disabled={!selectCopyType}
className="op-btn op-btn-primary"
>
Apply
{t("apply")}
</button>
<button
type="button"
className="op-btn op-btn-ghost"
onClick={() => handleUniqueId()}
>
Cancel
{t("cancel")}
</button>
</div>
</ModalUi>
@@ -14,18 +14,23 @@ import "react-datepicker/dist/react-datepicker.css";
import "../../styles/signature.css";
import RegexParser from "regex-parser";
import { emailRegex } from "../../constant/const";
import { useTranslation } from "react-i18next";
const textWidgetCls =
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
const selectWidgetCls =
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
function PlaceholderType(props) {
const { t } = useTranslation();
const type = props?.pos?.type;
const widgetTypeTraslation = t(`widgets-name.${props?.pos?.type}`);
const [selectOption, setSelectOption] = useState("");
const [validatePlaceholder, setValidatePlaceholder] = useState("");
const inputRef = useRef(null);
const [textValue, setTextValue] = useState();
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
const years = range(1990, getYear(new Date()) + 16, 1);
const fontSize = (props.pos.options?.fontSize || "12") + "px";
const fontColor = props.pos.options?.fontColor || "black";
const months = [
"January",
"February",
@@ -140,7 +145,12 @@ function PlaceholderType(props) {
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
<div
className={`${selectWidgetCls} text-[12px] overflow-hidden`}
style={{
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif"
}}
className={`${selectWidgetCls} overflow-hidden`}
onClick={onClick}
ref={ref}
>
@@ -311,7 +321,7 @@ function PlaceholderType(props) {
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
widgetTypeTraslation
)}
</div>
);
@@ -329,7 +339,7 @@ function PlaceholderType(props) {
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
widgetTypeTraslation
)}
</div>
);
@@ -340,9 +350,13 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
width: fontSize,
height: fontSize
}}
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
} flex justify-center op-checkbox rounded-[1px] `}
onBlur={handleInputBlur}
disabled={
props.isNeedSign &&
@@ -376,7 +390,15 @@ function PlaceholderType(props) {
}}
/>
{!props.pos.options?.isHideLabel && (
<label className="text-xs mb-0 text-center">{data}</label>
<label
style={{
fontSize: fontSize,
color: fontColor
}}
className="text-xs mb-0 text-center"
>
{data}
</label>
)}
</div>
);
@@ -407,29 +429,29 @@ function PlaceholderType(props) {
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
fontSize: fontSize,
color: fontColor
}}
cols="50"
/>
) : (
<div
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
overflow: "hidden"
}}
>
<span>{type}</span>
<span>{widgetTypeTraslation}</span>
</div>
);
case "dropdown":
return props.data?.signerObjId === props.signerObjId ? (
<select
style={{
fontSize: fontSize,
color: fontColor
}}
className={`${selectWidgetCls} text-[12px] bg-inherit`}
id="myDropdown"
value={selectOption}
@@ -447,21 +469,44 @@ function PlaceholderType(props) {
}}
>
{/* Default/Title option */}
<option value="" disabled hidden>
<option
style={{
fontSize: fontSize,
color: fontColor
}}
value=""
disabled
hidden
>
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values?.map((data, ind) => {
return (
<option key={ind} value={data}>
<option
style={{
fontSize: fontSize,
color: fontColor
}}
key={ind}
value={data}
>
{data}
</option>
);
})}
</select>
) : (
<div className="text-[12px] overflow-hidden">
{props.pos?.options?.name ? props.pos.options.name : type}
<div
style={{
fontSize: fontSize,
color: fontColor
}}
className=" overflow-hidden"
>
{props.pos?.options?.name
? props.pos.options.name
: widgetTypeTraslation}
</div>
);
case "initials":
@@ -478,7 +523,7 @@ function PlaceholderType(props) {
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
widgetTypeTraslation
)}
</div>
);
@@ -506,25 +551,21 @@ function PlaceholderType(props) {
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
fontSize: fontSize,
color: fontColor
}}
cols="50"
/>
) : (
<div
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif",
overflow: "hidden"
}}
>
<span>{type}</span>
<span>{widgetTypeTraslation}</span>
</div>
);
case "company":
@@ -550,25 +591,21 @@ function PlaceholderType(props) {
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
fontSize: fontSize,
color: fontColor
}}
cols="50"
/>
) : (
<div
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
overflow: "hidden",
fontFamily: "Arial, sans-serif"
}}
>
<span>{type}</span>
<span>{widgetTypeTraslation}</span>
</div>
);
case "job title":
@@ -594,25 +631,21 @@ function PlaceholderType(props) {
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
fontSize: fontSize,
color: fontColor
}}
cols="50"
/>
) : (
<div
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif",
overflow: "hidden"
}}
>
<span>{type}</span>
<span>{widgetTypeTraslation}</span>
</div>
);
case "date":
@@ -675,7 +708,14 @@ function PlaceholderType(props) {
/>
</div>
) : (
<div className="text-[12px] text-black items-center overflow-hidden">
<div
style={{
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif"
}}
className="items-center overflow-hidden"
>
<span>
{props.selectDate
? props.selectDate?.format
@@ -699,7 +739,7 @@ function PlaceholderType(props) {
props?.handleUserName(
props?.data?.Id,
props?.data?.Role,
props?.pos?.type
widgetTypeTraslation
)}
</div>
);
@@ -727,10 +767,8 @@ function PlaceholderType(props) {
}}
className={textWidgetCls}
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif"
}}
cols="50"
@@ -738,15 +776,13 @@ function PlaceholderType(props) {
) : (
<div
style={{
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black",
fontSize: fontSize,
color: fontColor,
fontFamily: "Arial, sans-serif",
overflow: "hidden"
}}
>
<span>{type}</span>
<span>{widgetTypeTraslation}</span>
</div>
);
case radioButtonWidget:
@@ -756,9 +792,12 @@ function PlaceholderType(props) {
return (
<div key={ind} className="flex items-center text-center gap-0.5">
<input
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} ${"w-[15px] h-[15px]"} flex justify-center op-radio`}
style={{
width: fontSize,
height: fontSize,
marginTop: ind > 0 ? "10px" : "0px"
}}
className={`flex justify-center op-radio`}
type="radio"
disabled={
props.isNeedSign &&
@@ -773,7 +812,15 @@ function PlaceholderType(props) {
}}
/>
{!props.pos.options?.isHideLabel && (
<label className="text-xs mb-0">{data}</label>
<label
style={{
fontSize: fontSize,
color: fontColor
}}
className="text-xs mb-0"
>
{data}
</label>
)}
</div>
);
@@ -803,10 +850,8 @@ function PlaceholderType(props) {
className={textWidgetCls}
style={{
fontFamily: "Arial, sans-serif",
fontSize: props.pos.options?.fontSize
? props.pos.options?.fontSize + "px"
: "12px",
color: props.pos.options?.fontColor || "black"
fontSize: fontSize,
color: fontColor
}}
cols="50"
/>
+5 -5
View File
@@ -1,6 +1,8 @@
import React from "react";
import { useTranslation } from "react-i18next";
function PrevNext({ pageNumber, allPages, changePage }) {
const { t } = useTranslation();
//for go to previous page
function previousPage() {
changePage(-1);
@@ -17,23 +19,21 @@ function PrevNext({ pageNumber, allPages, changePage }) {
disabled={pageNumber <= 1}
onClick={previousPage}
>
<span className="block lg:hidden">
<span className="block">
<i className="fa-light fa-backward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden 2xl:text-[30px]">Prev</span>
</button>
<span className="text-xs text-base-content font-medium mx-2 2xl:text-[30px]">
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
{pageNumber || (allPages ? 1 : "--")} {t("of")} {allPages || "--"}
</span>
<button
className="op-btn op-btn-neutral op-btn-xs md:op-btn-sm font-semibold text-xs"
disabled={pageNumber >= allPages}
onClick={nextPage}
>
<span className="block lg:hidden">
<span className="block">
<i className="fa-light fa-forward" aria-hidden="true"></i>
</span>
<span className="lg:block hidden 2xl:text-[30px]">Next</span>
</button>
</div>
);
@@ -16,7 +16,7 @@ const RecipientList = (props) => {
//function for onhover signer name change background color
const inputRef = useRef(null);
const isWidgetExist = (Id) => {
return props.signerPos.some((x) => x.Id === Id);
return props.signerPos.some((x) => x.Id === Id && x.placeHolder);
};
//handle drag start
@@ -63,9 +63,7 @@ const RecipientList = (props) => {
remainingItems.splice(index, 0, ...draggedItem);
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
props?.setSignersData(remainingItems);
props?.setSignerObjId(remainingItems[index]?.objectId || "");
props?.setIsSelectId(index);
props?.setContractName(remainingItems[index]?.className || "");
props?.setUniqueId(remainingItems[index]?.Id);
props?.setRoleName(remainingItems[index]?.Role);
props?.setBlockColor(remainingItems[index]?.blockColor);
@@ -121,9 +119,7 @@ const RecipientList = (props) => {
}}
onClick={(e) => {
e.preventDefault();
props.setSignerObjId(obj?.objectId || "");
props.setIsSelectId(ind);
props.setContractName(obj?.className || "");
props.setUniqueId(obj.Id);
props.setRoleName(obj.Role);
props.setBlockColor(obj?.blockColor);
@@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Document, Page } from "react-pdf";
import { useSelector } from "react-redux";
@@ -11,8 +12,10 @@ function RenderAllPdfPage({
pageNumber,
signerPos,
signerObjectId,
containerWH
containerWH,
pdfRotateBase64
}) {
const { t } = useTranslation();
const [signPageNumber, setSignPageNumber] = useState([]);
const [bookmarkColor, setBookmarkColor] = useState("");
//set all number of pages after load pdf
@@ -64,19 +67,20 @@ function RenderAllPdfPage({
)
);
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Pages
{t("pages")}
</div>
<div
className={`flex h-[90%] flex-col items-center m-2
autoSignScroll hide-scrollbar max-h-[100vh] `}
>
<Document
loading={"Loading Document.."}
loading={t("loading-doc")}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
file={(pdfRotateBase64 && pdfDataBase64) || signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
@@ -93,16 +97,16 @@ function RenderAllPdfPage({
>
{signerPos && addSignatureBookmark(index)}
<div className="relative z-[1] overflow-hidden">
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={pageWidth - 60}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
</div>
{/* <div className="relative z-[1] overflow-hidden"> */}
<Page
key={`page_${index + 1}`}
pageNumber={index + 1}
width={pageWidth - 60}
scale={1}
renderAnnotationLayer={false}
renderTextLayer={false}
/>
{/* </div> */}
</div>
))}
</Document>
+257 -218
View File
@@ -9,6 +9,7 @@ import {
} from "../../constant/Utils";
import Placeholder from "./Placeholder";
import Alert from "../../primitives/Alert";
import { useTranslation } from "react-i18next";
function RenderPdf({
pageNumber,
@@ -24,7 +25,6 @@ function RenderPdf({
setSignKey,
pdfDetails,
xyPostion,
pdfUrl,
pageDetails,
pdfRequest,
signerObjectId,
@@ -41,7 +41,6 @@ function RenderPdf({
setUniqueId,
signersdata,
setIsPageCopy,
setSignerObjId,
setShowDropdown,
setIsInitial,
setIsValidate,
@@ -61,9 +60,16 @@ function RenderPdf({
scale,
setIsSelectId,
ispublicTemplate,
handleUserDetails
handleUserDetails,
pdfRotateBase64,
fontSize,
setFontSize,
fontColor,
setFontColor
}) {
const { t } = useTranslation();
const isMobile = window.innerWidth < 767;
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
const isGuestSigner = localStorage.getItem("isGuestSigner");
@@ -247,154 +253,181 @@ function RenderPdf({
);
}
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<>
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{successEmail && (
<Alert type={"success"}>{t("success-email-alert")}</Alert>
)}
{isMobile ? (
<div
data-tut="reactourForth"
className={`${
isGuestSigner ? "30px" : ""
} border-[0.1px] border-[#ebe8e8]`}
ref={drop}
id="container"
<RSC
style={{
position: "relative",
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
height: window.innerHeight - 200,
zIndex: 0
}}
className="h-full"
noScrollY={false}
noScrollX={scale === 1 ? true : false}
>
{containerWH?.width &&
pdfOriginalWH.length > 0 &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={key}>
{checkSignedSigners(data)}
</React.Fragment>
);
})
: placeholder // placeholder mobile
? signerPos.map((data, ind) => {
<div
data-tut="reactourForth"
className={`${
isGuestSigner ? "30px" : ""
} border-[0.1px] border-[#ebe8e8] overflow-x-auto`}
style={{
width: containerWH?.width && containerWH?.width * scale
}}
ref={drop}
id="container"
>
{containerWH?.width &&
pdfOriginalWH.length > 0 &&
(pdfRequest
? signerPos.map((data, key) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
<React.Fragment key={key}>
{checkSignedSigners(data)}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
pos && (
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
/>
)
);
})}
</React.Fragment>
);
}))}
: placeholder // placeholder mobile
? signerPos?.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data?.placeHolder &&
data?.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={
setSelectWidgetId
}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
</React.Fragment>
);
})
: !pdfDetails?.[0]?.IsCompleted &&
xyPostion.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.pageNumber === pageNumber &&
data.pos.map((pos) => {
return (
pos && (
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleSignYourselfImageResize
}
index={index}
xyPostion={xyPostion}
setXyPostion={setXyPostion}
containerWH={containerWH}
setIsSignPad={setIsSignPad}
isShowBorder={true}
isSignYourself={true}
posWidth={posWidth}
posHeight={posHeight}
pdfDetails={pdfDetails[0]}
isDragging={isDragging}
setIsInitial={setIsInitial}
setWidgetType={setWidgetType}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleUserName={handleUserName}
setIsCheckbox={setIsCheckbox}
setValidateAlert={setValidateAlert}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
)
);
})}
</React.Fragment>
);
}))}
{/* this component for render pdf document is in middle of the component */}
<div className="flex items-center justify-center">
<Document
onLoadError={() => {
setPdfLoad(false);
}}
loading={"Loading Document.."}
onLoadError={() => setPdfLoad(false)}
loading={t("loading-doc")}
onLoadSuccess={pageDetails}
// ref={pdfRef}'
onClick={() => {
@@ -403,14 +436,13 @@ function RenderPdf({
}
}}
file={
pdfUrl
? pdfUrl
: pdfDetails[0] && pdfDetails[0].SignedUrl
? pdfDetails[0].SignedUrl
: pdfDetails[0].URL
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
scale={scale || 1}
key={index}
pageNumber={pageNumber}
width={containerWH.width}
@@ -422,7 +454,7 @@ function RenderPdf({
/>
</Document>
</div>
</div>
</RSC>
) : (
<RSC
style={{
@@ -456,71 +488,78 @@ function RenderPdf({
? signerPos.map((data, ind) => {
return (
<React.Fragment key={ind}>
{data.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
setSignerObjId={setSignerObjId}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={setSelectWidgetId}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
{data?.placeHolder &&
data?.placeHolder.map((placeData, index) => {
return (
<React.Fragment key={index}>
{placeData.pageNumber === pageNumber &&
placeData.pos.map((pos) => {
return (
<React.Fragment key={pos.key}>
<Placeholder
pos={pos}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
handleDeleteSign={handleDeleteSign}
setIsStamp={setIsStamp}
handleTabDrag={handleTabDrag}
handleStop={handleStop}
handleSignYourselfImageResize={
handleImageResize
}
index={pageNumber}
xyPostion={signerPos}
setXyPostion={setSignerPos}
data={data}
setIsResize={setIsResize}
setShowDropdown={setShowDropdown}
isShowBorder={true}
isPlaceholder={true}
setUniqueId={setUniqueId}
handleLinkUser={handleLinkUser}
handleUserName={handleUserName}
isSignYourself={false}
posWidth={posWidth}
posHeight={posHeight}
isDragging={isDragging}
setIsValidate={setIsValidate}
setWidgetType={setWidgetType}
setIsRadio={setIsRadio}
setIsCheckbox={setIsCheckbox}
setCurrWidgetsDetails={
setCurrWidgetsDetails
}
setSelectWidgetId={
setSelectWidgetId
}
selectWidgetId={selectWidgetId}
handleNameModal={handleNameModal}
setTempSignerId={setTempSignerId}
uniqueId={uniqueId}
handleTextSettingModal={
handleTextSettingModal
}
scale={scale}
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
setIsSelectId={setIsSelectId}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
</React.Fragment>
);
})}
</React.Fragment>
);
})}
</React.Fragment>
);
})
: xyPostion.map((data, ind) => {
: !pdfDetails?.[0]?.IsCompleted &&
xyPostion?.map((data, ind) => {
// signyourself flow
return (
<React.Fragment key={ind}>
@@ -568,6 +607,10 @@ function RenderPdf({
containerWH={containerWH}
pdfOriginalWH={pdfOriginalWH}
pageNumber={pageNumber}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
</React.Fragment>
);
@@ -578,10 +621,8 @@ function RenderPdf({
{/* this component for render pdf document is in middle of the component */}
<Document
onLoadError={() => {
setPdfLoad(false);
}}
loading={"Loading Document.."}
onLoadError={() => setPdfLoad(false)}
loading={t("loading-doc")}
onLoadSuccess={pageDetails}
onClick={() => {
if (setSelectWidgetId) {
@@ -590,11 +631,9 @@ function RenderPdf({
}}
// ref={pdfRef}
file={
pdfUrl
? pdfUrl
: pdfDetails[0] && pdfDetails[0].SignedUrl
? pdfDetails[0].SignedUrl
: pdfDetails[0].URL
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
@@ -0,0 +1,49 @@
import i18next from "i18next";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
function SelectLanguage(props) {
const { i18n } = useTranslation();
const languages = [
{ value: "en", text: "English" },
{ value: "fr", text: "Français" }
];
const [lang, setLang] = useState(i18next.language || "en");
// 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
});
};
return (
<div
className={`${
!props.isProfile && " mt-[9px] pb-2 md:pb-0 "
} flex justify-center items-center `}
>
<select
value={lang}
onChange={handleChangeLang}
className={`${
!props.isProfile ? " md:w-[15%] w-[50%]" : "w-[180px]"
} op-select op-select-bordered bg-white op-select-sm `}
>
<option disabled selected>
select
</option>
{languages.map((item) => {
return (
<option key={item.value} value={item.value}>
{item.text}
</option>
);
})}
</select>
</div>
);
}
export default SelectLanguage;
+13 -11
View File
@@ -1,4 +1,5 @@
import React, { useRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import SignatureCanvas from "react-signature-canvas";
function SignPad({
isSignPad,
@@ -22,6 +23,7 @@ function SignPad({
currWidgetsDetails,
setCurrWidgetsDetails
}) {
const { t } = useTranslation();
const [penColor, setPenColor] = useState("blue");
const allColor = ["blue", "red", "black"];
const canvasRef = useRef(null);
@@ -81,7 +83,7 @@ function SignPad({
className="op-btn op-btn-ghost mr-1 mt-[2px]"
onClick={() => handleClear()}
>
Clear
{t("clear")}
</button>
)}
<button
@@ -142,7 +144,7 @@ function SignPad({
: true
}
>
Save
{t("save")}
</button>
</div>
);
@@ -306,8 +308,8 @@ function SignPad({
<span className="text-base-content font-bold text-lg">
{widgetType === "image" ||
currWidgetsDetails?.type === "image"
? "Upload image"
: "Upload stamp image"}
? t("upload-image")
: t("upload-stamp-image")}
</span>
) : (
<>
@@ -328,7 +330,7 @@ function SignPad({
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Draw
{t("draw")}
</span>
</div>
<div>
@@ -345,7 +347,7 @@ function SignPad({
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Upload Image
{t("upload-image")}
</span>
</div>
<div>
@@ -363,7 +365,7 @@ function SignPad({
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
Type
{t("type")}
</span>
</div>
{!isInitial && defaultSign ? (
@@ -382,7 +384,7 @@ function SignPad({
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Signature
{t("my-signature")}
</span>
</div>
) : (
@@ -403,7 +405,7 @@ function SignPad({
: "no-underline"
} op-link underline-offset-8 ml-[2px]`}
>
My Initials
{t("my-initials")}
</span>
</div>
)
@@ -490,7 +492,7 @@ function SignPad({
hidden
/>
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
<div className="text-[10px]">Upload</div>
<div className="text-[10px]">{t("upload")}</div>
</div>
</div>
) : (
@@ -527,7 +529,7 @@ function SignPad({
<div>
<div className="flex justify-between items-center">
<span className="mr-[5px] text-[12px]">
{isInitial ? "Initials" : "Signature"}:
{isInitial ? t("initial-teb") : t("signature-tab")}:
</span>
<input
maxLength={isInitial ? 3 : 30}
+3 -1
View File
@@ -1,6 +1,8 @@
import React from "react";
import "../../styles/signature.css";
import { useTranslation } from "react-i18next";
function Signedby({ pdfDetails }) {
const { t } = useTranslation();
const getFirstLetter = (name) => {
const firstLetter = name.charAt(0);
return firstLetter;
@@ -9,7 +11,7 @@ function Signedby({ pdfDetails }) {
return (
<div className="hidden md:block w-full h-full bg-base-100">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
Signed By
{t("signed-by")}
</div>
<div className="mt-[2px] bg-base-100">
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
@@ -1,8 +1,10 @@
import React from "react";
import RecipientList from "./RecipientList";
import { Tooltip } from "react-tooltip";
import { useTranslation } from "react-i18next";
function SignerListPlace(props) {
const { t } = useTranslation();
return (
<div>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
@@ -18,36 +20,12 @@ function SignerListPlace(props) {
</a>
<Tooltip id="my-tooltip" className="z-[100]">
<div className="max-w-[450px] 2xl:max-w-[500px] 2xl:text-[20px] p-[1px]">
<p className="font-bold pb-[1px]">
What are template roles?
</p>
<p>
Begin by specifying each role needed for the completion of
the document. Think about the parties involved in the
signing process and what their responsibilities are.
Common roles include HR for internal documents, Customer
for agreements or Vendor for business agreements.{" "}
</p>
<p className="font-bold">
Why pre-attach users to some roles?
</p>
<p>
For roles that consistently involve the same individual
(e.g., the CEO&apos;s signature on employee offer
letters), you can pre-attach a user to a role within the
template. This step is optional but recommended for
efficiency and consistency across documents.
</p>
<p className="font-bold">
When do i specify the user attached to each role?
</p>
<p>
When you create a document from your template, you&apos;ll
be prompted to attach users to each defined role. If a
role already has a user attached, this will be pre-filled,
but you can modify it as needed before sending out the
document.
</p>
<p className="font-bold pb-[1px]">{t("role-help.p1")}</p>
<p>{t("role-help.p2")} </p>
<p className="font-bold">{t("role-help.p3")}</p>
<p>{t("role-help.p4")}</p>
<p className="font-bold">{t("role-help.p5")}</p>
<p>{t("role-help.p6")}</p>
</div>
</Tooltip>
</>
@@ -67,7 +45,7 @@ function SignerListPlace(props) {
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
onClick={() => props.handleAddSigner()}
>
<i className="fa-light fa-plus"></i> Add role
<i className="fa-light fa-plus"></i> {t("add-role")}
</div>
) : (
<div
@@ -77,7 +55,7 @@ function SignerListPlace(props) {
disabled={props?.isMailSend ? true : false}
onClick={() => props.setIsAddSigner(true)}
>
<i className="fa-light fa-plus"></i> Add recipients
<i className="fa-light fa-plus"></i> {t("add-recipients")}
</div>
)}
</div>
@@ -1,19 +1,21 @@
import React from "react";
import ModalUi from "../../primitives/ModalUi";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function TextFontSetting(props) {
const { t } = useTranslation();
return (
<ModalUi
headerColor={"#dc3545"}
isOpen={props.isTextSetting}
reduceWidth={"max-w-[350px]"}
title={"Text field"}
title={t("text-field")}
handleClose={() => props.setIsTextSetting(false)}
>
<div className="h-full p-[20px]">
<div className="flex items-center">
<span>Font size:</span>
<span>{t("font-size")} :</span>
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
@@ -32,7 +34,7 @@ function TextFontSetting(props) {
})}
</select>
<div className="flex flex-row gap-1 items-center ml-4">
<span>color: </span>
<span>{t("color")}: </span>
<select
value={
props.fontColor ||
@@ -45,7 +47,7 @@ function TextFontSetting(props) {
{fontColorArr.map((color, ind) => {
return (
<option value={color} key={ind}>
{color}
{t(`color-type.${color}`)}
</option>
);
})}
@@ -67,7 +69,7 @@ function TextFontSetting(props) {
type="button"
className="op-btn op-btn-primary mt-2"
>
save
{t("save")}
</button>
</div>
</ModalUi>
+14 -10
View File
@@ -1,12 +1,14 @@
import React from "react";
import Loader from "../../primitives/Loader";
import { useTranslation } from "react-i18next";
function VerifyEmail(props) {
const { t } = useTranslation();
return (
<dialog className="op-modal op-modal-open absolute z-[1999]">
<div className="md:w-[40%] w-[80%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
<h3 className="font-bold text-lg pt-[15px] px-[20px] text-base-content">
OTP verification
{t("otp-verification")}
</h3>
{props.isVerifyModal ? (
<form
@@ -16,26 +18,30 @@ function VerifyEmail(props) {
}}
>
<div className="px-6 py-3 text-base-content">
<label className="mb-2">Enter OTP</label>
<label className="mb-2">{t("enter-otp")}</label>
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter OTP received over email"
placeholder={t("otp-placeholder")}
value={props.otp}
onChange={(e) => props.setOtp(e.target.value)}
/>
</div>
<div className="px-6 my-3">
<button type="submit" className="op-btn op-btn-primary">
Verify
{t("verify")}
</button>
<button
className="op-btn op-btn-secondary ml-2"
onClick={(e) => props.handleResend(e)}
>
Resend
{t("resend")}
</button>
</div>
</form>
@@ -45,16 +51,14 @@ function VerifyEmail(props) {
</div>
) : (
<div className="px-6 py-3 text-base-content">
<p className="mb-2 text-sm">Please verify your email !</p>
<p className="mb-2 text-sm">{t("verify-email")}</p>
<div className="px-0 mt-3">
<button
className="op-btn op-btn-primary"
type="submit"
onClick={() => {
props.handleVerifyBtn();
}}
onClick={() => props.handleVerifyBtn()}
>
Send OTP
{t("send-otp")}
</button>
</div>
</div>
@@ -9,6 +9,7 @@ import {
textWidget,
widgets
} from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function WidgetComponent({
dragSignature,
signRef,
@@ -19,9 +20,7 @@ function WidgetComponent({
addPositionOfSignature,
signersdata,
isSelectListId,
setSignerObjId,
setIsSelectId,
setContractName,
isSigners,
dataTut,
isMailSend,
@@ -40,6 +39,7 @@ function WidgetComponent({
setIsAddSigner,
uniqueId
}) {
const { t } = useTranslation();
const [isSignersModal, setIsSignersModal] = useState(false);
const [, dropdown] = useDrag({
type: "BOX",
@@ -330,7 +330,7 @@ function WidgetComponent({
} hidden md:block h-full bg-base-100`}
>
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
<span>Fields</span>
<span>{t("fields")}</span>
</div>
<div
@@ -348,7 +348,7 @@ function WidgetComponent({
)}
{isSignersModal && (
<ModalUi
title={title ? title : "Recipients"}
title={title ? title : t("recipients")}
isOpen={isSignersModal}
handleClose={handleModal}
>
@@ -358,9 +358,7 @@ function WidgetComponent({
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
@@ -375,7 +373,7 @@ function WidgetComponent({
</div>
) : (
<div className=" p-[20px] text-[15px] font-medium text-center">
Please add a {title ? title : "recipients"}
{t("please-add")} {title ? title : t("recipients")}
</div>
)}
</ModalUi>
@@ -1,7 +1,10 @@
import React from "react";
import { getWidgetType } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
function WidgetList(props) {
const { t } = useTranslation();
return props.updateWidgets.map((item, ind) => {
return (
<div className="2xl:p-3 mb-[5px]" key={ind}>
@@ -25,7 +28,7 @@ function WidgetList(props) {
onMouseDown={() => props?.handleMouseLeave()}
onTouchStart={props?.handleDivClick}
>
{item.ref && getWidgetType(item)}
{item.ref && getWidgetType(item, t(`widgets-name.${item.type}`))}
</div>
</div>
);
@@ -7,8 +7,10 @@ import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip";
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
import { useTranslation } from "react-i18next";
const WidgetNameModal = (props) => {
const { t } = useTranslation();
const [formdata, setFormdata] = useState({
name: "",
defaultValue: "",
@@ -104,7 +106,7 @@ const WidgetNameModal = (props) => {
<ModalUi
isOpen={props.isOpen}
handleClose={props.handleClose && props.handleClose}
title={"Widget info"}
title={t("widget-info")}
>
<form
onSubmit={handleSubmit}
@@ -114,7 +116,7 @@ const WidgetNameModal = (props) => {
>
<div className="mb-[0.75rem] text-[13px]">
<label htmlFor="name">
Name
{t("name")}
<span className="text-[red]"> *</span>
</label>
<input
@@ -122,6 +124,8 @@ const WidgetNameModal = (props) => {
name="name"
value={formdata.name}
onChange={(e) => handleChange(e)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -138,7 +142,7 @@ const WidgetNameModal = (props) => {
: ""
} text-[13px]`}
>
Validation
{t("validation")}
</label>
<Tooltip
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
@@ -192,7 +196,7 @@ const WidgetNameModal = (props) => {
)}
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
Default value
{t("default-value")}
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -216,7 +220,7 @@ const WidgetNameModal = (props) => {
className="fa-light fa-exclamation-circle text-[15px]"
style={{ color: "#fab005" }}
></i>
invalid default value
{t("invalid-default-value")}
</div>
)}
</div>
@@ -238,7 +242,9 @@ const WidgetNameModal = (props) => {
formdata.status.toLowerCase() === data.toLowerCase()
}
/>
<div className="text-[13px] font-medium">{data}</div>
<div className="text-[13px] font-medium">
{t(`widget-status.${data}`)}
</div>
</div>
);
})}
@@ -247,7 +253,7 @@ const WidgetNameModal = (props) => {
{props.defaultdata?.type === textInputWidget && (
<div className="mb-[0.75rem]">
<label htmlFor="hint" className="text-[13px]">
Hint
{t("hint")}
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -266,7 +272,7 @@ const WidgetNameModal = (props) => {
"email"
].includes(props.defaultdata?.type) && (
<div className="flex items-center mb-[0.75rem]">
<span>Font size:</span>
<span>{t("font-size")}:</span>
<select
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
value={
@@ -283,7 +289,7 @@ const WidgetNameModal = (props) => {
})}
</select>
<div className="flex flex-row gap-1 items-center ml-4">
<span>color: </span>
<span>{t("color")}: </span>
<select
value={
props.fontColor ||
@@ -296,7 +302,7 @@ const WidgetNameModal = (props) => {
{fontColorArr.map((color, ind) => {
return (
<option value={color} key={ind}>
{color}
{t(`color-type.${color}`)}
</option>
);
})}
@@ -316,7 +322,7 @@ const WidgetNameModal = (props) => {
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
<button type="submit" className="op-btn op-btn-primary">
Save
{t("save")}
</button>
</form>
</ModalUi>
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import Alert from "../../../primitives/Alert";
import Loader from "../../../primitives/Loader";
import { useTranslation } from "react-i18next";
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
const folderPtr = {
@@ -9,6 +10,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
className: folderCls,
objectId: parentFolderId
};
const { t } = useTranslation();
const [name, setName] = useState("");
const [folderList, setFolderList] = useState([]);
const [isAlert, setIsAlert] = useState(false);
@@ -60,7 +62,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
}
const templExist = await exsitQuery.first();
if (templExist) {
setAlert({ type: "danger", message: "Folder already exist!" });
setAlert({ type: "danger", message: t("folder-already-exist") });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
@@ -85,7 +87,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
handleLoader(false);
setAlert({
type: "success",
message: "Folder created successfully!"
message: t("folder-created-successfully")
});
setIsAlert(true);
setTimeout(() => {
@@ -98,7 +100,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
}
} else {
handleLoader(false);
setAlert({ type: "info", message: "Please fill folder name" });
setAlert({ type: "info", message: t("fill-folder-name") });
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
@@ -120,20 +122,25 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
<Loader />
</div>
)}
<h1 className="text-base font-semibold mt-[0.4rem]">Create Folder</h1>
<h1 className="text-base font-semibold mt-[0.4rem]">
{t("create-folder")}
</h1>
<div className="text-xs mt-2">
<label className="block">
Name<span className="text-red-500 text-[13px]">*</span>
{t("name")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Parent Folder</label>
<label className="block">{t("parent-folder")}</label>
<select
value={selectedParent}
onChange={handleOptions}
@@ -155,7 +162,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
className="op-btn op-btn-primary op-btn-sm mt-3"
>
<i className="fa-light fa-plus"></i>
<span>Create</span>
<span>{t("create")}</span>
</button>
</div>
</div>
@@ -2,8 +2,10 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
import { useTranslation } from "react-i18next";
const FolderModal = (props) => {
const { t } = useTranslation();
const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
@@ -147,7 +149,7 @@ const FolderModal = (props) => {
return (
<div className="text-xs mt-2">
<ModalUi
title={"Select Folder"}
title={t("select-foler")}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
@@ -158,7 +160,7 @@ const FolderModal = (props) => {
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
{t("OpenSign-drive")} /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
@@ -214,7 +216,7 @@ const FolderModal = (props) => {
))
: !isLoader && (
<div className="text-base-content text-center my-2">
No data found
{t("no-data")}
</div>
)}
</div>
@@ -236,27 +238,28 @@ const FolderModal = (props) => {
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="op-btn op-btn-seconday op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleCreate}
>
{isAdd ? (
<>
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
<span className="text-xs">Back</span>
<span className="text-xs">{t("back")}</span>
</>
) : (
<>
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
<span className="">Add folder</span>
<span className="">{t("add-folder")}</span>
</>
)}
</div>
<div
className="op-btn op-btn-primary op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleSubmit}
>
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
<i className="fa-light fa-save" aria-hidden="true"></i>
{t("save-here")}
</div>
</div>
</ModalUi>
@@ -3,8 +3,10 @@ import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
import Tooltip from "../../../primitives/Tooltip";
import { useTranslation } from "react-i18next";
const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
const { t } = useTranslation();
const [isOpen, SetIsOpen] = useState(false);
const [clickFolder, setClickFolder] = useState("");
const [selectFolder, setSelectedFolder] = useState({});
@@ -169,7 +171,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<div className="text-xs mt-2 ">
<div>
<label className="block">
Select Folder
{t("select-folder")}
{required && <span className="text-red-500 text-[13px]">*</span>}
</label>
</div>
@@ -192,12 +194,12 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<p>
{selectFolder && selectFolder.Name
? selectFolder.Name
: "OpenSign™ Drive"}
: t("OpenSign-drive")}
</p>
<div className="text-black text-sm">
<i
className="fa-light fa-pencil cursor-pointer"
title="Select Folder"
title={t("select-folder")}
aria-hidden="true"
></i>
</div>
@@ -208,15 +210,11 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
</div>
</div>
<div className="absolute top-2 right-1 cursor-pointer">
<Tooltip
message={
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
}
/>
<Tooltip message={t("select-folder-help")} />
</div>
</div>
<ModalUi
title={"Select Folder"}
title={t("select-folder")}
isOpen={isOpen}
handleClose={handleCancel}
>
@@ -227,7 +225,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
{t("OpenSign-drive")} /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
@@ -283,7 +281,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
))
: !isLoader && (
<div className="text-base-content text-center my-2">
No data found
{t("no-data")}
</div>
)}
</div>
@@ -305,27 +303,28 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="op-btn op-btn-seconday op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleCreate}
>
{isAdd ? (
<>
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
<span className="text-xs">Back</span>
<span className="text-xs">{t("back")}</span>
</>
) : (
<>
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
<span className="">Add folder</span>
<span className="">{t("add-folder")}</span>
</>
)}
</div>
<div
className="op-btn op-btn-primary op-btn-sm"
title="Save Here"
title={t("save-here")}
onClick={handleSubmit}
>
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
<i className="fa-light fa-save" aria-hidden="true"></i>
{t("save-here")}
</div>
</div>
</ModalUi>
@@ -1,8 +1,10 @@
import React, { useState } from "react";
import Parse from "parse";
import AsyncSelect from "react-select/async";
import { useTranslation } from "react-i18next";
const SelectSigners = (props) => {
const { t } = useTranslation();
const [userList, setUserList] = useState([]);
const [selected, setSelected] = useState();
const [userData, setUserData] = useState({});
@@ -71,13 +73,15 @@ const SelectSigners = (props) => {
<div className="h-full px-[20px] py-[10px] text-base-content">
<div className="w-full mx-auto p-[8px]">
<div className="mb-0">
<label className="text-[14px] font-bold">Choose from contacts</label>
<label className="text-[14px] font-bold">
{t("choose-from-contacts")}
</label>
<AsyncSelect
cacheOptions
defaultOptions
value={selected}
loadingMessage={() => "Loading..."}
noOptionsMessage={() => "Contact not found"}
loadingMessage={() => t("loading")}
noOptionsMessage={() => t("contact-not-found")}
loadOptions={loadOptions}
onChange={handleOptions}
unstyled
@@ -102,11 +106,11 @@ const SelectSigners = (props) => {
isError ? "text-[red]" : "text-transparent"
} text-[11px] ml-[6px] my-[2px]`}
>
Please select signer
{t("select-signer")}
</p>
<div>
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
Submit
{t("submit")}
</button>
</div>
</div>
@@ -1,9 +1,10 @@
import React, { useState, useEffect } from "react";
import Select from "react-select";
import AsyncSelect from "react-select/async";
import AddSigner from "../../AddSigner";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
@@ -32,8 +33,8 @@ const AddSignerModal = ({ isOpen, children }) => {
*/
const SignersInput = (props) => {
const { t } = useTranslation();
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = useState([]);
const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions);
@@ -44,40 +45,6 @@ const SignersInput = (props) => {
setSelected(newValue);
};
const GetSelectListData = async () => {
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);
const contactRes = await contactbook.find();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
let list = [];
// let _selected = [];
res.forEach((x) => {
let obj = {
label: x.Name,
value: x.objectId,
isChecked: true
};
list.push(obj);
});
setState(list);
}
} catch (error) {
console.log("err", error);
}
};
useEffect(() => {
GetSelectListData();
}, []);
useEffect(() => {
if (props.isReset && props.isReset === true) {
setSelected([]);
@@ -116,33 +83,71 @@ const SignersInput = (props) => {
setSelected([data]);
}
};
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();
if (contactRes) {
const res = JSON.parse(JSON.stringify(contactRes));
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
//and filter signers from total signer's list which already present in document's signers list
const compareArrays = (res, signerObj) => {
return res.filter(
(item1) =>
!signerObj.find((item2) => item2.objectId === item1.objectId)
);
};
//get update signer's List if signersdata is present
const updateSignersList =
props?.signersData && compareArrays(res, props?.signersData);
const result = updateSignersList ? updateSignersList : res;
setState(result);
return await result.map((item) => ({
label: item.Name,
value: item.objectId,
isChecked: true
}));
}
} catch (error) {
console.log("err", error);
}
};
return (
<div className="text-xs mt-2 ">
<label className="block relative">
Signers
{t("signers")}
{props.required && <span className="text-red-500 text-[13px]">*</span>}
<span className="absolute ml-1 text-xs z-30">
<Tooltip
id={"signer-tooltip"}
message={
"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."
}
/>
<Tooltip id={"signer-tooltip"} message={t("signers-help")} />
</span>
</label>
<div className="flex gap-x-[5px]">
<div className="w-full">
<Select
<AsyncSelect
onSortEnd={onSortEnd}
distance={4}
isMulti
cacheOptions
defaultOptions
options={state || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
noOptionsMessage={() => "Contact not found"}
loadingMessage={() => t("loading")}
noOptionsMessage={() => t("contact-not-found")}
loadOptions={loadOptions}
unstyled
classNames={{
control: () =>
@@ -171,7 +176,7 @@ const SignersInput = (props) => {
</div>
<AddSignerModal isOpen={modalIsOpen}>
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
Add Signer
{t("add-signer")}
</h3>
<button
onClick={handleModalCloseClick}
+3 -1
View File
@@ -1,7 +1,9 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
const Menu = ({ item, isOpen, closeSidebar }) => {
const { t } = useTranslation();
return (
<li key={item.title} role="none" className="my-0.5">
<NavLink
@@ -20,7 +22,7 @@ const Menu = ({ item, isOpen, closeSidebar }) => {
role="menuitem"
>
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
<span className="ml-3 lg:ml-4">{item.title}</span>
<span className="ml-3 lg:ml-4">{t(`sidebar.${item.title}`)}</span>
</NavLink>
</li>
);
@@ -1,27 +1,54 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
const SocialMedia = () => {
return (
<React.Fragment>
<NavLink to="https://github.com/opensignlabs/opensign" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">OpenSign&apos;s Github</span>
</NavLink>
<NavLink to="https://www.linkedin.com/company/opensign%E2%84%A2/" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">OpenSign&apos;s LinkedIn</span>
</NavLink>
<NavLink to="https://www.twitter.com/opensignlabs" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">OpenSign&apos;s Twitter</span>
</NavLink>
<NavLink to="https://discord.com/invite/xe9TDuyAyj" target="_blank" rel="noopener noreferrer">
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">OpenSign&apos;s Discord</span>
</NavLink>
</React.Fragment>
)
}
const { t } = useTranslation();
export default SocialMedia;
return (
<React.Fragment>
<NavLink
to="https://github.com/opensignlabs/opensign"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-github"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.github")}
</span>
</NavLink>
<NavLink
to="https://www.linkedin.com/company/opensign%E2%84%A2/"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-linkedin"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.linked-in")}
</span>
</NavLink>
<NavLink
to="https://www.twitter.com/opensignlabs"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-square-x-twitter"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.twitter")}
</span>
</NavLink>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
target="_blank"
rel="noopener noreferrer"
>
<i aria-hidden="true" className="fa-brands fa-discord"></i>
<span className="fa-sr-only">
OpenSign&apos;s {t("social-media.discord")}
</span>
</NavLink>
</React.Fragment>
);
};
export default SocialMedia;
@@ -1,7 +1,9 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { NavLink } from "react-router-dom";
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
const { t } = useTranslation();
const { title, icon, children } = item;
return (
<li role="none" className="my-0.5">
@@ -14,7 +16,7 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
>
<i className={`${icon} text-[18px]`}></i>
<div className="flex justify-between items-center w-full">
<span className="ml-3 lg:ml-4">{title}</span>
<span className="ml-3 lg:ml-4">{t(`sidebar.${item.title}`)}</span>
<i
className={`${
submenuOpen[item.title]
@@ -48,7 +50,9 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
className={`${childItem.icon} text-[18px]`}
aria-hidden="true"
></i>
<span className="ml-3 lg:ml-4">{childItem.title}</span>
<span className="ml-3 lg:ml-4">
{t(`sidebar.${item.title}-Children.${childItem.title}`)}
</span>
</NavLink>
</li>
))}
+339 -108
View File
@@ -1,12 +1,13 @@
import axios from "axios";
import moment from "moment";
import React from "react";
import { rgb } from "pdf-lib";
import { PDFDocument, rgb, degrees } from "pdf-lib";
import Parse from "parse";
import { appInfo } from "./appinfo";
import { saveAs } from "file-saver";
import printModule from "print-js";
import { validplan } from "../json/plansArr";
import fontkit from "@pdf-lib/fontkit";
export const fontsizeArr = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
export const fontColorArr = ["red", "black", "blue", "yellow"];
@@ -15,6 +16,12 @@ export const isTabAndMobile = window.innerWidth < 1023;
export const textInputWidget = "text input";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
export const fileasbytes = async (filepath) => {
const response = await fetch(filepath); // Adjust the path accordingly
const arrayBuffer = await response.arrayBuffer();
return new Uint8Array(arrayBuffer);
};
export const openInNewTab = (url, target) => {
if (target) {
window.open(url, target, "noopener,noreferrer");
@@ -49,18 +56,68 @@ export async function fetchSubscription(
? { contactId: contactObjId }
: { extUserId: extUser };
const tenatRes = await axios.post(url, params, { headers: headers });
let plan, status, billingDate;
let plan, status, billingDate, adminId;
if (isGuestSign) {
plan = tenatRes.data?.result?.result?.plan;
status = tenatRes.data?.result?.result?.isSubscribed;
} else {
plan = tenatRes.data?.result?.result?.PlanCode;
billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
const allowedUsers = tenatRes.data?.result?.result?.AllowedUsers || 0;
adminId = tenatRes?.data?.result?.result?.ExtUserPtr?.objectId;
localStorage.setItem("allowedUsers", allowedUsers);
}
return { plan, billingDate, status };
return { plan, billingDate, status, adminId };
} catch (err) {
console.log("Err in fetch subscription", err);
return { plan: "", billingDate: "" };
return { plan: "", billingDate: "", status: "", adminId: "" };
}
}
export async function fetchSubscriptionInfo() {
try {
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
if (extClass && extClass.length > 0) {
const extUser = extClass[0].objectId;
const baseURL = localStorage.getItem("baseUrl");
const url = `${baseURL}functions/getsubscriptions`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const params = { extUserId: extUser };
const tenatRes = await axios.post(url, params, { headers: headers });
const price =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.plan?.price;
const totalPrice =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.amount;
const planId =
tenatRes.data?.result?.result?.SubscriptionDetails?.data?.subscription
?.subscription_id;
const plan_code = tenatRes.data?.result?.result?.PlanCode;
const totalAllowedUser = tenatRes.data?.result?.result?.AllowedUsers || 0;
const adminId =
tenatRes?.data?.result?.result?.ExtUserPtr?.objectId || "";
return {
status: "success",
price: price,
totalPrice: totalPrice,
planId: planId,
plan_code: plan_code,
totalAllowedUser: totalAllowedUser,
adminId: adminId
};
}
} catch (err) {
console.log("Err in fetch subscription", err);
return { status: "error", error: err };
}
}
//function to get subcripition details from subscription class
@@ -68,41 +125,22 @@ export async function checkIsSubscribed() {
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
} else if (res.billingDate) {
if (new Date(res.billingDate) > new Date()) {
return true;
} else {
return false;
}
} else {
return false;
}
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
}
}
//function to get subcripition details from subscription class
export async function checkIsSubscribedTeam() {
try {
const res = await fetchSubscription();
if (res.plan === "freeplan") {
return false;
return { plan: res.plan, isValid: false, adminId: res?.adminId };
} else if (res.billingDate) {
const plan = validplan[res.plan] || false;
if (plan && new Date(res.billingDate) > new Date()) {
return true;
return { plan: res.plan, isValid: true, adminId: res?.adminId };
} else if (new Date(res.billingDate) > new Date()) {
return { plan: res.plan, isValid: true, adminId: res?.adminId };
} else {
return false;
return { plan: res.plan, isValid: false, adminId: res?.adminId };
}
} else {
return false;
return { plan: res.plan, isValid: false, adminId: res?.adminId };
}
} catch (err) {
console.log("Err in fetch subscription", err);
return false;
return { plan: "no-plan", isValid: false, adminId: "" };
}
}
@@ -253,7 +291,6 @@ export const handleImageResize = (
}
}
};
export const widgets = [
{
type: "signature",
@@ -383,14 +420,14 @@ export const addWidgetOptions = (type) => {
return {};
}
};
export const getWidgetType = (item) => {
export const getWidgetType = (item, widgetName) => {
return (
<div className="op-btn w-fit md:w-[100%] op-btn-primary op-btn-outline op-btn-sm focus:outline-none outline outline-[1.5px] ml-[6px] md:ml-0 p-0 overflow-hidden">
<div className="w-full h-full flex md:justify-between items-center">
<div className="flex justify-start items-center text-[13px] ml-1">
{!isMobile && <i className="fa-light fa-grip-vertical ml-[3px]"></i>}
<span className="md:inline-block text-center text-[15px] ml-[5px] font-semibold pr-1 md:pr-0">
{item.type}
{widgetName}
</span>
</div>
<div className="text-[20px] op-btn op-btn-primary rounded-none w-[40px] h-full flex justify-center items-center">
@@ -440,7 +477,7 @@ export const resizeBorderExtraWidth = () => {
return 20;
};
export async function getBase64FromUrl(url) {
export async function getBase64FromUrl(url, autosign) {
const data = await fetch(url);
const blob = await data.blob();
return new Promise((resolve) => {
@@ -448,8 +485,12 @@ export async function getBase64FromUrl(url) {
reader.readAsDataURL(blob);
reader.onloadend = function () {
const pdfBase = this.result;
const suffixbase64 = pdfBase.split(",").pop();
resolve(suffixbase64);
if (autosign) {
resolve(pdfBase);
} else {
const suffixbase64 = pdfBase.split(",").pop();
resolve(suffixbase64);
}
};
});
}
@@ -620,7 +661,12 @@ export const randomId = () => {
return randomDigit;
};
export const createDocument = async (template, placeholders, signerData) => {
export const createDocument = async (
template,
placeholders,
signerData,
pdfUrl
) => {
if (template && template.length > 0) {
const Doc = template[0];
@@ -643,7 +689,7 @@ export const createDocument = async (template, placeholders, signerData) => {
}
const data = {
Name: Doc.Name,
URL: Doc.URL,
URL: pdfUrl,
SignedUrl: Doc.SignedUrl,
SentToOthers: Doc.SentToOthers,
Description: Doc.Description,
@@ -752,7 +798,9 @@ export const onChangeInput = (
initial,
dateFormat,
isDefaultEmpty,
isRadio
isRadio,
fontSize,
fontColor
) => {
const isSigners = xyPostion.some((data) => data.signerPtr);
let filterSignerPos;
@@ -779,6 +827,8 @@ export const onChangeInput = (
options: {
...position.options,
response: value,
fontSize: fontSize,
fontColor: fontColor,
validation: {
type: "date-format",
format: dateFormat // This indicates the required date format explicitly.
@@ -825,7 +875,6 @@ export const onChangeInput = (
}
} else {
let getXYdata = xyPostion[index].pos;
const updatePosition = getXYdata.map((positionData) => {
if (positionData.key === signKey) {
if (dateFormat) {
@@ -834,6 +883,8 @@ export const onChangeInput = (
options: {
...positionData.options,
response: value,
fontSize: fontSize,
fontColor: fontColor,
validation: {
type: "date-format",
format: dateFormat // This indicates the required date format explicitly.
@@ -1022,19 +1073,29 @@ export const addInitialData = (signerPos, setXyPostion, value, userId) => {
//function for embed document id
export const embedDocId = async (pdfDoc, documentId, allPages) => {
// `fontBytes` is used to embed custom font in pdf
const fontBytes = await fileasbytes(
"https://cdn.opensignlabs.com/webfonts/times.ttf"
);
pdfDoc.registerFontkit(fontkit);
const font = await pdfDoc.embedFont(fontBytes, { subset: true });
for (let i = 0; i < allPages; i++) {
const font = await pdfDoc.embedFont("Helvetica");
const fontSize = 10;
const textContent = documentId && `OpenSign™ DocumentId: ${documentId} `;
const pages = pdfDoc.getPages();
const page = pages[i];
page.drawText(textContent, {
x: 10,
y: page.getHeight() - 10,
size: fontSize,
const getObj = compensateRotation(
page.getRotation().angle,
10,
5,
1,
page.getSize(),
fontSize,
rgb(0.5, 0.5, 0.5),
font,
color: rgb(0.5, 0.5, 0.5)
});
page
);
page.drawText(textContent, getObj);
}
};
@@ -1199,8 +1260,8 @@ export const fetchImageBase64 = async (imageUrl) => {
};
//function for select image and upload image
export const changeImageWH = async (base64Image) => {
const newWidth = 100;
const newHeight = 40;
const newWidth = 300;
const newHeight = 120;
return new Promise((resolve, reject) => {
const img = new Image();
img.src = base64Image;
@@ -1228,6 +1289,21 @@ const calculateFontSize = (position, containerScale, signyourself) => {
return font / containerScale;
}
};
const getWidgetsFontColor = (type) => {
switch (type) {
case "red":
return rgb(1, 0, 0);
case "black":
return rgb(0, 0, 0);
case "blue":
return rgb(0, 0, 1);
case "yellow":
return rgb(0.9, 1, 0);
default:
return rgb(0, 0, 0);
}
};
//function for embed multiple signature using pdf-lib
export const multiSignEmbed = async (
widgets,
@@ -1237,6 +1313,12 @@ export const multiSignEmbed = async (
pdfOriginalWH,
containerWH
) => {
// `fontBytes` is used to embed custom font in pdf
const fontBytes = await fileasbytes(
"https://cdn.opensignlabs.com/webfonts/times.ttf"
);
pdfDoc.registerFontkit(fontkit);
const font = await pdfDoc.embedFont(fontBytes, { subset: true });
for (let item of widgets) {
const containerScale = getContainerScale(
pdfOriginalWH,
@@ -1330,6 +1412,9 @@ export const multiSignEmbed = async (
return resizePos;
}
};
const color = position?.options?.fontColor;
const updateColorInRgb = getWidgetsFontColor(color);
const fontSize = parseInt(position?.options?.fontSize || 13);
const widgetTypeExist = [
textWidget,
textInputWidget,
@@ -1340,12 +1425,11 @@ export const multiSignEmbed = async (
"email"
].includes(position.type);
if (position.type === "checkbox") {
const font = await pdfDoc.embedFont("Helvetica");
let checkboxOptionGapFromTop, isCheck;
let checkboxGapFromTop, isCheck;
let y = yPos(position);
const optionsFontSize = 13;
const checkboxSize = 18;
const checkboxTextGapFromLeft = 22;
const optionsFontSize = fontSize || 13;
const checkboxSize = fontSize;
const checkboxTextGapFromLeft = fontSize + 5 || 22;
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
const checkboxRandomId = "checkbox" + randomId();
@@ -1362,9 +1446,9 @@ export const multiSignEmbed = async (
const checkbox = form.createCheckBox(checkboxRandomId);
if (ind > 0) {
y = y + checkboxOptionGapFromTop;
y = y + checkboxGapFromTop;
} else {
checkboxOptionGapFromTop = 26;
checkboxGapFromTop = fontSize + 5 || 26;
}
if (!position?.options?.isHideLabel) {
@@ -1376,7 +1460,7 @@ export const multiSignEmbed = async (
1,
page.getSize(),
optionsFontSize,
rgb(0, 0, 0),
updateColorInRgb,
font,
page
);
@@ -1388,7 +1472,7 @@ export const multiSignEmbed = async (
width: checkboxSize,
height: checkboxSize
};
checkboxObj = getImagePosition(page, checkboxObj, 1);
checkboxObj = getWidgetPosition(page, checkboxObj, 1);
checkbox.addToPage(page, checkboxObj);
//applied which checkbox should be checked
@@ -1401,27 +1485,12 @@ export const multiSignEmbed = async (
});
}
} else if (widgetTypeExist) {
const font = await pdfDoc.embedFont("Helvetica");
const fontSize = calculateFontSize(
position,
containerScale,
signyourself
);
parseInt(fontSize);
const color = position?.options?.fontColor;
let updateColorInRgb;
if (color === "red") {
updateColorInRgb = rgb(1, 0, 0);
} else if (color === "black") {
updateColorInRgb = rgb(0, 0, 0);
} else if (color === "blue") {
updateColorInRgb = rgb(0, 0, 1);
} else if (color === "yellow") {
updateColorInRgb = rgb(0.9, 1, 0);
} else {
updateColorInRgb = rgb(0, 0, 0);
}
let textContent;
if (position?.options?.response) {
textContent = position.options?.response;
@@ -1500,6 +1569,8 @@ 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);
@@ -1508,32 +1579,41 @@ export const multiSignEmbed = async (
} else if (position?.options?.defaultValue) {
dropdown.select(position?.options?.defaultValue);
}
// Define the default appearance string
// Example format: `/FontName FontSize Tf 0 g` where:
// - `/FontName` is the name of the font (e.g., `/Helv` for Helvetica)
// - `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`;
// Set the default appearance for the dropdown field
dropdown.acroField.setDefaultAppearance(defaultAppearance);
dropdown.setFontSize(fontsize);
const dropdownObj = {
x: xPos(position),
y: yPos(position),
width: widgetWidth,
height: widgetHeight
};
const dropdownOption = getImagePosition(page, dropdownObj, 1);
// page.drawImage(img, imageOptions);
dropdown.addToPage(page, dropdownOption);
const dropdownOption = getWidgetPosition(page, dropdownObj, 1);
const dropdownSelected = { ...dropdownOption, font: font };
dropdown.defaultUpdateAppearances(font);
dropdown.addToPage(page, dropdownSelected);
dropdown.enableReadOnly();
} else if (position.type === radioButtonWidget) {
const font = await pdfDoc.embedFont("Helvetica");
const radioRandomId = "radio" + randomId();
const radioGroup = form.createRadioGroup(radioRandomId);
let radioOptionGapFromTop;
const optionsFontSize = 16;
const radioTextGapFromLeft = 20;
const radioSize = 18;
const optionsFontSize = fontSize || 13;
const radioTextGapFromLeft = fontSize + 5 || 20;
const radioSize = fontSize;
let y = yPos(position);
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
if (ind > 0) {
y = y + radioOptionGapFromTop;
} else {
radioOptionGapFromTop = 25;
radioOptionGapFromTop = fontSize + 10 || 25;
}
if (!position?.options?.isHideLabel) {
// below line of code is used to embed label with radio button in pdf
@@ -1545,7 +1625,7 @@ export const multiSignEmbed = async (
1,
page.getSize(),
optionsFontSize,
rgb(0, 0, 0),
updateColorInRgb,
font,
page
);
@@ -1559,7 +1639,7 @@ export const multiSignEmbed = async (
height: radioSize
};
radioObj = getImagePosition(page, radioObj, 1);
radioObj = getWidgetPosition(page, radioObj, 1);
radioGroup.addOptionToPage(item, page, radioObj);
});
}
@@ -1577,7 +1657,7 @@ export const multiSignEmbed = async (
height: widgetHeight
};
const imageOptions = getImagePosition(page, signature, 1);
const imageOptions = getWidgetPosition(page, signature, 1);
page.drawImage(img, imageOptions);
}
});
@@ -1781,29 +1861,26 @@ export const handleCopyNextToWidget = (
setXyPostion,
userId
) => {
const isSigners = xyPostion.some((data) => data.signerPtr);
let filterSignerPos;
//get position of previous widget and create new widget next to that widget on same data except
// xPosition and key
let newpos = position;
let newposition = position;
const calculateXPosition =
parseInt(position.xPosition) +
defaultWidthHeight(widgetType).width +
resizeBorderExtraWidth();
const newId = randomId();
newpos = { ...newpos, xPosition: calculateXPosition, key: newId };
newposition = { ...newposition, xPosition: calculateXPosition, key: newId };
//if condition to create widget in request-sign flow
if (isSigners) {
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
}
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder.filter(
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
const getPlaceHolder = filterSignerPos && filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder?.filter(
(data) => data.pageNumber === index
);
const getXYdata = getPageNumer[0].pos;
getXYdata.push(newpos);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer && getPageNumer[0]?.pos;
getXYdata.push(newposition);
if (getPageNumer && getPageNumer.length > 0) {
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === index) {
return { ...obj, pos: getXYdata };
@@ -1821,9 +1898,8 @@ export const handleCopyNextToWidget = (
setXyPostion(newUpdateSigner);
}
} else {
// else condition to create widget in sign-yourself flow
let getXYdata = xyPostion[index].pos;
getXYdata.push(newpos);
let getXYdata = xyPostion[index]?.pos || [];
getXYdata.push(newposition);
const updatePlaceholder = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: getXYdata };
@@ -1905,14 +1981,14 @@ export function replaceMailVaribles(subject, body, variables) {
return result;
}
export const copytoData = (text) => {
export const copytoData = (url) => {
// navigator.clipboard.writeText(text);
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
navigator.clipboard.writeText(url);
} else {
// Fallback for browsers that don't support navigator.clipboard
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.value = url;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
@@ -2124,9 +2200,11 @@ function compensateRotation(
font,
page
) {
// Ensure pageRotation is between 0 and 360 degrees
pageRotation = ((pageRotation % 360) + 360) % 360;
let rotationRads = (pageRotation * Math.PI) / 180;
//These coords are now from bottom/left
// Coordinates are from bottom-left
let coordsFromBottomLeft = { x: x / scale };
if (pageRotation === 90 || pageRotation === 270) {
coordsFromBottomLeft.y = dimensions.width - (y + fontSize) / scale;
@@ -2136,6 +2214,7 @@ function compensateRotation(
let drawX = null;
let drawY = null;
if (pageRotation === 90) {
drawX =
coordsFromBottomLeft.x * Math.cos(rotationRads) -
@@ -2161,8 +2240,8 @@ function compensateRotation(
coordsFromBottomLeft.x * Math.sin(rotationRads) +
coordsFromBottomLeft.y * Math.cos(rotationRads) +
dimensions.height;
} else {
//no rotation
} else if (pageRotation === 0 || pageRotation === 360) {
// No rotation or full rotation
drawX = coordsFromBottomLeft.x;
drawY = coordsFromBottomLeft.y;
}
@@ -2180,8 +2259,8 @@ function compensateRotation(
}
}
// `getImagePosition` is used to calulcate position of image type widget like x, y, width, height for pdf-lib
function getImagePosition(page, image, sizeRatio) {
// `getWidgetPosition` is used to calulcate position of image type widget like x, y, width, height for pdf-lib
function getWidgetPosition(page, image, sizeRatio) {
let pageWidth;
// pageHeight;
if ([90, 270].includes(page.getRotation().angle)) {
@@ -2225,3 +2304,155 @@ export const getContainerScale = (pdfOriginalWH, pageNumber, containerWH) => {
const containerScale = containerWH?.width / getPdfPageWidth?.width || 1;
return containerScale;
};
//function to get current laguage and set it in local
export const saveLanguageInLocal = (i18n) => {
const detectedLanguage = i18n.language || "en";
localStorage.setItem("i18nextLng", detectedLanguage);
};
//function to get default signatur eof current user from `contracts_Signature` class
export const getDefaultSignature = async (objectId) => {
try {
const query = new Parse.Query("contracts_Signature");
query.equalTo("UserId", {
__type: "Pointer",
className: "_User",
objectId: objectId
});
const result = await query.first();
if (result) {
const res = JSON.parse(JSON.stringify(result));
const defaultSignature = res?.ImageURL
? await getBase64FromUrl(res?.ImageURL, true)
: "";
const defaultInitial = res?.Initials
? await getBase64FromUrl(res?.Initials, true)
: "";
return {
status: "success",
res: {
defaultSignature: defaultSignature,
defaultInitial: defaultInitial
}
};
}
} catch (err) {
console.log("Error: error in fetch data in contracts_Signature", err);
return {
status: "error"
};
}
};
//function to rotate pdf page
export async function rotatePdfPage(
url,
rotateDegree,
pageNumber,
pdfRotateBase64
) {
let file;
//condition to handle pdf base64 in arrayBuffer format
if (pdfRotateBase64) {
file = base64ToArrayBuffer(pdfRotateBase64);
} else {
//condition to handle pdf url in arrayBuffer format
file = await convertPdfArrayBuffer(url);
}
// Load the existing PDF
const pdfDoc = await PDFDocument.load(file);
// Get the page according to page number
const page = pdfDoc.getPage(pageNumber);
//get current page rotation angle
const currentRotation = page.getRotation().angle;
// Apply the rotation in the counterclockwise direction
let newRotation = (currentRotation + rotateDegree) % 360;
// Adjust for negative angles to keep within 0-359 range
if (newRotation < 0) {
newRotation += 360;
}
page.setRotation(degrees(newRotation));
const pdfbase64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
//convert base64 to arraybuffer is used in pdf-lib
//pdfbase64 is used to show pdf rotated format
const arrayBuffer = base64ToArrayBuffer(pdfbase64);
//`base64` is used to show pdf
return { arrayBuffer: arrayBuffer, base64: pdfbase64 };
}
function base64ToArrayBuffer(base64) {
// Decode the base64 string to a binary string
const binaryString = atob(base64);
// Create a new ArrayBuffer with the same length as the binary string
const len = binaryString.length;
const bytes = new Uint8Array(len);
// Convert the binary string to a byte array
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Return the ArrayBuffer
return bytes.buffer;
}
export const convertBase64ToFile = async (pdfName, pdfBase64) => {
const fileName = sanitizeFileName(pdfName) + ".pdf";
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url();
return pdfUrl;
};
export const onClickZoomIn = (scale, zoomPercent, setScale, setZoomPercent) => {
setScale(scale + 0.1 * scale);
setZoomPercent(zoomPercent + 10);
};
export const onClickZoomOut = (
zoomPercent,
scale,
setZoomPercent,
setScale
) => {
if (zoomPercent > 0) {
if (zoomPercent === 10) {
setScale(1);
} else {
setScale(scale - 0.1 * scale);
}
setZoomPercent(zoomPercent - 10);
}
};
//function to use remove widgets from current page when user want to rotate page
export const handleRemoveWidgets = (
setSignerPos,
signerPos,
pageNumber,
setIsRotate
) => {
const updatedSignerPos = signerPos.map((placeholderObj) => {
return {
...placeholderObj,
placeHolder: placeholderObj?.placeHolder?.filter(
(data) => data?.pageNumber !== pageNumber
)
};
});
setSignerPos(updatedSignerPos);
setIsRotate({
status: false,
degree: 0
});
};
//function to show warning when user rotate page and there are some already widgets on that page
export const handleRotateWarning = (signerPos, pageNumber) => {
const placeholderExist = signerPos?.some((placeholderObj) =>
placeholderObj?.placeHolder?.some((data) => data?.pageNumber === pageNumber)
);
if (placeholderExist) {
return true;
} else {
return false;
}
};
+16 -3
View File
@@ -1,10 +1,23 @@
import logo from "../assets/images/logo.png";
import { isEnableSubscription } from "./const";
export function serverUrl_fn() {
let baseUrl;
if (isEnableSubscription) {
baseUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
} else {
baseUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/app";
}
return baseUrl;
}
export const appInfo = {
applogo: logo,
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
baseUrl: process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app",
baseUrl: serverUrl_fn(),
defaultRole: "contracts_User",
fbAppId: process.env.REACT_APP_FBAPPID
? `${process.env.REACT_APP_FBAPPID}`
+2
View File
@@ -11,3 +11,5 @@ export const isEnableSubscription =
: false;
export const isStaging =
window.location.origin === "https://staging-app.opensignlabs.com";
export const isPublicStaging =
window.location.origin === "https://staging.opensign.me";
+2 -4
View File
@@ -1,11 +1,9 @@
import axios from "axios";
import { serverUrl_fn } from "./appinfo";
const parseAppId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const serverUrl = serverUrl_fn();
export const SaveFileSize = async (size, imageUrl, tenantId) => {
//checking server url and save file's size
const tenantPtr = {
+31
View File
@@ -0,0 +1,31 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";
import LanguageDetector from "i18next-browser-languagedetector";
i18n
.use(Backend)
.use(LanguageDetector) // Use LanguageDetector directly without creating an instance
.use(initReactI18next)
.init({
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json"
},
fallbackLng: "en", // Fallback to English if no other language is detected
detection: {
// Specifies the default language to fall back to if the detected language is not available.
order: ["localStorage", "navigator"],
// Defines where the detected language should be cached.
caches: ["localStorage"]
},
ns: ["translation"], // default namespace
defaultNS: "translation", // default namespace
//Enables debug mode, which outputs detailed logs to the console about the translation process.
debug: false,
interpolation: {
escapeValue: false // Not needed for react as it escapes by default
},
whitelist: ["en", "fr"] // List of allowed languages
});
export default i18n;
+32 -8
View File
@@ -12,23 +12,35 @@ body {
}
.hide-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
}
@media screen and (max-width: 766px) {
.bBofGr {
width: 14px !important;
}
}
/* below css class replace bootstrap background color with daisy */
.op-bg-primary {
@apply bg-primary;
}
.op-bg-secondary {
@apply bg-secondary;
}
.op-bg-info {
@apply bg-info;
}
.op-bg-success{
.op-bg-success {
@apply bg-success;
}
.op-bg-warning {
@apply bg-warning;
}
@@ -37,25 +49,37 @@ body {
.op-text-primary {
@apply text-primary;
}
.op-text-secondary {
@apply text-secondary;
}
.op-text-info {
@apply text-info;
}
.op-text-success{
.op-text-accent {
@apply text-accent;
}
.op-text-success {
@apply text-success;
}
.op-text-warning {
@apply text-warning;
}
.op-border-primary{
@apply border-primary
.op-border-primary {
@apply border-primary;
}
/* CSS for scrollbar customization */
* {
scrollbar-width: thin; /* For Firefox */
scrollbar-color: gray; /* For Firefox */
scrollbar-width: thin;
/* For Firefox */
scrollbar-color: gray;
/* For Firefox */
}
/* For webkit-based browsers like Chrome, Safari */
+5 -4
View File
@@ -16,13 +16,14 @@ import {
import DragElement from "./components/pdf/DragElement";
import TagManager from "react-gtm-module";
import Parse from "parse";
import './polyfills'
import "./polyfills";
import { serverUrl_fn } from "./constant/appinfo";
import "./i18n";
const appId = process.env.REACT_APP_APPID
? process.env.REACT_APP_APPID
: "opensign";
const serverUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const serverUrl = serverUrl_fn();
Parse.initialize(appId);
Parse.serverURL = serverUrl;
+10 -9
View File
@@ -10,7 +10,7 @@ export default function reportJson(id) {
"Owner",
"Signers"
];
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
switch (id) {
@@ -325,9 +325,7 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-plus",
redirectUrl: "placeHolderSign",
action: "redirect",
selector: "reactourSecond",
message:
"Click the 'Use' button to create a new document from an existing template. "
selector: "reactourSecond"
},
{
btnId: "1631",
@@ -337,9 +335,7 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-envelope",
redirectUrl: "",
action: "bulksend",
selector: "tourbulksend",
message:
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
selector: "tourbulksend"
},
{
btnId: "2234",
@@ -349,8 +345,6 @@ export default function reportJson(id) {
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
action: "option",
selector: "reactourThird",
message:
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but wont affect existing documents.Use the Delete button you can delete template. ",
subaction: [
{
btnId: "2434",
@@ -360,6 +354,13 @@ export default function reportJson(id) {
redirectUrl: "template",
action: "redirect"
},
{
btnId: "2434",
btnLabel: "Embed",
hoverLabel: "Embed",
btnIcon: "fa-light fa-code",
action: "Embed"
},
{
btnId: "1834",
btnLabel: "Delete",
+9 -16
View File
@@ -19,9 +19,7 @@ const dashboardJson = [
key: "count",
Redirect_type: "Report",
Redirect_id: "4Hhwbp482K",
tourSection: "tourcard1",
tourMessage:
"Clicking on this card will take you to the list of documents awaiting your review."
tourSection: "tourcard1"
}
}
},
@@ -38,13 +36,11 @@ const dashboardJson = [
queryType: "",
class: "contracts_Document",
query:
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"SignedUrl":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
key: "count",
Redirect_type: "Report",
Redirect_id: "1MwEuxLEkF",
tourSection: "tourcard2",
tourMessage:
"Clicking on this card will take you to a list of documents awaiting signature."
tourSection: "tourcard2"
}
}
},
@@ -53,10 +49,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "5Go51Q7T8r",
label: "Recent signature requests",
data: {
tourSection: "tourreport1",
tourMessage:
"This is a list of documents that are waiting for your signature."
tourSection: "tourreport1"
}
}
},
@@ -65,10 +60,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "d9k3UfYHBc",
label: "Recently sent for signatures",
data: {
tourSection: "tourreport2",
tourMessage:
"This is a list of documents you've sent to other parties for signature."
tourSection: "tourreport2"
}
}
},
@@ -77,10 +71,9 @@ const dashboardJson = [
widget: {
type: "report",
reportId: "kC5mfynCi4",
label: "Drafts",
data: {
tourSection: "tourreport3",
tourMessage:
"This are documents you have started but have not finalized for sending."
tourSection: "tourreport3"
}
}
}
+87 -14
View File
@@ -3,6 +3,7 @@ import { isStaging } from "../constant/const";
const plans = [
{
planName: "OPENSIGN™ FREE",
code: { monthly: "freeplan", yearly: "freeplan" },
img: "free.png",
currency: "",
monthlyPrice: "Free",
@@ -12,7 +13,7 @@ const plans = [
url: "",
target: "_blank",
benefits: [
"Unlimited envelopes",
"Unlimited digital signatures",
"Sign documents yourself",
"Request signatures from others",
"14 field types",
@@ -26,12 +27,11 @@ const plans = [
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"Bulk send (upto 5 docs)",
"Public profiles",
"And much more"
],
yearlyBenefits: [
"Unlimited envelopes",
"Unlimited digital signatures",
"Sign documents yourself",
"Request signatures from others",
"14 field types",
@@ -45,13 +45,15 @@ const plans = [
"Decline document support",
"Email notifications",
"Recipient authentication using OTP",
"Bulk send (upto 5 docs)",
"Public profiles",
"And much more"
]
},
{
planName: "OPENSIGN™ PROFESSIONAL",
code: isStaging
? { monthly: "pro-weekly", yearly: "pro-yearly" }
: { monthly: "professional-monthly", yearly: "professional-yearly" },
img: "professional.png",
currency: "$",
monthlyPrice: "29.99",
@@ -73,10 +75,10 @@ const plans = [
"Webhooks",
"Zapier integration",
"API Access",
"100 API signatures included",
"upto 100 API signatures",
"Custom email templates",
"Auto reminders",
"Bulk send (upto 20 docs)",
"Bulk send (upto 100 docs)",
"Premium Public profile usernames",
"Embedding (coming soon)"
],
@@ -88,16 +90,17 @@ const plans = [
"Webhooks",
"Zapier integration",
"API Access",
"240 API signatures included",
"upto 240 API signatures",
"Custom email templates",
"Auto reminders",
"Bulk send (upto 20 docs)",
"Bulk send (upto 240 docs)",
"Premium Public profile usernames",
"Embedding (coming soon)"
]
},
{
planName: "OPENSIGN™ TEAMS",
code: { monthly: "teams-monthly", yearly: "teams-yearly" },
img: "teams.png",
currency: "$",
monthlyPrice: `39.99<sup style="font-size: 17px;">/user</sup>`,
@@ -105,31 +108,31 @@ const plans = [
subtitle: "Exclusive Access to advanced features.",
btn: { text: "Subscribe", color: "op-btn-accent" },
url: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
yearlyUrl: isStaging
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
target: "_self",
benefits: [
"Everything in OpenSign™ professional",
"100 API signatures included",
"upto 100 API signatures",
"Teams and Organizations",
"Share Templates with teams",
"Share Templates with individuals",
"DocumentId removal from signed docs",
"Bulk send (upto 50 docs)",
"Bulk send (upto 100 docs)",
"Request Payments (coming soon)",
"Mobile app (coming soon)"
],
yearlyBenefits: [
"Everything in OpenSign™ professional",
"500 API signatures included",
"upto 500 API signatures",
"Teams and Organizations",
"Share Templates with teams",
"Share Templates with individuals",
"DocumentId removal from signed docs",
"Bulk send (upto 50 docs)",
"Bulk send (upto 500 docs)",
"Request Payments (coming soon)",
"Mobile app (coming soon)"
]
@@ -171,4 +174,74 @@ export const validplan = {
"enterprise-monthly": true,
"enterprise-yearly": true
};
export const paidUrl = (plan) => {
const teamperiod = {
"team-weekly": "monthly",
"team-yearly": "yearly",
"teams-monthly": "monthly",
"teams-yearly": "yearly"
};
const period = teamperiod[plan] || "";
if (period) {
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const user = {
name: extUser?.Name,
email: extUser?.Email,
company: extUser?.Company,
phone: extUser?.Phone
};
// console.log("userDetails ", userDetails);
const fullname = user && user.name ? user.name.split(" ") : "";
const firstname = fullname?.[0]
? "first_name=" + encodeURIComponent(fullname?.[0])
: "";
const lastname = fullname?.[1]
? "&last_name=" + encodeURIComponent(fullname?.[1])
: "";
const name = firstname ? firstname + lastname : "";
const email =
user && user.email ? "&email=" + encodeURIComponent(user.email) : "";
const company =
user && user.company
? "&company_name=" + encodeURIComponent(user.company)
: "";
const phone =
user && user.phone ? "&mobile=" + encodeURIComponent(user.phone) : "";
const allowedUsers =
localStorage.getItem("allowedUsers") &&
localStorage.getItem("allowedUsers") > 0
? localStorage.getItem("allowedUsers") - 1
: "";
const quantity = allowedUsers
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
quantity +
name +
email +
company +
phone;
if (user) {
localStorage.setItem("userDetails", JSON.stringify(user));
}
const url = {
monthly: isStaging
? "https://billing.zoho.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.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly"
};
const planurl = url[period] + details;
return planurl;
} else {
return "/subscription";
}
};
export default plans;
+18 -7
View File
@@ -14,8 +14,10 @@ import { useCookies } from "react-cookie";
import { fetchSubscription } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { showHeader } from "../redux/reducers/showHeader";
import { useTranslation } from "react-i18next";
const HomeLayout = () => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
@@ -32,6 +34,13 @@ const HomeLayout = () => {
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
const tenantId = localStorage.getItem("TenantId");
useEffect(() => {
const language = localStorage.getItem("i18nextLng");
i18n.changeLanguage(language);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (!tenantId) {
setIsUserValid(false);
@@ -86,7 +95,9 @@ const HomeLayout = () => {
domain: updateDomain
});
};
const handleNavigation = () => {
navigate("/subscription");
};
async function checkIsSubscribed() {
if (isEnableSubscription) {
const res = await fetchSubscription();
@@ -98,10 +109,10 @@ const HomeLayout = () => {
setIsUserValid(true);
setIsLoader(false);
} else {
navigate(`/subscription`);
handleNavigation(res.plan);
}
} else {
navigate(`/subscription`);
handleNavigation(res.plan);
}
} else {
setIsUserValid(true);
@@ -150,20 +161,20 @@ const HomeLayout = () => {
setTourConfigs([
{
selector: '[data-tut="reactourFirst"]',
content: `You have logged in successfully! Let's take a look.`,
content: t("tour-mssg.home-layout-1"),
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
{
selector: '[data-tut="tourbutton"]',
content: `To upload documents for self-signing or to request others signatures, simply select the respective buttons.`,
content: t("tour-mssg.home-layout-2"),
position: "top"
// style: { backgroundColor: "#abd4d2" },
},
...resArr,
{
selector: '[data-tut="reactourLast"]',
content: `You are ready to start using OpenSign! If you need support feel free to contact us.`,
content: t("tour-mssg.home-layout-3"),
position: "top"
// style: { backgroundColor: "#abd4d2" },
}
@@ -295,7 +306,7 @@ const HomeLayout = () => {
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-medium">Your session has expired.</p>
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
Login
{t("login")}
</button>
</div>
</ModalUi>
+76 -32
View File
@@ -2,13 +2,19 @@ import React, { useEffect, useState } from "react";
import Parse from "parse";
import { appInfo } from "../constant/appinfo";
import { NavLink, useNavigate } from "react-router-dom";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import {
getAppLogo,
openInNewTab,
saveLanguageInLocal
} from "../constant/Utils";
import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import Loader from "../primitives/Loader";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
const AddAdmin = () => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const dispatch = useDispatch();
const [showPassword, setShowPassword] = useState(false);
@@ -40,18 +46,20 @@ const AddAdmin = () => {
try {
const app = await getAppLogo();
if (app?.user === "exist") {
setErrMsg("Admin already exist.");
setErrMsg(t("admin-already-exist"));
}
} catch (err) {
setErrMsg("Something went wrong.");
setErrMsg(t("something-went-wrong-mssg"));
console.log("err in check user exist", err);
} finally {
setState((prev) => ({ ...prev, loading: false }));
}
};
const clearStorage = async () => {
if (Parse.User.current()) {
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
const baseUrl = localStorage.getItem("baseUrl");
const appid = localStorage.getItem("parseAppId");
@@ -61,6 +69,7 @@ const AddAdmin = () => {
const userSettings = localStorage.getItem("userSettings");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("appLogo", applogo);
@@ -124,7 +133,7 @@ const AddAdmin = () => {
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert("User already exists with this username!");
alert(t("already-exists-this-username"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
@@ -132,7 +141,7 @@ const AddAdmin = () => {
await Parse.User.requestPasswordReset(email).then(
async function (res) {
if (res.data === undefined) {
alert("Verification mail has been sent to your E-mail!");
alert(t("verification-code-sent"));
}
}
);
@@ -196,27 +205,27 @@ const AddAdmin = () => {
setState({
loading: false,
alertType: "success",
alertMsg: "Registered user successfully."
alertMsg: t("registered-user-successfully")
});
navigate(`/${menu.pageType}/${menu.pageId}`);
} else {
setState({
loading: false,
alertType: "danger",
alertMsg: "Role not found."
alertMsg: t("role-not-found")
});
}
} else {
setState({
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
alertMsg: t("do-not-access")
});
}
}
} catch (error) {
console.log("error in fetch extuser", error);
const msg = error.message || "Something went wrong.";
const msg = error.message || t("something-went-wrong-mssg");
setState({ loading: false, alertType: "danger", alertMsg: msg });
} finally {
setTimeout(() => setState({ loading: false, alertMsg: "" }), 2000);
@@ -246,7 +255,7 @@ const AddAdmin = () => {
};
return (
<div className="h-screen flex justify-center">
<Title title={"Add admin"} />
<Title title="Add admin" />
{state.loading ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
@@ -262,7 +271,7 @@ const AddAdmin = () => {
<form onSubmit={handleSubmit}>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<h2 className="text-[30px] text-center mt-3 font-medium">
Opensign Setup
{t("opensign-setup")}
</h2>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
@@ -270,7 +279,7 @@ const AddAdmin = () => {
rel="noopener noreferrer"
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
>
Join our discord server
{t("join-discord")}
<i
aria-hidden="true"
className="fa-brands fa-discord ml-1"
@@ -279,52 +288,76 @@ const AddAdmin = () => {
</NavLink>
<div className="px-6 py-3 text-xs">
<label className="block ">
Name <span className="text-[red] text-[13px]">*</span>
{t("name")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Email <span className="text-[red] text-[13px]">*</span>
{"email"}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
type="email"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
onChange={(e) =>
setEmail(
e.target.value?.toLowerCase()?.replace(/\s/g, "")
)
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Phone <span className="text-[red] text-[13px]">*</span>
{t("phone")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={phone}
onChange={(e) => setPhone(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Company <span className="text-[red] text-[13px]">*</span>
{t("company")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={company}
onChange={(e) => setCompany(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Job Title{" "}
{t("job-title")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
@@ -332,11 +365,16 @@ const AddAdmin = () => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={jobTitle}
onChange={(e) => setJobTitle(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Password <span className="text-[red] text-[13px]">*</span>
{t("password")}
<span className="text-[red] text-[13px]">*</span>
</label>
<div className="relative">
<input
@@ -345,6 +383,10 @@ const AddAdmin = () => {
name="password"
value={password}
onChange={(e) => handlePasswordChange(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<span
@@ -365,24 +407,22 @@ const AddAdmin = () => {
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8
characters long
{lengthValid ? "✓" : "✗"} {t("password-length")}
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain
uppercase letter, lowercase letter, digit
{caseDigitValid ? "✓" : "✗"} {t("password-case")}
</p>
<p
className={`${
specialCharValid ? "text-green-600" : "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should contain
special character
{specialCharValid ? "✓" : "✗"}{" "}
{t("password-special-char")}
</p>
</div>
)}
@@ -393,13 +433,17 @@ const AddAdmin = () => {
id="termsandcondition"
checked={isAuthorize}
onChange={(e) => setIsAuthorize(e.target.checked)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
I agree to the
{t("agreee")}
</label>
<span
className="underline cursor-pointer ml-1"
@@ -409,7 +453,7 @@ const AddAdmin = () => {
)
}
>
Terms of Service
{t("term")}
</span>
<span>.</span>
</div>
@@ -417,15 +461,15 @@ const AddAdmin = () => {
<input
type="checkbox"
className="op-checkbox op-checkbox-sm"
id="termsandcondition"
id="subscribetoopensign"
checked={isSubscribeNews}
onChange={(e) => setIsSubscribeNews(e.target.checked)}
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
htmlFor="subscribetoopensign"
>
Subscribe to OpenSign newsletter
{t("subscribe-to-opensign")}
</label>
</div>
</div>
@@ -435,7 +479,7 @@ const AddAdmin = () => {
className="op-btn op-btn-primary w-full"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Next"}
{state.loading ? t("loading") : t("next")}
</button>
</div>
</div>
+21 -13
View File
@@ -2,8 +2,10 @@ import React, { useState } from "react";
import Parse from "parse";
import { Navigate } from "react-router-dom";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
function ChangePassword() {
const { t } = useTranslation();
const [currentpassword, setCurrentPassword] = useState("");
const [newpassword, setnewpassword] = useState("");
const [confirmpassword, setconfirmpassword] = useState("");
@@ -27,23 +29,23 @@ function ChangePassword() {
await Parse.User.become(_user.sessionToken);
localStorage.setItem("accesstoken", _user.sessionToken);
}
alert("Password updated successfully.");
alert(t("password-update-alert-1"));
})
.catch((error) => {
console.log("err", error);
alert("Something went wrong.");
alert(t("something-went-wrong-mssg"));
});
});
} else {
alert("Your current password is missing or incorrect.");
alert(t("password-update-alert-2"));
}
})
.catch((error) => {
alert("Your current password is missing or incorrect.");
alert(t("password-update-alert-3"));
console.error("Error while logging in user", error);
});
} else {
alert("Your password and confirmation password do not match.");
alert(t("password-update-alert-4"));
}
} catch (error) {
console.log("err", error);
@@ -56,13 +58,13 @@ function ChangePassword() {
<div className="w-full bg-base-100 text-base-content shadow rounded-box p-2">
<Title title="Change Password" />
<div className="text-xl font-bold border-b-[1px] border-gray-300">
Change Password
{t("change-password")}
</div>
<div className="m-2">
<form onSubmit={handleSubmit} className=" flex flex-col gap-y-2">
<div>
<label htmlFor="currentpassword" className="block text-xs ml-1">
Current Password
{t("current-password")}
</label>
<input
type="password"
@@ -70,13 +72,15 @@ function ChangePassword() {
value={currentpassword}
onChange={(e) => setCurrentPassword(e.target.value)}
className="op-input op-input-bordered op-input-sm text-xs w-full"
placeholder="Current Password"
placeholder={t("current-password")}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div>
<label htmlFor="newpassword" className="text-xs block ml-1">
New Password
{t("new-password")}
</label>
<input
type="password"
@@ -84,13 +88,15 @@ function ChangePassword() {
value={newpassword}
onChange={(e) => setnewpassword(e.target.value)}
className="op-input op-input-bordered op-input-sm text-xs w-full"
placeholder="New Password"
placeholder={t("new-password")}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div>
<label htmlFor="newpassword" className="text-xs block ml-1">
Confirm Password
{t("confirm-password")}
</label>
<input
type="password"
@@ -98,7 +104,9 @@ function ChangePassword() {
className="op-input op-input-bordered op-input-sm text-xs w-full"
value={confirmpassword}
onChange={(e) => setconfirmpassword(e.target.value)}
placeholder="Confirm Password"
placeholder={t("confirm-password")}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -106,7 +114,7 @@ function ChangePassword() {
type="submit"
className="op-btn op-btn-primary shadow-md mt-2"
>
Change Password
{t("change-password")}
</button>
</form>
</div>
+3 -1
View File
@@ -6,8 +6,10 @@ import { useDispatch } from "react-redux";
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
import dashboardJson from "../json/dashboardJson";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const Dashboard = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const dispatch = useDispatch();
const { id } = useParams();
@@ -40,7 +42,7 @@ const Dashboard = () => {
.map((col) => {
return {
selector: `[data-tut=${col.widget.data.tourSection}]`,
content: col.widget.data.tourMessage,
content: t(`tour-mssg.${col.widget.label}`),
position: "top"
// style: { backgroundColor: "#abd4d2" },
};
+1 -5
View File
@@ -215,11 +215,7 @@ const DebugPdf = () => {
};
return (
<div>
{copied && (
<Alert type="success">
<span className="ml-3">Copied</span>
</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} />
+23 -15
View File
@@ -9,20 +9,21 @@ import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo } from "../constant/Utils";
import { useTranslation } from "react-i18next";
function ForgotPassword() {
const { t } = useTranslation();
const dispatch = useDispatch();
const navigate = useNavigate();
const [state, setState] = useState({
email: "",
password: "",
hideNav: ""
});
const [state, setState] = useState({ email: "", password: "", hideNav: "" });
const [sentStatus, setSentStatus] = useState("");
const [image, setImage] = useState();
const handleChange = (event) => {
const { name, value } = event.target;
let { name, value } = event.target;
if (name === "email") {
value = value?.toLowerCase()?.replace(/\s/g, "");
}
setState({ ...state, [name]: value });
};
@@ -60,6 +61,11 @@ function ForgotPassword() {
// eslint-disable-next-line
}, []);
const saveLogo = async () => {
try {
await Parse.User.logOut();
} catch (err) {
console.log("err while logging out ", err);
}
if (isEnableSubscription) {
const app = await getAppLogo();
if (app?.logo) {
@@ -75,12 +81,10 @@ function ForgotPassword() {
<div>
<Title title="Forgot password page" />
{sentStatus === "success" && (
<Alert type="success">
Reset password link has been sent to your email id
</Alert>
<Alert type="success">{t("reset-password-alert-1")}</Alert>
)}
{sentStatus === "failed" && (
<Alert type={"danger"}>Please setup email adapter </Alert>
<Alert type={"danger"}>{t("reset-password-alert-2")}</Alert>
)}
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
@@ -96,19 +100,23 @@ function ForgotPassword() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
<h2 className="text-[30px] mt-6">{t("welcome")}</h2>
<span className="text-[12px] text-[#878787]">
Reset Your Password
{t("reset-password-alert-3")}
</span>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="px-6 py-4">
<label className="block text-xs">Email</label>
<label className="block text-xs">{t("email")}</label>
<input
type="email"
name="email"
className="op-input op-input-bordered op-input-sm w-full"
value={state.email}
onChange={handleChange}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
@@ -116,13 +124,13 @@ function ForgotPassword() {
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
<button
onClick={() => navigate("/", { replace: true })}
className="op-btn op-btn-secondary"
>
Login
{t("login")}
</button>
</div>
</form>
+87 -84
View File
@@ -1,8 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { formJson } from "../json/FormJson";
import AddUser from "../components/AddUser";
import sanitizeFileName from "../primitives/sanitizeFileName";
import Parse from "parse";
import DropboxChooser from "../components/shared/fields/DropboxChoose";
import Alert from "../primitives/Alert";
@@ -19,24 +17,35 @@ import ModalUi from "../primitives/ModalUi";
import { Tooltip } from "react-tooltip";
import Upgrade from "../primitives/Upgrade";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
// `generatePdfName` is used to generate file name
function generatePdfName(length) {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
// `Form` render all type of Form on this basis of their provided in path
function Form() {
const { id } = useParams();
if (id === "lM0xRnM3iE") {
return <AddUser />;
const config = formJson[id];
if (config) {
return <Forms {...config} />;
} else {
const config = formJson[id];
if (config) {
return <Forms {...config} />;
} else {
return <PageNotFound prefix={"Form"} />;
}
return <PageNotFound prefix={"Form"} />;
}
}
const Forms = (props) => {
const { t } = useTranslation();
const maxFileSize = 20;
const abortController = new AbortController();
const inputFileRef = useRef(null);
@@ -81,8 +90,8 @@ const Forms = (props) => {
}, []);
const fetchSubscription = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
}
};
@@ -109,9 +118,7 @@ const Forms = (props) => {
if (typeof files[0] !== "undefined") {
const mb = Math.round(files[0].size / Math.pow(1024, 2));
if (mb > maxFileSize) {
alert(
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
);
alert(`${t("file-alert-1")} ${maxFileSize} MB`);
setFileUpload("");
e.target.value = "";
return;
@@ -133,9 +140,8 @@ const Forms = (props) => {
// console.log("err ", err);
try {
setIsDecrypting(true);
const fileName = files?.[0].name;
const size = files?.[0].size;
const name = sanitizeFileName(fileName);
const name = generatePdfName(16);
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
let formData = new FormData();
formData.append("file", files[0]);
@@ -226,9 +232,8 @@ const Forms = (props) => {
useObjectStreams: false
});
setfileload(true);
const fileName = files[0].name;
const size = files[0].size;
const name = sanitizeFileName(fileName);
const name = generatePdfName(16);
const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File(
pdfName,
@@ -292,7 +297,7 @@ const Forms = (props) => {
}
}
} else {
alert("Please select file.");
alert(t("file-alert-2"));
return false;
}
} catch (error) {
@@ -303,9 +308,8 @@ const Forms = (props) => {
const handleFileUpload = async (file) => {
setfileload(true);
const fileName = file.name;
const size = file.size;
const name = sanitizeFileName(fileName);
const name = generatePdfName(16);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
@@ -344,14 +348,11 @@ const Forms = (props) => {
if (mb > maxFileSize) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
);
alert(`${t("file-alert-1")}${maxFileSize} MB`);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const name = generatePdfName(16);
const parseFile = new Parse.File(name, { uri: url });
try {
@@ -438,13 +439,11 @@ const Forms = (props) => {
setIsErr(true);
} finally {
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
setTimeout(() => setIsAlert(false), 1000);
setIsSubmit(false);
}
} else {
alert("Please wait while the document is being uploaded.");
alert(t("file-alert-3"));
}
};
@@ -492,9 +491,8 @@ const Forms = (props) => {
setIsPassword(false);
setfileload(true);
try {
const fileName = formData?.file?.name;
const size = formData?.file?.size;
const name = sanitizeFileName(fileName);
const name = generatePdfName(16);
const url = "https://ai.nxglabs.in/decryptpdf"; //
let Data = new FormData();
Data.append("file", formData?.file);
@@ -569,7 +567,7 @@ const Forms = (props) => {
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
{isErr
? "Something went wrong please try again!"
? t("something-went-wrong-mssg")
: `${props.msgVar} created successfully!`}
</Alert>
)}
@@ -582,12 +580,12 @@ const Forms = (props) => {
<ModalUi
isOpen={isPassword}
handleClose={() => handeCloseModal()}
title={"Enter Pdf Password"}
title={t("enter-pdf-password")}
>
<form onSubmit={handlePasswordSubmit}>
<div className="px-6 pt-3 pb-2">
<label className="mb-2 text-xs text-base-content">
Password
{t("password")}
</label>
<input
type="text"
@@ -596,6 +594,10 @@ const Forms = (props) => {
onChange={(e) => handleStrInput(e)}
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter pdf password"
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<p
@@ -605,18 +607,20 @@ const Forms = (props) => {
: "text-transparent pointer-events-none"
} ml-2 text-[11px] `}
>
Please provide correct password
{t("correct-password")}
</p>
</div>
<div className="px-6 mb-3">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
</div>
</form>
</ModalUi>
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
<h1 className="text-[20px] font-semibold mb-4">
{t(`form-name.${props?.title}`)}
</h1>
{fileload && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
@@ -631,13 +635,13 @@ const Forms = (props) => {
{isDecrypting && (
<div className="flex items-center gap-x-2">
<span className="text-base-content text-sm">
Decrypting pdf please wait...
{t("decrypting-pdf")}
</span>
</div>
)}
<div className="text-xs">
<label className="block">
{`File (pdf, png, jpg, jpeg${
{`${t("report-heading.File")} (${t("file-type")} ${
isEnableSubscription ? ", docx)" : ")"
}`}
<span className="text-red-500 text-[13px]">*</span>
@@ -646,7 +650,7 @@ const Forms = (props) => {
<div className="flex gap-1 justify-center items-center">
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-[13px]">
<div className="break-all cursor-default">
file selected: {getFileName(fileupload)}
{t("file-selected")}: {getFileName(fileupload)}
</div>
<div
onClick={() => setFileUpload("")}
@@ -674,6 +678,10 @@ const Forms = (props) => {
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
: "application/pdf,image/png,image/jpeg"
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
@@ -688,8 +696,8 @@ const Forms = (props) => {
<div className="text-xs mt-2">
<label className="block">
{props.title === "New Template"
? "Template Title"
: "Document Title"}
? t("template-title")
: t("document-title")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
@@ -697,12 +705,16 @@ const Forms = (props) => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
{props.title === "New Template" && (
<div className="text-xs mt-2">
<label className="block">Description</label>
<label className="block">{t("description")}</label>
<input
name="Description"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -720,13 +732,18 @@ const Forms = (props) => {
)}
<div className="text-xs mt-2">
<label className="block">
Note<span className="text-red-500 text-[13px]">*</span>
{t("report-heading.Note")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Note"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -740,7 +757,7 @@ const Forms = (props) => {
{props.title === "Request Signatures" && (
<div className="text-xs mt-2">
<label className="block">
Time To Complete (Days)
{t("time-to-complete")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
@@ -749,6 +766,10 @@ const Forms = (props) => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.TimeToCompleteDays}
onChange={(e) => handleStrInput(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -757,7 +778,7 @@ const Forms = (props) => {
<>
<div className="text-xs mt-2">
<label className="block">
Send In Order
{t("send-in-order")}
<a data-tooltip-id="sendInOrder-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>
@@ -765,44 +786,21 @@ const Forms = (props) => {
</a>
<Tooltip id="sendInOrder-tooltip" className="z-50">
<div className="max-w-[200px] md:max-w-[450px]">
<p className="font-bold">Send in Order</p>
<p>
Choose how you want the signing requests to be sent to
the document signers:
</p>
<p className="font-bold">{t("send-in-order")}</p>
<p>{t("send-in-order-help.p1")}</p>
<p className="p-[5px]">
<ol className="list-disc">
<li>
<span className="font-bold">Yes:</span>
<span>
Selecting this option will send the signing
request to the first signer initially. Once the
first signer completes their part, the next
signer in the sequence will receive the request.
This process continues until all signers have
signed the document. This method ensures that
the document is signed in a specific order.
</span>
<span className="font-bold">{t("yes")}:</span>
<span>{t("send-in-order-help.p2")}</span>
</li>
<li>
<span className="font-bold">No: </span>
<span>
Selecting this option will send the signing
links to all signers simultaneously. Every
signer can sign the document at their
convenience, regardless of whether other signers
have completed their signatures. This method is
faster but does not enforce any signing order
among the participants.
</span>
<span className="font-bold">{t("no")}: </span>
<span>{t("send-in-order-help.p3")}</span>
</li>
</ol>
</p>
<p>
Select the option that best suits the needs of your
document processing.
</p>
<p>{t("send-in-order-help.p4")}</p>
</div>
</Tooltip>
</label>
@@ -815,7 +813,7 @@ const Forms = (props) => {
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div className="text-center">Yes</div>
<div className="text-center">{t("yes")}</div>
</div>
<div className="flex items-center gap-2 ml-2 mb-1">
<input
@@ -826,7 +824,7 @@ const Forms = (props) => {
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div className="text-center">No</div>
<div className="text-center">{t("no")}</div>
</div>
</div>
{isEnableSubscription && (
@@ -838,7 +836,8 @@ const Forms = (props) => {
: "font-semibold text-gray-300"
}
>
Auto reminder{" "}
{t("auto-reminder")}
{" "}
{!isSubscribe && isEnableSubscription && <Upgrade />}
</span>
<label
@@ -860,7 +859,7 @@ const Forms = (props) => {
{formData?.autoreminder === true && (
<div className="text-xs mt-2">
<label className="block">
Remind once in every (Days)
{t("remind-once")}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
@@ -869,6 +868,10 @@ const Forms = (props) => {
name="remindOnceInEvery"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
onChange={handleStrInput}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -883,13 +886,13 @@ const Forms = (props) => {
type="submit"
disabled={isSubmit}
>
Next
{t("next")}
</button>
<div
className="op-btn op-btn-ghost"
onClick={() => handleCancel()}
>
Cancel
{t("cancel")}
</div>
</div>
</form>
+310 -52
View File
@@ -9,32 +9,56 @@ import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
const tourSteps = [
{
selector: '[data-tut="apisubscribe"]',
content: "Upgrade now to generate API token"
}
];
import { useTranslation } from "react-i18next";
import Parse from "parse";
function GenerateToken() {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const origin = window.location.origin;
const { t } = useTranslation();
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
const [apiToken, SetApiToken] = useState("");
const [isLoader, setIsLoader] = useState(true);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isModal, setIsModal] = useState({
generateapi: false,
buyapis: false,
unauthorized: false
});
const [isSubscribe, setIsSubscribe] = useState({
plan: "",
isValid: false,
adminId: "",
adminName: "",
adminEmail: ""
});
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
const [amount, setAmount] = useState({
quantity: 500,
priceperapi: 0.15,
allowedcredits: 0,
addoncredits: 0,
totalcredits: 0,
price: (75.0).toFixed(2)
});
const [isFormLoader, setIsFormLoader] = useState(false);
const [apitestToken, setApiTestToken] = useState("");
const quantityList = [500, 1000, 5000, 50000];
useEffect(() => {
fetchToken();
// eslint-disable-next-line
}, []);
const tourSteps = [
{
selector: '[data-tut="apisubscribe"]',
content: t("tour-mssg.generate-token")
}
];
const fetchToken = async () => {
try {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
@@ -44,22 +68,40 @@ function GenerateToken() {
};
const res = await axios.post(url, {}, { headers: headers });
if (res) {
SetApiToken(res.data?.result?.result);
const resCredits = await Parse.Cloud.run("allowedcredits");
const allowedcredits = resCredits?.allowedcredits || 0;
const addoncredits = resCredits?.addoncredits || 0;
const totalcredits = allowedcredits + addoncredits;
setAmount((obj) => ({
...obj,
allowedcredits: allowedcredits,
addoncredits: addoncredits,
totalcredits: totalcredits
}));
SetApiToken(res?.data?.result?.result);
}
try {
const body = { email: Parse?.User?.current()?.getEmail() || "" };
const testurl = "https://sandbox.opensignlabs.com/api/gettesttoken";
const testRes = await axios.post(testurl, body);
setApiTestToken(testRes.data?.token);
} catch (err) {
console.log("err in fetch test token", err);
}
setIsLoader(false);
} catch (err) {
SetApiToken();
setIsLoader(false);
console.log("Err", err);
} finally {
setIsLoader(false);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!isSubscribe && isEnableSubscription) {
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
setIsTour(true);
} else {
setIsLoader(true);
setIsModal(false);
setIsModal((obj) => ({ ...obj, generateapi: false }));
try {
const url = parseBaseUrl + "functions/generateapitoken";
const headers = {
@@ -70,13 +112,13 @@ function GenerateToken() {
const res = await axios.post(url, {}, { headers: headers });
if (res) {
SetApiToken(res.data.result.token);
setIsAlert({ type: "success", msg: "Token generated successfully." });
setIsAlert({ type: "success", msg: t("token-generated") });
} else {
console.error("Error while generating Token");
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
}
} catch (error) {
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
console.log("while generating Token", error);
} finally {
setIsLoader(false);
@@ -89,21 +131,116 @@ function GenerateToken() {
const copytoclipboard = (text) => {
copytoData(text);
setIsAlert({ type: "success", msg: "Copied" });
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
}, 1500); // Reset copied state after 1.5 seconds
setIsAlert({ type: "success", msg: t("copied") });
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
};
const handleModal = () => {
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
setIsTour(true);
} else {
setIsModal((obj) => ({ ...obj, generateapi: !obj.generateapi }));
}
};
const handleModal = () => setIsModal(!isModal);
const handleBuyAPIsModal = () => {
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
setIsTour(true);
} else {
const extUser =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (isSubscribe?.adminId && extUser?.objectId === isSubscribe?.adminId) {
setIsModal((obj) => ({ ...obj, buyapis: !obj.buyapis }));
} else {
setIsSubscribe((obj) => ({
...obj,
adminName: extUser?.CreatedBy?.name,
adminEmail: extUser?.CreatedBy?.email
}));
setIsModal((obj) => ({ ...obj, unauthorized: !obj.unauthorized }));
}
}
};
const handleCloseModal = () => {
setIsModal({ generateapi: false, buyapis: false, unauthorized: false });
};
const handlePricePerAPIs = (e) => {
const quantity = e.target?.value;
const price =
quantity > 0
? (Math.round(quantity * amount.priceperapi * 100) / 100).toFixed(2)
: (Math.round(500 * amount.priceperapi * 100) / 100).toFixed(2);
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
};
const handleAddOnApiSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsFormLoader(true);
try {
const resAddon = await Parse.Cloud.run("buycredits", {
credits: amount.quantity
});
if (resAddon) {
const _resAddon = JSON.parse(JSON.stringify(resAddon));
if (_resAddon.status === "success") {
setAmount((obj) => ({
...obj,
quantity: 500,
priceperapi: 0.15,
price: (75.0).toFixed(2),
totalcredits: obj?.totalcredits + _resAddon.addon,
addoncredits: obj?.addoncredits + _resAddon.addon
}));
}
}
setIsModal((obj) => ({ ...obj, buyapis: false }));
} catch (err) {
console.log("Err in buy addon", err);
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
} finally {
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
setIsFormLoader(false);
}
};
const handleGenerateTestToken = async (e) => {
e.preventDefault();
setIsLoader(true);
// setIsModal((obj) => ({ ...obj, generateapi: false }));
try {
const url = "https://sandbox.opensignlabs.com/api/generatetesttoken";
const headers = {
"Content-Type": "application/json",
token: apitestToken || "1"
};
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
const body = {
name: extUser?.Name,
email: extUser?.Email,
phone: extUser?.Phone,
company: extUser?.Company,
jobTitle: extUser?.JobTitle
};
const res = await axios.post(url, body, { headers: headers });
if (res) {
setApiTestToken(res.data?.token);
setIsAlert({ type: "success", msg: t("token-generated") });
} else {
console.error("Error while generating Token");
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
}
} catch (error) {
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
console.log("while generating Token", error);
} finally {
setIsLoader(false);
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
}
};
return (
<React.Fragment>
<Title title={"API Token"} />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{isLoader ? (
<div className="flex justify-center items-center h-screen">
<Loader />
@@ -112,31 +249,68 @@ function GenerateToken() {
<>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign API{" "}
OpenSign {t("API")}{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
}
url="https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
isSubscribe={true}
/>
</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li className="flex flex-col md:flex-row justify-between items-center border-t-[1px] border-gray-300 break-all py-2">
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5">
<span className="ml-1">
<span className="font-medium">API Test Token : </span>
<span className="text-xs">
<Tooltip message={t("help-test-token")} />
</span>
</span>
<span id="token" className="md:text-end py-2 md:py-0">
<span
className="cursor-pointer"
onClick={() => copytoclipboard(apitestToken)}
>
{apitestToken ? apitestToken : "_____"}
</span>
<button
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
onClick={() => copytoclipboard(apitestToken)}
>
<i className="fa-light fa-copy"></i>
</button>
</span>
</div>
<button
onClick={handleGenerateTestToken}
className="op-btn op-btn-primary w-[210px] md:w-auto"
>
{apitestToken
? t("regenerate-test-token")
: t("generate-test-token")}
</button>
</li>
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5">
<span className="ml-1">API Token:</span>{" "}
<span className="ml-1">
<span className="font-medium">{t("api-token")} : </span>
<span className="text-xs">
<Tooltip message={t("help-api-token", { origin })} />
</span>
</span>
<span
id="token"
className={`${
isSubscribe
? ""
: "bg-white/20 pointer-events-none select-none"
isSubscribe?.plan === "freeplan"
? "bg-white/20 pointer-events-none select-none"
: ""
} md:text-end py-2 md:py-0`}
>
<span
className="cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken ? apiToken : "_____"}
{isSubscribe?.plan !== "freeplan" && apiToken
? apiToken
: "_____"}
</span>
<button
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
@@ -148,13 +322,30 @@ function GenerateToken() {
</div>
<button
onClick={apiToken ? handleModal : handleSubmit}
className="op-btn op-btn-primary"
className="op-btn op-btn-primary w-[210px] md:w-auto"
>
{apiToken ? "Regenerate Token" : "Generate Token"}
{apiToken ? t("regenerate-token") : t("generate-token")}
</button>
</li>
<div className="text-xs md:text-[15px] my-3 w-full md:w-[70%] flex-col md:flex-row flex items-center gap-x-5">
<span className="font-medium">
{t("remaining-credits")}{" "}
<span className="text-xs">
<Tooltip
message={t("remaining-credits-help", {
allowedcredits: amount?.allowedcredits || 0,
addoncredits: amount?.addoncredits || 0
})}
/>
</span>
</span>
<span className="md:text-end py-2 md:py-0">
{amount.totalcredits}
</span>
</div>
<hr />
</ul>
<div className="flex items-center justify-center">
<div className="flex flex-col md:flex-row items-center justify-center gap-0 md:gap-1">
<button
type="button"
onClick={() =>
@@ -162,20 +353,26 @@ function GenerateToken() {
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="op-btn op-btn-secondary mt-2 mb-3 px-8"
className="op-btn op-btn-secondary mt-2 md:mb-3 px-8 w-[210px] md:w-auto"
>
View Docs
{t("view-docs")}
</button>
<button
type="button"
onClick={() => handleBuyAPIsModal()}
className="op-btn op-btn-accent mt-2 mb-3 px-8 w-[210px] md:w-auto"
>
{t("buy-credits")}
</button>
</div>
<ModalUi
isOpen={isModal}
title={apiToken ? "Regenerate Token" : "Generate Token"}
isOpen={isModal.generateapi}
title={apiToken ? t("regenerate-token") : t("generate-token")}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-base-content">
Are you sure you want to regenerate token it will expire old
token?
{t("generate-token-alert")}
</div>
<hr className="bg-[#ccc] mt-4" />
<div className="flex items-center mt-3 gap-2 text-white">
@@ -183,19 +380,80 @@ function GenerateToken() {
onClick={handleSubmit}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
{t("yes")}
</button>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
{t("no")}
</button>
</div>
</div>
</ModalUi>
{/* buy APIs */}
<ModalUi
isOpen={isModal.buyapis}
title={"Buy Credits"}
handleClose={handleCloseModal}
>
{isFormLoader && (
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
<Loader />
</div>
)}
<form onSubmit={handleAddOnApiSubmit} className="p-3">
<div className="mb-3 flex justify-between">
<label
htmlFor="quantity"
className="block text-xs text-gray-700 font-semibold"
>
{t("quantity-of-credits")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<select
value={amount.quantity}
onChange={(e) => handlePricePerAPIs(e)}
name="quantity"
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-1/4 text-xs"
required
>
{quantityList.length > 0 &&
quantityList.map((x) => (
<option key={x} value={x}>
{x}
</option>
))}
</select>
</div>
<div className="mb-3 flex justify-between">
<label className="block text-xs text-gray-700 font-semibold">
{t("Price")} (1 * {amount.priceperapi})
</label>
<div className="w-1/4 flex justify-center items-center text-sm">
USD {amount.price}
</div>
</div>
<hr className="text-base-content mb-3" />
<button className="op-btn op-btn-primary w-full mt-2">
{t("Proceed")}
</button>
</form>
</ModalUi>
<ModalUi
isOpen={isModal.unauthorized}
title={"Unauthorized"}
handleClose={handleCloseModal}
>
<div className="m-8 flex justify-center text-center items-center font-medium break-all">
{t("unauthorized-modal", {
adminName: isSubscribe?.adminName,
adminEmail: isSubscribe?.adminEmail
})}
</div>
</ModalUi>
</div>
{!isSubscribe && isEnableSubscription && (
{isEnableSubscription && isSubscribe?.plan === "freeplan" && (
<div data-tut="apisubscribe">
<SubscribeCard />
</div>
+151 -136
View File
@@ -2,13 +2,20 @@ import React, { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import axios from "axios";
import { isEnableSubscription } from "../constant/const";
import { contractUsers, getAppLogo } from "../constant/Utils";
import {
contractUsers,
getAppLogo,
saveLanguageInLocal
} from "../constant/Utils";
import logo from "../assets/images/logo.png";
import { appInfo } from "../constant/appinfo";
import Parse from "parse";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
function GuestLogin() {
const { t, i18n } = useTranslation();
const { id, userMail, contactBookId, base64url } = useParams();
const navigate = useNavigate();
const [email, setEmail] = useState(userMail);
@@ -40,6 +47,7 @@ function GuestLogin() {
}
localStorage.clear();
saveLanguageInLocal(i18n);
const parseId = appInfo.appId;
const newServer = `${appInfo.baseUrl}/`;
localStorage.setItem("baseUrl", newServer);
@@ -87,7 +95,7 @@ function GuestLogin() {
setEnterOtp(true);
}
} catch (error) {
alert("something went wrong!");
alert(t("something-went-wrong-mssg"));
}
};
@@ -117,10 +125,10 @@ function GuestLogin() {
};
let user = await axios.post(url, body, { headers: headers });
if (user.data.result === "Invalid Otp") {
alert("Invalid Otp");
alert(t("invalid-otp"));
setLoading(false);
} else if (user.data.result === "user not found!") {
alert("User not found!");
alert(t("user-not-found"));
setLoading(false);
} else {
let _user = user.data.result;
@@ -157,7 +165,7 @@ function GuestLogin() {
console.log("err ", error);
}
} else {
alert("Please Enter OTP!");
alert(t("enter-otp-alert"));
}
};
const handleUserData = async (e) => {
@@ -172,7 +180,7 @@ function GuestLogin() {
await SendOtp();
} catch (err) {
setLoading(false);
alert("something went wron, please try agian later.");
alert(t("something-went-wrong-mssg"));
console.log("Err in link ext contact", err);
}
};
@@ -180,153 +188,160 @@ function GuestLogin() {
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
return (
<div className="p-[2rem]">
{isLoading ? (
<div className="flex flex-col justify-center items-center h-[100vh]">
<Loader />
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
</div>
) : (
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
{appLogo && (
<img src={appLogo} className="object-contain" alt="logo" />
)}
<div className="p-14">
<div>
{isLoading ? (
<div className="flex flex-col justify-center items-center h-[100vh]">
<Loader />
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
</div>
{contactId ? (
<>
{!EnterOTP ? (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
Verification code is sent to your email
</legend>
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
) : (
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
{appLogo && (
<img src={appLogo} className="object-contain" alt="logo" />
)}
</div>
{contactId ? (
<>
{!EnterOTP ? (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
{t("guest-email-alert")}
</legend>
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
<input
type="email"
name="email"
value={email}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
disabled
/>
</div>
<div className="mt-3">
<button
className="op-btn op-btn-primary"
onClick={(e) => handleSendOTPBtn(e)}
disabled={loading}
>
{loading ? t("loading") : t("get-verification-code")}
</button>
</div>
</div>
) : (
<form
className="w-full md:w-[50%] text-base-content"
onSubmit={VerifyOTP}
>
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
<legend className="text-[12px] text-[#878787] mt-2">
{t("get-verification-code-2")}
</legend>
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
<p className="text-sm">{t("enter-verification-code")}</p>
<input
type="number"
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="OTP"
value={OTP}
onChange={(e) => setOTP(e.target.value)}
/>
</div>
<div className="mt-2.5">
<button
className="op-btn op-btn-primary"
type="submit"
disabled={loading}
>
{loading ? t("loading") : t("verify")}
</button>
</div>
</form>
)}
</>
) : (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
<legend className="text-[12px] text-[#878787] mt-2">
{t("provide-your-details")}
</legend>
<form
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"
onSubmit={handleUserData}
>
<div className="mb-2">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
{t("name")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={contact.name}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="mb-2">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
{t("email")}
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="mobile"
value={email}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
name="email"
value={contact.email}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
required
disabled
/>
</div>
<div className="mt-3">
<button
className="op-btn op-btn-primary"
onClick={(e) => handleSendOTPBtn(e)}
disabled={loading}
<div className="mt-2.5">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
{loading ? "Loading..." : "Get verification code"}
</button>
</div>
</div>
) : (
<form
className="w-full md:w-[50%] text-base-content"
onSubmit={VerifyOTP}
>
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
<legend className="text-[12px] text-[#878787] mt-2">
You will get a verification code via Email
</legend>
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
<p className="text-sm">Enter Verification Code</p>
{t("phone")}
</label>
<input
type="number"
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
name="OTP"
value={OTP}
onChange={(e) => setOTP(e.target.value)}
type="text"
name="phone"
value={contact.phone}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
/>
</div>
<div className="mt-2.5">
<div className="mt-2 flex justify-start">
<button
className="op-btn op-btn-primary"
type="submit"
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Verify"}
{loading ? t("loading") : t("next")}
</button>
</div>
</form>
)}
</>
) : (
<div className="w-full md:w-[50%] text-base-content">
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
<legend className="text-[12px] text-[#878787] mt-2">
Provide your details
</legend>
<form
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"
onSubmit={handleUserData}
>
<div className="mb-2">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="text"
name="name"
value={contact.name}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
required
/>
</div>
<div className="mb-2">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span className="text-[red] text-[13px]"> *</span>
</label>
<input
type="email"
name="email"
value={contact.email}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
required
disabled
/>
</div>
<div className="mt-2.5">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
</label>
<input
type="text"
name="phone"
value={contact.phone}
onChange={handleInputChange}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
disabled={loading}
/>
</div>
<div className="mt-2 flex justify-start">
<button
type="submit"
className="op-btn op-btn-primary"
disabled={loading}
>
{loading ? "Loading..." : "Next"}
</button>
</div>
</form>
</div>
)}
</div>
)}
</div>
)}
</div>
)}
</div>
<SelectLanguage />
</div>
);
}
+85 -38
View File
@@ -14,9 +14,18 @@ import Alert from "../primitives/Alert";
import { appInfo } from "../constant/appinfo";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchSubscription, getAppLogo, openInNewTab } from "../constant/Utils";
import {
fetchSubscription,
getAppLogo,
openInNewTab,
saveLanguageInLocal
} from "../constant/Utils";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
function Login() {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
@@ -65,10 +74,15 @@ function Login() {
dispatch(fetchAppInfo());
};
const handleChange = (event) => {
const { name, value } = event.target;
let { name, value } = event.target;
if (name === "email") {
value = value?.toLowerCase()?.replace(/\s/g, "");
}
setState({ ...state, [name]: value });
};
const handlePaidRoute = () => {
navigate("/subscription");
};
const handleSubmit = async (event) => {
localStorage.removeItem("accesstoken");
event.preventDefault();
@@ -96,6 +110,7 @@ function Login() {
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");
@@ -109,8 +124,14 @@ function Login() {
`/${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);
localStorage.setItem("Extand_Class", extUser_str);
const extInfo = JSON.parse(JSON.stringify(extUser));
localStorage.setItem("userEmail", extInfo.Email);
@@ -139,9 +160,9 @@ function Login() {
JSON.stringify(LocalUserDetails)
);
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
setState({ ...state, loading: false });
navigate(redirectUrl);
} else if (billingDate) {
@@ -152,11 +173,11 @@ function Login() {
navigate(redirectUrl);
} else {
setState({ ...state, loading: false });
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
setState({ ...state, loading: false });
@@ -242,7 +263,7 @@ function Login() {
}
} catch (err) {
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
alert(t("something-went-wrong-mssg"));
}
};
const thirdpartyLoginfn = async (sessionToken) => {
@@ -311,9 +332,9 @@ function Login() {
localStorage.setItem("pageType", menu.pageType);
if (isEnableSubscription) {
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
@@ -323,14 +344,14 @@ function Login() {
if (isFreeplan) {
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
}
} else {
if (isFreeplan) {
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
}
} else {
@@ -443,8 +464,8 @@ function Login() {
localStorage.setItem("userDetails", JSON.stringify(userInfo));
const res = await fetchSubscription();
const billingDate = res.billingDate;
const freeplan = res.plan;
if (freeplan === "freeplan") {
const plan = res.plan;
if (plan === "freeplan") {
navigate(redirectUrl);
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
@@ -452,10 +473,10 @@ function Login() {
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`);
handlePaidRoute(plan);
}
} else {
navigate(`/subscription`);
handlePaidRoute(plan);
}
} else {
// Redirect to the appropriate URL after successful login
@@ -541,7 +562,7 @@ function Login() {
payload &&
payload.message.replace(/ /g, "_") === "Internal_server_err"
) {
alert("Internal server error !");
alert(t("server-error"));
}
} else {
setState({
@@ -554,10 +575,12 @@ function Login() {
}
};
const logOutUser = () => {
const logOutUser = async () => {
setIsModal(false);
if (Parse?.User?.current()) {
Parse.User.logOut();
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
@@ -567,6 +590,7 @@ function Login() {
let appid = localStorage.getItem("parseAppId");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
@@ -591,10 +615,11 @@ function Login() {
"_self"
);
} else {
alert("Please provide email.");
alert(t("provide-email"));
}
}
};
return (
<div>
<Title title={"Login Page"} />
@@ -626,14 +651,14 @@ function Login() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit} aria-label="Login Form">
<h1 className="text-[30px] mt-6">Welcome Back!</h1>
<h1 className="text-[30px] mt-6">{t("welcome")}</h1>
<fieldset>
<legend className="text-[12px] text-[#878787]">
Login to your account
{t("Login-to-your-account")}
</legend>
<div className="w-full px-6 py-3 my-1 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<label className="block text-xs" htmlFor="email">
Email
{t("email")}
</label>
<input
id="email"
@@ -644,12 +669,16 @@ function Login() {
value={state.email}
onChange={handleChange}
required
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
/>
<hr className="my-1 border-none" />
{!isLoginSSO && (
<>
<label className="block text-xs" htmlFor="password">
Password
{t("password")}
</label>
<div className="relative">
<input
@@ -662,6 +691,12 @@ function Login() {
value={state.password}
autoComplete="current-password"
onChange={handleChange}
onInvalid={(e) =>
e.target.setCustomValidity(
t("input-required")
)
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<span
@@ -682,7 +717,7 @@ function Login() {
to="/forgetpassword"
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
>
Forgot Password?
{t("forgot-password")}
</NavLink>
</div>
</div>
@@ -693,7 +728,7 @@ function Login() {
className="op-btn op-btn-primary"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Login"}
{state.loading ? t("loading") : t("login")}
</button>
{isEnableSubscription && (
<button
@@ -708,13 +743,13 @@ function Login() {
)
}
>
Create Account
{t("create-account")}
</button>
)}
</div>
</form>
{(appInfo.googleClietId || isEnableSubscription) && (
<div className="op-divider my-4 text-sm">OR</div>
<div className="op-divider my-4 text-sm">{t("or")}</div>
)}
<div className="flex flex-col justify-center items-center gap-y-3">
{/* {appInfo?.fbAppId && (
@@ -738,7 +773,7 @@ function Login() {
className="cursor-pointer border-[1px] border-gray-300 rounded px-[40px] py-2 font-semibold text-sm hover:border-[#d2e3fc] hover:bg-[#ecf3feb7]"
onClick={() => handleSignInWithSSO()}
>
Sign in with SSO
{t("sign-SSO")}
</div>
)}
</div>
@@ -756,13 +791,16 @@ function Login() {
)}
</div>
</div>
<SelectLanguage />
{state.alertMsg && (
<Alert type={state.alertType}>
<div className="ml-3">{state.alertMsg}</div>
</Alert>
<Alert type={state.alertType}>{state.alertMsg}</Alert>
)}
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<ModalUi
isOpen={isModal}
title={t("additional-info")}
showClose={false}
>
<form className="px-4 py-3 text-base-content">
<div className="mb-3">
<label
@@ -770,7 +808,8 @@ function Login() {
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span className="text-[red] text-[13px]">*</span>
{t("company")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
@@ -783,6 +822,10 @@ function Login() {
Company: e.target.value
})
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -792,7 +835,7 @@ function Login() {
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title
{t("job-title")}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
@@ -806,6 +849,10 @@ function Login() {
Destination: e.target.value
})
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
@@ -815,14 +862,14 @@ function Login() {
className="op-btn op-btn-primary"
onClick={(e) => handleSubmitbtn(e)}
>
Login
{t("login")}
</button>
<button
type="button"
className="op-btn op-btn-ghost"
onClick={logOutUser}
>
Cancel
{t("cancel")}
</button>
</div>
</form>
+12 -10
View File
@@ -7,7 +7,9 @@ import Parse from "parse";
import { SaveFileSize } from "../constant/saveFileSize";
import Alert from "../primitives/Alert";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const ManageSign = () => {
const { t } = useTranslation();
const [penColor, setPenColor] = useState("blue");
const [initialPen, setInitialPen] = useState("blue");
const [image, setImage] = useState();
@@ -187,7 +189,7 @@ const ManageSign = () => {
setIsLoader(false);
setIsAlert({
type: "success",
message: "Signature saved successfully."
message: t("signature-saved-alert")
});
setTimeout(() => setIsAlert({}), 2000);
return res;
@@ -211,7 +213,7 @@ const ManageSign = () => {
setIsLoader(false);
setIsAlert({
type: "success",
message: "Signature saved successfully."
message: t("signature-saved-alert")
});
setTimeout(() => setIsAlert({}), 2000);
return res;
@@ -245,12 +247,12 @@ const ManageSign = () => {
<div className="relative w-full">
<div className="ml-[5px] my-[20px] md:m-[20px]">
<div className="text-[20px] font-semibold m-[10px] md:m-0 mb-2">
My Signature
{t("my-signature")}
</div>
<div className="flex flex-col md:flex-row gap-0 md:gap-[12px]">
<div className="relative">
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
Signature
{t("signature")}
</span>
<input
type="file"
@@ -330,14 +332,14 @@ const ManageSign = () => {
className="op-link"
onClick={() => handleUploadBtn()}
>
Upload image
{t("upload-image")}
</div>
<div
type="button"
className="op-link"
onClick={() => handleClear()}
>
Clear
{t("clear")}
</div>
</div>
</div>
@@ -345,14 +347,14 @@ const ManageSign = () => {
{warning && (
<span className="customwarning signWarning text-[12px] w-[220px] md:w-[300px]">
<i className="fa-light fa-exclamation-circle text-[#fab005] text-[15px] mr-[4px]"></i>
Please upload signature/Image
{t("upload-signature/Image")}
</span>
)}
</div>
</div>
<div className="relative">
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
Initials
{t("initials")}
</span>
<div>
{isInitials ? (
@@ -425,7 +427,7 @@ const ManageSign = () => {
className="op-link text-sm md:text-base mr-1"
onClick={() => handleClearInitials()}
>
Clear
{t("clear")}
</div>
</div>
</div>
@@ -437,7 +439,7 @@ const ManageSign = () => {
className="op-btn op-btn-primary"
onClick={(e) => handleSubmit(e)}
>
save
{t("save")}
</button>
</div>
</div>
+36 -26
View File
@@ -10,6 +10,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Tour from "reactour";
import axios from "axios";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const DriveBody = React.lazy(
() => import("../components/opensigndrive/DriveBody")
@@ -22,6 +23,7 @@ const AppLoader = () => {
);
};
function Opensigndrive() {
const { t } = useTranslation();
const navigate = useNavigate();
const scrollRef = useRef(null);
const [isList, setIsList] = useState(false);
@@ -37,7 +39,7 @@ function Opensigndrive() {
const [tourStatusArr, setTourStatusArr] = useState([]);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
message: t("loading-mssg")
});
const [docId, setDocId] = useState();
const [handleError, setHandleError] = useState("");
@@ -78,7 +80,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourFirst"]',
content: () => (
<TourContentWithBtn
message={`Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.`}
message={t("tour-mssg.opensign-drive-1")}
isChecked={handleDontShow}
/>
),
@@ -89,7 +91,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`Click the add button to create a new folder or document.`}
message={t("tour-mssg.opensign-drive-2")}
isChecked={handleDontShow}
/>
),
@@ -100,7 +102,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourThird"]',
content: () => (
<TourContentWithBtn
message={`Sort your documents by Date or Name using this menu.`}
message={t("tour-mssg.opensign-drive-3")}
isChecked={handleDontShow}
/>
),
@@ -111,7 +113,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourForth"]',
content: () => (
<TourContentWithBtn
message={`Click on this menu to display the documents in list view.`}
message={t("tour-mssg.opensign-drive-4")}
isChecked={handleDontShow}
/>
),
@@ -126,7 +128,7 @@ function Opensigndrive() {
checkTourStatus();
}
if (!disbaleLoading) {
setIsLoading({ isLoad: true, message: "This might take some time" });
setIsLoading({ isLoad: true, message: t("loading-mssg") });
}
try {
const driveDetails = await getDrive(docId, skip, limit);
@@ -138,7 +140,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourFifth"]',
content: () => (
<TourContentWithBtn
message={`The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.`}
message={t("tour-mssg.opensign-drive-5")}
isChecked={handleDontShow}
/>
),
@@ -149,7 +151,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourSixth"]',
content: () => (
<TourContentWithBtn
message={`Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.`}
message={t("tour-mssg.opensign-drive-6")}
isChecked={handleDontShow}
/>
),
@@ -160,7 +162,7 @@ function Opensigndrive() {
selector: '[data-tut="reactourSeventh"]',
content: () => (
<TourContentWithBtn
message={`Right-click on any folder to see options. Choose Rename to change the folders name or click on the folder to navigate through its contents.`}
message={t("tour-mssg.opensign-drive-7")}
isChecked={handleDontShow}
/>
),
@@ -190,12 +192,12 @@ function Opensigndrive() {
setTourData(tourConfigs);
}
if (!docId) {
setFolderName([{ name: "OpenSign™ Drive", objectId: "" }]);
setFolderName([{ name: t("OpenSign-drive"), objectId: "" }]);
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage: t("something-went-wrong-mssg")
});
} finally {
setLoading(false);
@@ -319,11 +321,11 @@ function Opensigndrive() {
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong!"
alertMessage: t("something-went-wrong-mssg")
});
}
} else {
setError("Please fill out this field");
setError(t("fill-field"));
}
};
@@ -522,7 +524,7 @@ function Opensigndrive() {
<Title title={"OpenSign™ Drive"} drive={true} />
<ModalUi
isOpen={isAlert.isShow}
title={"Alert"}
title={t("alert")}
handleClose={() => {
setIsAlert({
isShow: false,
@@ -543,13 +545,13 @@ function Opensigndrive() {
type="button"
className="op-btn op-btn-neutral op-btn-sm"
>
Close
{t("close")}
</button>
</div>
</ModalUi>
<ModalUi
isOpen={isFolder}
title={"Add New Folder"}
title={t("add-new-folder")}
handleClose={oncloseFolder}
>
<div className="h-full p-[20px] pt-[10px] pb-[15px]">
@@ -563,10 +565,14 @@ function Opensigndrive() {
className="flex flex-col text-base-content"
>
<label className="py-[8px] text-[15px] font-[400] mb-0">
Name
{t("name")}
<span className="text-[red]">*</span>
</label>
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
className="op-input op-input-bordered op-input-sm"
type="text"
@@ -577,14 +583,14 @@ function Opensigndrive() {
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
<div className="flex flex-row">
<button type="submit" className="op-btn op-btn-primary">
Add
{t("add")}
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-1"
onClick={oncloseFolder}
>
Close
{t("close")}
</button>
</div>
</form>
@@ -652,21 +658,21 @@ function Opensigndrive() {
className="fa-light fa-plus mr-[5px]"
aria-hidden="true"
></i>
Create folder
{t("create-folder")}
</span>
<span
className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/sHAnZphf69")}
>
<i className="fa-light fa-pen-nib mr-[5px]"></i>
Sign Yourself
{t("form-name.Sign Yourself")}
</span>
<span
className="dropdown-item text-[10px] md:text-[13px]"
onClick={() => navigate("/form/8mZzFxbG1z")}
>
<i className="fa-light fa-file-signature mr-[5px]"></i>
Request Signatures
{t("form-name.Request Signatures")}
</span>
</div>
</div>
@@ -716,7 +722,9 @@ function Opensigndrive() {
aria-hidden="true"
></i>
)}
<span className="ml-[5px]">{value}</span>
<span className="ml-[5px]">
{t(`sort-order.${value}`)}
</span>
</span>
);
})}
@@ -741,7 +749,9 @@ function Opensigndrive() {
aria-hidden="true"
></i>
)}
<span className="ml-[5px]">{order}</span>
<span className="ml-[5px]">
{t(`sort-order.${order}`)}
</span>
</span>
);
})}
@@ -778,7 +788,7 @@ function Opensigndrive() {
{pdfData && pdfData.length === 0 ? (
<div className="flex justify-center items-center w-full h-[50vh]">
<span className="text-base-content font-bold">
No Data Found!
{t("no-data")}
</span>
</div>
) : (
@@ -800,7 +810,7 @@ function Opensigndrive() {
sortingData={sortingData}
/>
{loading && (
<div className="text-center pb-[20px]">Loading...</div>
<div className="text-center pb-[20px]">{t("loading")}</div>
)}
</React.Suspense>
</div>
File diff suppressed because it is too large Load Diff
+22 -15
View File
@@ -8,8 +8,10 @@ import { appInfo } from "../constant/appinfo";
import { showTenant } from "../redux/reducers/ShowTenant";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const PgSignUp = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
@@ -166,11 +168,11 @@ const PgSignUp = () => {
} catch (error) {
console.log("err ", error);
if (error.message === "Account already exists for this username.") {
alert("Account already exists!");
alert(t("account-already-exists"));
navigate("/", { replace: true });
} else {
setIsLoader(false);
alert("Something went wrong, please try again later!");
alert(t("something-went-wrong-mssg"));
}
}
};
@@ -257,7 +259,7 @@ const PgSignUp = () => {
localStorage.setItem("defaultmenuid", menu.menuId);
localStorage.setItem("pageType", menu.pageType);
setIsLoader(false);
alert("Registered user successfully");
alert(t("registered-user-successfully"));
const redirectUrl =
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
// Redirect to the appropriate URL after successful login
@@ -289,27 +291,31 @@ const PgSignUp = () => {
>
<div className="p-[16px]">
<h1 className="text-2xl md:text-3xl font-bold">
Choose Password
{t("choose-password")}
</h1>
<hr className="border-[1px] border-[#cecece] mb-[5px]" />
<label htmlFor="password">
<b className="text-[13px]">Password</b>
<b className="text-[13px]">{t("password")}</b>
</label>
<input
type="password"
placeholder="Enter Password"
placeholder={t("enter-password")}
name="password"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={formData.password}
onChange={handlePassowordChange}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<label htmlFor="confirmPassword">
<b className="text-[13px]">Confirm Password</b>
<b className="text-[13px]">{t("confirm-password")}</b>
</label>
<input
type="password"
placeholder="Confirm Password"
placeholder={t("confirm-password")}
name="confirmPassword"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
style={{
@@ -321,6 +327,10 @@ const PgSignUp = () => {
}}
value={formData.confirmPassword}
onChange={handleConFirmPassowordChange}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<div
@@ -339,24 +349,21 @@ const PgSignUp = () => {
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8 characters
long
{lengthValid ? "✓" : "✗"} {t("password-length")}
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain
uppercase letter, lowercase letter, digit
{caseDigitValid ? "✓" : "✗"} {t("password-case")}
</p>
<p
className={`${
specialCharValid ? "text-green-600" : "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should contain
special character
{specialCharValid ? "✓" : "✗"} {t("password-special-char")}
</p>
</div>
)}
@@ -364,7 +371,7 @@ const PgSignUp = () => {
type="submit"
className="op-btn op-btn-primary w-full mt-[8px]"
>
Submit
{t("submit")}
</button>
</div>
</form>
File diff suppressed because it is too large Load Diff
+81 -15
View File
@@ -4,8 +4,9 @@ import checkmark from "../assets/images/checkmark.png";
import plansArr from "../json/plansArr";
import Title from "../components/Title";
import Parse from "parse";
import { openInNewTab } from "../constant/Utils";
import { openInNewTab, saveLanguageInLocal } from "../constant/Utils";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const listItemStyle = {
paddingLeft: "20px", // Add padding to create space for the image
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
@@ -15,6 +16,7 @@ const listItemStyle = {
};
const PlanSubscriptions = () => {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const [yearlyVisible, setYearlyVisible] = useState(true);
const [isLoader, setIsLoader] = useState(true);
@@ -51,19 +53,45 @@ const PlanSubscriptions = () => {
? "&mobile=" + encodeURIComponent(userDetails.phone)
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
name +
email +
company +
phone;
useEffect(() => {
setIsLoader(false);
detectLanguage();
// eslint-disable-next-line
}, []);
const detectLanguage = () => {
const detectedLanguage = i18n.language || "en";
i18n.changeLanguage(detectedLanguage);
localStorage.setItem("i18nextLng", detectedLanguage);
};
const handleFreePlan = async (item) => {
if (item.url) {
const code = yearlyVisible ? item.code.yearly : item.code.monthly;
const allowedUsers =
localStorage.getItem("allowedUsers") &&
localStorage.getItem("allowedUsers") > 0
? localStorage.getItem("allowedUsers") - 1
: "";
const teamperiod = {
"team-weekly": "monthly",
"team-yearly": "yearly",
"teams-monthly": "monthly",
"teams-yearly": "yearly"
};
const period = teamperiod[code] || "";
const quantity =
allowedUsers && period
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
: "";
const details =
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
quantity +
name +
email +
company +
phone;
const url = yearlyVisible ? item.yearlyUrl + details : item.url + details;
if (user) {
localStorage.setItem("userDetails", JSON.stringify(user));
@@ -76,7 +104,7 @@ const PlanSubscriptions = () => {
const res = await Parse.Cloud.run("freesubscription", params);
if (res.status === "success" && res.result === "already subscribed!") {
setIsLoader(false);
alert("You have already subscribed to plan!");
alert(t("subscribed-alert"));
} else if (res.status === "success") {
setIsLoader(false);
navigate("/");
@@ -87,10 +115,34 @@ const PlanSubscriptions = () => {
} catch (err) {
setIsLoader(false);
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
alert(t("something-went-wrong-mssg"));
}
}
};
const handleLogout = async () => {
try {
Parse?.User?.logOut();
} catch (err) {
console.log("Err", err);
}
let appdata = localStorage.getItem("userSettings");
let applogo = localStorage.getItem("appLogo");
let defaultmenuid = localStorage.getItem("defaultmenuid");
let PageLanding = localStorage.getItem("PageLanding");
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("appLogo", applogo);
localStorage.setItem("defaultmenuid", defaultmenuid);
localStorage.setItem("PageLanding", PageLanding);
localStorage.setItem("userSettings", appdata);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
navigate("/");
};
return (
<>
<Title title={"Subscriptions"} />
@@ -101,7 +153,13 @@ const PlanSubscriptions = () => {
) : (
<div className="overflow-y-auto max-h-full">
<div className="block my-2">
<div className="flex flex-col justify-center items-center w-full">
<div className="flex flex-col justify-center items-center w-full relative">
<button
className="md:hidden op-btn op-btn-primary op-btn-sm h-[40px] w-[250px] shadow-lg"
onClick={() => handleLogout()}
>
Log out
</button>
<div
role="tablist"
className="op-tabs op-tabs-boxed bg-base-100 my-2 shadow-lg transition-all"
@@ -111,17 +169,23 @@ const PlanSubscriptions = () => {
role="tab"
className={`${!yearlyVisible ? "op-tab-active" : ""} op-tab`}
>
Monthly
{t("monthly")}
</a>
<a
onClick={() => setYearlyVisible(true)}
role="tab"
className={`${yearlyVisible ? "op-tab-active" : ""} op-tab`}
>
Yearly (upto 66% off)
{t("yearly-upto")}
</a>
</div>
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
<button
className="hidden md:block -top-12 right-1 absolute w-[150px] h-[40px] op-btn op-btn-primary op-btn-sm shadow-lg"
onClick={() => handleLogout()}
>
{t("log-out")}
</button>
{plansArr.map((item) => (
<li
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
@@ -170,7 +234,9 @@ const PlanSubscriptions = () => {
)}
</span>
<p className="font-semibold pt-2 text-sm">
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
{yearlyVisible
? t("billed-yearly")
: t("billed-monthly")}
</p>
<div className="max-w-[250px] h-[40px] text-center text-sm my-2">
<div
@@ -228,7 +294,7 @@ const PlanSubscriptions = () => {
</div>
<div className="flex flex-col justify-center items-center">
<h3 className="text-[#002862] mt-1 mb-2">
Host it yourself for free
{t("plansubscription-1")}
</h3>
<div
className="op-btn op-btn-primary w-[200px]"
@@ -236,7 +302,7 @@ const PlanSubscriptions = () => {
openInNewTab("https://github.com/OpenSignLabs/OpenSign")
}
>
Visit Github
{t("visit-github")}
</div>
</div>
</div>
+4 -4
View File
@@ -8,8 +8,10 @@ import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const Report = () => {
const { t } = useTranslation();
const { id } = useParams();
const [List, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true);
@@ -83,9 +85,7 @@ const Report = () => {
selector: "[data-tut=reactourFirst]",
content: () => (
<TourContentWithBtn
message={
"Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like HR Manager and New Employee. Each time you use the template, you can assign the New Employee role to different incoming staff members, while the HR Manager role remains constant, facilitating a seamless onboarding process for each recruit. "
}
message={t("tour-mssg.report-1")}
isChecked={handleDontShow}
/>
),
@@ -100,7 +100,7 @@ const Report = () => {
selector: `[data-tut="${data?.selector}"]`,
content: () => (
<TourContentWithBtn
message={data?.message}
message={t(`tour-mssg.${data?.action}`)}
isChecked={handleDontShow}
/>
),
+36 -21
View File
@@ -8,8 +8,10 @@ import { useDispatch } from "react-redux";
import { showTenant } from "../redux/reducers/ShowTenant";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
const SSOVerify = () => {
const { t } = useTranslation();
const location = useLocation();
const navigate = useNavigate();
const dispatch = useDispatch();
@@ -48,6 +50,9 @@ const SSOVerify = () => {
console.log("err", err.message);
}
};
const handlePaidRoute = () => {
navigate("/subscription");
};
const checkExtUser = async (ssosign) => {
const params = { email: ssosign?.email };
try {
@@ -120,16 +125,16 @@ const SSOVerify = () => {
} catch (err) {
console.log("error in usersignup", err);
localStorage.removeItem("accesstoken");
alert("Something went wrong.");
alert(t("something-went-wrong-mssg"));
setIsLoader(false);
}
} else {
localStorage.removeItem("accesstoken");
alert("Internal server error.");
alert(t("server-error"));
setIsLoader(false);
}
} else {
alert("Please fill required details correctly.");
alert(t("filed-required-correctly"));
setIsLoader(false);
}
};
@@ -194,29 +199,25 @@ const SSOVerify = () => {
localStorage.removeItem("userDetails");
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
navigate(`/subscription`, { replace: true });
handlePaidRoute(plan);
}
} else {
navigate(redirectUrl);
}
}
} else {
alert(
"Error: You don't have access, please contact the admin."
);
setMessage(
"Error: You don't have access, please contact the admin."
);
alert(t("do-not-access-contact-admin"));
setMessage(t("do-not-access-contact-admin"));
}
}
})
.catch((error) => {
console.error("Err in fetch extuser", error);
alert("user not exist.");
setMessage("Error: User not exist.");
alert(t("user-not-exist"));
setMessage(t("user-not-exist"));
});
} catch (err) {
console.log("err in getuserdetails", err);
@@ -227,10 +228,12 @@ const SSOVerify = () => {
}
};
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
const handleCloseModal = () => {
const handleCloseModal = async () => {
setIsModal(false);
if (Parse?.User?.current()) {
Parse.User.logOut();
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
};
@@ -262,7 +265,7 @@ const SSOVerify = () => {
>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Phone <span className="text-[13px] text-[red]">*</span>
{t("phone")} <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="tel"
@@ -276,6 +279,10 @@ const SSOVerify = () => {
})
}
disabled={isLoader}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<p className="text-[10px] text-[red] ml-2">
@@ -284,7 +291,7 @@ const SSOVerify = () => {
</div>
<div className="mb-3">
<label htmlFor="Company" className="block text-xs font-semibold">
Company <span className="text-[13px] text-[red]">*</span>
{t("company")} <span className="text-[13px] text-[red]">*</span>
</label>
<input
type="text"
@@ -298,12 +305,16 @@ const SSOVerify = () => {
})
}
disabled={isLoader}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="mb-3">
<label htmlFor="JobTitle" className="block text-xs font-semibold">
Job Title
{t("job-title")}
<span className="text-[13px] text-[red]">*</span>
</label>
<input
@@ -318,19 +329,23 @@ const SSOVerify = () => {
})
}
disabled={isLoader}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
</div>
<div className="mt-4">
<button type="submit" className="op-btn op-btn-primary">
Login
{t("login")}
</button>
<button
type="button"
className="op-btn op-btn-ghost ml-2"
onClick={handleCloseModal}
>
Cancel
{t("cancel")}
</button>
</div>
</form>
+72 -31
View File
@@ -11,10 +11,17 @@ import { useDispatch } from "react-redux";
import { fetchAppInfo } from "../redux/reducers/infoReducer";
import { showTenant } from "../redux/reducers/ShowTenant";
import { isEnableSubscription } from "../constant/const";
import { getAppLogo, openInNewTab } from "../constant/Utils";
import {
getAppLogo,
openInNewTab,
saveLanguageInLocal
} from "../constant/Utils";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
const Signup = () => {
const { width } = useWindowSize();
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const location = useLocation();
const dispatch = useDispatch();
@@ -38,8 +45,10 @@ const Signup = () => {
const togglePasswordVisibility = () => setShowPassword(!showPassword);
const clearStorage = async () => {
if (Parse.User.current()) {
try {
await Parse.User.logOut();
} catch (err) {
console.log("Err while logging out", err);
}
let baseUrl = localStorage.getItem("baseUrl");
let appid = localStorage.getItem("parseAppId");
@@ -49,7 +58,7 @@ const Signup = () => {
let userSettings = localStorage.getItem("userSettings");
localStorage.clear();
saveLanguageInLocal(i18n);
localStorage.setItem("baseUrl", baseUrl);
localStorage.setItem("parseAppId", appid);
localStorage.setItem("appLogo", applogo);
@@ -69,7 +78,7 @@ const Signup = () => {
}
} catch (err) {
console.log("err in free subscribe", err.message);
alert("Somenthing went wrong, please try again later!");
alert(t("something-went-wrong-mssg"));
}
};
const handleSubmit = (event) => {
@@ -136,7 +145,7 @@ const Signup = () => {
const res = await Parse.Cloud.run("getUserDetails", params);
// console.log("Res ", res);
if (res) {
alert("User already exists with this username!");
alert(t("user-already-exist-name"));
setState({ loading: false });
} else {
// console.log("state.email ", email);
@@ -144,9 +153,7 @@ const Signup = () => {
await Parse.User.requestPasswordReset(email).then(
async function (res1) {
if (res1.data === undefined) {
alert(
"Verification mail has been sent to your E-mail!"
);
alert(t("verification-code-sent"));
}
}
);
@@ -230,14 +237,14 @@ const Signup = () => {
navigate(`/subscription`, { replace: true });
}
} else {
alert("Registered user successfully");
alert(t("registered-user-successfully"));
navigate(`/${menu.pageType}/${menu.pageId}`);
}
} else {
setState({
loading: false,
alertType: "danger",
alertMsg: "Role not found."
alertMsg: t("role-not-found")
});
setTimeout(() => {
setState({ loading: false, alertMsg: "" });
@@ -247,7 +254,7 @@ const Signup = () => {
setState({
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
alertMsg: t("do-not-access-contact-admin")
});
setTimeout(() => {
setState({ loading: false, alertMsg: "" });
@@ -260,7 +267,7 @@ const Signup = () => {
setState({
loading: false,
alertType: "danger",
alertMsg: "You don't have access, please contact the admin."
alertMsg: t("do-not-access-contact-admin")
});
setTimeout(() => {
setState({ loading: false, alertMsg: "" });
@@ -319,7 +326,7 @@ const Signup = () => {
<Loader />
</div>
)}
<Title title={"Signup page"} />
<Title title="Signup page" />
{appInfo && appInfo.applogo ? (
<div className="md:p-10 lg:p-16">
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
@@ -331,22 +338,28 @@ const Signup = () => {
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit}>
<h2 className="text-[30px] mt-6">Create Account !</h2>
<h2 className="text-[30px] mt-6">{t("create-account")}!</h2>
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
<div className="px-6 py-4 text-xs">
<label className="block ">
Name <span className="text-[red] text-[13px]">*</span>
{t("name")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={name}
onChange={(e) => setName(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Email <span className="text-[red] text-[13px]">*</span>
{t("email")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
@@ -354,24 +367,35 @@ const Signup = () => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={email}
onChange={(e) =>
setEmail(e.target.value?.toLowerCase())
setEmail(
e.target.value?.toLowerCase()?.replace(/\s/g, "")
)
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Phone <span className="text-[red] text-[13px]">*</span>
{t("phone")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
type="tel"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={phone}
onChange={(e) => setPhone(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Company{" "}
{t("company")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
@@ -379,11 +403,15 @@ const Signup = () => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={company}
onChange={(e) => setCompany(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Job Title{" "}
{t("job-title")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
@@ -391,11 +419,15 @@ const Signup = () => {
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
value={jobTitle}
onChange={(e) => setJobTitle(e.target.value)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Password{" "}
{t("password")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<div className="relative">
@@ -405,6 +437,10 @@ const Signup = () => {
name="password"
value={password}
onChange={(e) => handlePasswordChange(e)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<span
@@ -425,16 +461,16 @@ const Signup = () => {
lengthValid ? "text-green-600" : "text-red-600"
}`}
>
{lengthValid ? "✓" : "✗"} Password should be 8
characters long
{lengthValid ? "✓" : "✗"}
{t("password-length")}
</p>
<p
className={`${
caseDigitValid ? "text-green-600" : "text-red-600"
}`}
>
{caseDigitValid ? "✓" : "✗"} Password should contain
uppercase letter, lowercase letter, digit
{caseDigitValid ? "✓" : "✗"}
{t("password-case")}
</p>
<p
className={`${
@@ -443,8 +479,8 @@ const Signup = () => {
: "text-red-600"
}`}
>
{specialCharValid ? "✓" : "✗"} Password should
contain special character
{specialCharValid ? "✓" : "✗"}{" "}
{t("password-special-char")}
</p>
</div>
)}
@@ -455,13 +491,17 @@ const Signup = () => {
id="termsandcondition"
checked={isAuthorize}
onChange={(e) => setIsAuthorize(e.target.checked)}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<label
className="text-xs cursor-pointer ml-1 mb-0"
htmlFor="termsandcondition"
>
I agree to the
{t("agreee")}
</label>
<span
className="underline cursor-pointer ml-1"
@@ -471,7 +511,7 @@ const Signup = () => {
)
}
>
Terms of Service
{t("term")}
</span>
<span>.</span>
</div>
@@ -483,7 +523,7 @@ const Signup = () => {
className="op-btn op-btn-primary"
disabled={state.loading}
>
{state.loading ? "Loading..." : "Register"}
{state.loading ? t("loading") : t("register")}
</button>
<button
type="button"
@@ -493,7 +533,7 @@ const Signup = () => {
navigate(location.search ? "/" + location.search : "/")
}
>
Login
{t("login")}
</button>
</div>
</form>
@@ -507,6 +547,7 @@ const Signup = () => {
)}
</div>
</div>
<SelectLanguage />
<Alert type={state.alertType}>{state.alertMsg}</Alert>
</div>
) : (
+298 -281
View File
@@ -33,7 +33,11 @@ import {
fetchImageBase64,
changeImageWH,
handleSendOTP,
getContainerScale
getContainerScale,
getDefaultSignature,
onClickZoomIn,
onClickZoomOut,
rotatePdfPage
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -51,8 +55,11 @@ import TextFontSetting from "../components/pdf/TextFontSetting";
import VerifyEmail from "../components/pdf/VerifyEmail";
import PdfZoom from "../components/pdf/PdfZoom";
import Loader from "../primitives/Loader";
import { useTranslation } from "react-i18next";
import RotateAlert from "../components/RotateAlert";
//For signYourself inProgress section signer can add sign and complete doc sign.
function SignYourSelf() {
const { t } = useTranslation();
const [pdfDetails, setPdfDetails] = useState([]);
const [isSignPad, setIsSignPad] = useState(false);
const [allPages, setAllPages] = useState(null);
@@ -85,9 +92,9 @@ function SignYourSelf() {
const [validateAlert, setValidateAlert] = useState(false);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
message: t("loading-mssg")
});
const [handleError, setHandleError] = useState();
const [handleError, setHandleError] = useState("");
const [isDragging, setIsDragging] = useState(false);
const [signTour, setSignTour] = useState(true);
const { docId } = useParams();
@@ -120,52 +127,38 @@ function SignYourSelf() {
const [zoomPercent, setZoomPercent] = useState(0);
const isHeader = useSelector((state) => state.showHeader);
const [scale, setScale] = useState(1);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
const divRef = useRef(null);
const nodeRef = useRef(null);
const [, drop] = useDrop({
accept: "BOX",
drop: (item, monitor) => addPositionOfSignature(item, monitor),
collect: (monitor) => ({
isOver: !!monitor.isOver()
})
collect: (monitor) => ({ isOver: !!monitor.isOver() })
});
const pdfRef = useRef();
const [{ isDragSign }, dragSignature] = useDrag({
type: "BOX",
item: {
id: 1,
text: "signature"
},
collect: (monitor) => ({
isDragSign: !!monitor.isDragging()
})
item: { id: 1, text: "signature" },
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
});
const [{ isDragStamp }, dragStamp] = useDrag({
type: "BOX",
item: {
id: 2,
text: "stamp"
},
collect: (monitor) => ({
isDragStamp: !!monitor.isDragging()
})
item: { id: 2, text: "stamp" },
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
});
const index = xyPostion.findIndex((object) => {
const index = xyPostion?.findIndex((object) => {
return object.pageNumber === pageNumber;
});
// rowlevel={JSON.parse(localStorage.getItem("rowlevel"))}
const rowLevel =
localStorage.getItem("rowlevel") &&
JSON.parse(localStorage.getItem("rowlevel"));
const signObjId =
rowLevel && rowLevel?.id
? rowLevel.id
: rowLevel?.objectId && rowLevel.objectId;
const documentId = docId ? docId : signObjId && signObjId;
const senderUser =
localStorage.getItem(
@@ -199,137 +192,91 @@ function SignYourSelf() {
// Use setTimeout to wait for the transition to complete
const timer = setTimeout(updateSize, 100); // match the transition duration
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
//function for get document details for perticular signer with signer'object id
const getDocumentDetails = async (showComplete) => {
let isCompleted;
//getting document details
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
setPdfDetails(documentData);
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const url = documentData[0] && documentData[0]?.URL;
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError("Error: Something went wrong!");
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError("Error: Something went wrong!");
}
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
if (isCompleted) {
setIsCelebration(true);
setTimeout(() => {
setIsCelebration(false);
}, 5000);
setIsCompleted(true);
setPdfUrl(documentData[0].SignedUrl);
const alreadySign = {
status: true,
mssg: "Congratulations! 🎉 This document has been successfully signed by you!"
};
if (showComplete) {
setShowAlreadySignDoc(alreadySign);
} else {
setIsUiLoading(false);
setIsSignPad(false);
setIsEmail(true);
setXyPostion([]);
setSignBtnPosition([]);
}
}
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else {
setHandleError("No Data Found!");
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
try {
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
} catch (err) {
console.log("err in fetch sub", err);
}
await axios
.get(
`${localStorage.getItem(
"baseUrl"
)}classes/contracts_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${
jsonSender.objectId
}"}}`,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
try {
let isCompleted;
//getting document details
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
setPdfDetails(documentData);
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const placeholders =
documentData[0]?.Placeholders?.length > 0
? documentData[0]?.Placeholders
: [];
setXyPostion(placeholders);
const url = documentData[0] && documentData[0]?.URL;
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
isCompleted = documentData?.[0]?.IsCompleted;
if (isCompleted) {
setIsCelebration(true);
setTimeout(() => setIsCelebration(false), 5000);
setIsCompleted(true);
setPdfUrl(documentData[0].SignedUrl);
if (showComplete) {
setShowAlreadySignDoc({
status: true,
mssg: t("document-signed-alert")
});
} else {
setIsUiLoading(false);
setIsSignPad(false);
setIsEmail(true);
setXyPostion([]);
setSignBtnPosition([]);
}
}
)
.then((Listdata) => {
const json = Listdata.data;
const res = json.results;
if (res[0] && res.length > 0) {
setDefaultSignImg(res[0].ImageURL);
setMyInitial(res[0]?.Initials);
}
})
.catch((err) => {
console.log("Err ", err);
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
});
const contractUsersRes = await contractUsers();
if (contractUsersRes === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
setContractName("_Users");
setSignerUserId(contractUsersRes[0].objectId);
const tourstatuss =
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
(data) => data.signyourself
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].signyourself);
}
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
setHandleError(t("something-went-wrong-mssg"));
setIsLoading({ isLoad: false });
} else {
setCheckTourStatus(true);
setHandleError(t("no-data-avaliable"));
setIsLoading({ isLoad: false });
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (contractUsersRes.length === 0) {
const contractContactBook = await contactBook(jsonSender.objectId);
if (contractContactBook && contractContactBook.length > 0) {
setContractName("_Contactbook");
setSignerUserId(contractContactBook[0].objectId);
//function to get default signatur eof current user from `contracts_Signature` class
const defaultSignRes = await getDefaultSignature(jsonSender.objectId);
if (defaultSignRes?.status === "success") {
setDefaultSignImg(defaultSignRes?.res?.defaultSignature);
setMyInitial(defaultSignRes?.res?.defaultInitial);
} else if (defaultSignRes?.status === "error") {
setHandleError("Error: Something went wrong!");
setIsLoading({ isLoad: false });
}
const contractUsersRes = await contractUsers();
if (contractUsersRes === "Error: Something went wrong!") {
setHandleError(t("something-went-wrong-mssg"));
setIsLoading({ isLoad: false });
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
setContractName("_Users");
setSignerUserId(contractUsersRes[0].objectId);
const tourstatuss =
contractContactBook[0].TourStatus &&
contractContactBook[0].TourStatus;
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
@@ -341,13 +288,44 @@ function SignYourSelf() {
} else {
setCheckTourStatus(true);
}
} else {
setHandleError("No Data Found!");
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (contractUsersRes.length === 0) {
const contractContactBook = await contactBook(jsonSender.objectId);
if (contractContactBook && contractContactBook.length > 0) {
setContractName("_Contactbook");
setSignerUserId(contractContactBook[0].objectId);
const tourstatuss =
contractContactBook[0].TourStatus &&
contractContactBook[0].TourStatus;
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
(data) => data.signyourself
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setCheckTourStatus(checkTourRecipients[0].signyourself);
}
} else {
setCheckTourStatus(true);
}
} else {
setHandleError(t("no-data-avaliable"));
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
}
const loadObj = {
} catch (err) {
console.log("Error: error in getDocumentDetails", err);
setHandleError("Error: Something went wrong!");
setIsLoading({
isLoad: false
};
setIsLoading(loadObj);
});
}
};
const getWidgetValue = (type) => {
@@ -372,51 +350,32 @@ function SignYourSelf() {
const addWidgetOptions = (type) => {
switch (type) {
case "signature":
return {
name: "signature"
};
return { name: "signature" };
case "stamp":
return {
name: "stamp"
};
return { name: "stamp" };
case "checkbox":
return {
name: "checkbox"
};
return { name: "checkbox" };
case textWidget:
return {
name: "text"
};
return { name: "text" };
case "initials":
return {
name: "initials"
};
return { name: "initials" };
case "name":
return {
name: "name",
defaultValue: getWidgetValue(type),
validation: {
type: "text",
pattern: ""
}
validation: { type: "text", pattern: "" }
};
case "company":
return {
name: "company",
defaultValue: getWidgetValue(type),
validation: {
type: "text",
pattern: ""
}
validation: { type: "text", pattern: "" }
};
case "job title":
return {
name: "job title",
defaultValue: getWidgetValue(type),
validation: {
type: "text",
pattern: ""
}
validation: { type: "text", pattern: "" }
};
case "date":
return {
@@ -425,17 +384,12 @@ function SignYourSelf() {
validation: { format: "MM/dd/yyyy", type: "date-format" }
};
case "image":
return {
name: "image"
};
return { name: "image" };
case "email":
return {
name: "email",
defaultValue: getWidgetValue(type),
validation: {
type: "email",
pattern: ""
}
validation: { type: "email", pattern: "" }
};
default:
return {};
@@ -447,7 +401,7 @@ function SignYourSelf() {
const key = randomId();
let dropData = [];
let dropObj = {};
let filterDropPos = xyPostion.filter(
let filterDropPos = xyPostion?.filter(
(data) => data.pageNumber === pageNumber
);
const dragTypeValue = item?.text ? item.text : monitor.type;
@@ -462,29 +416,27 @@ function SignYourSelf() {
);
//adding and updating drop position in array when user drop signature button in div
if (item === "onclick") {
// `getBoundingClientRect()` is used to get accurate measurement height of the div
const divHeight = divRef.current.getBoundingClientRect().height;
const getWidth = widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
: dragTypeValue === "initials"
? defaultWidthHeight(dragTypeValue).width
: "";
: defaultWidthHeight(dragTypeValue).width;
const getHeight = widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
: dragTypeValue === "initials"
? defaultWidthHeight(dragTypeValue).height
: "";
: defaultWidthHeight(dragTypeValue).height;
dropObj = {
xPosition: containerWH.width / 2,
yPosition: containerWH.height / 2,
xPosition: getWidth / 2 + containerWH.width / 2,
yPosition: getHeight + divHeight / 2,
isStamp:
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
key: key,
type: dragTypeValue,
scale: containerScale,
Width: getWidth / (containerScale * scale),
Height: getHeight / (containerScale * scale),
Width: getWidth,
Height: getHeight,
options: addWidgetOptions(dragTypeValue)
};
dropData.push(dropObj);
} else {
//This method returns the offset of the current pointer (mouse) position relative to the client viewport.
@@ -517,25 +469,18 @@ function SignYourSelf() {
options: addWidgetOptions(dragTypeValue),
scale: containerScale
};
dropData.push(dropObj);
}
if (filterDropPos.length > 0) {
const index = xyPostion.findIndex((object) => {
if (filterDropPos?.length > 0) {
const index = xyPostion?.findIndex((object) => {
return object.pageNumber === pageNumber;
});
const updateData = filterDropPos[0].pos;
const updateData = filterDropPos?.[0].pos;
const newSignPos = updateData.concat(dropData);
let xyPos = {
pageNumber: pageNumber,
pos: newSignPos
};
xyPostion.splice(index, 1, xyPos);
let xyPos = { pageNumber: pageNumber, pos: newSignPos };
xyPostion?.splice(index, 1, xyPos);
} else {
const xyPos = {
pageNumber: pageNumber,
pos: dropData
};
const xyPos = { pageNumber: pageNumber, pos: dropData };
setXyPostion((prev) => [...prev, xyPos]);
}
@@ -547,7 +492,6 @@ function SignYourSelf() {
) {
setIsSignPad(true);
}
if (dragTypeValue === "stamp" || dragTypeValue === "image") {
setIsStamp(true);
} else if (dragTypeValue === "initials") {
@@ -578,7 +522,7 @@ function SignYourSelf() {
setOtpLoader(true);
await handleSendOTP(Parse.User.current().getEmail());
setOtpLoader(false);
alert("OTP sent on you email");
alert(t("otp-sent-alert"));
};
//`handleVerifyEmail` function is used to verify email with otp
const handleVerifyEmail = async (e) => {
@@ -591,11 +535,12 @@ function SignYourSelf() {
});
if (resEmail?.message === "Email is verified.") {
setIsEmailVerified(true);
alert(t("Email-verified-alert-1"));
} else if (resEmail?.message === "Email is already verified.") {
setIsEmailVerified(true);
alert(t("Email-verified-alert-2"));
}
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
// handleRecipientSign();
} catch (error) {
@@ -609,6 +554,29 @@ function SignYourSelf() {
setIsVerifyModal(true);
await handleSendOTP(Parse.User.current().getEmail());
};
useEffect(() => {
const timer = setTimeout(() => {
if (xyPostion?.length > 0) {
autosavedetails();
}
}, 2000);
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [xyPostion]);
// `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 () => {
try {
const docCls = new Parse.Object("contracts_Document");
docCls.id = documentId;
docCls.set("Placeholders", xyPostion);
docCls.set("IsSignyourself", true);
await docCls.save();
} catch (e) {
console.log("error", e);
alert(t("something-went-wrong-mssg"));
}
};
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
async function embedWidgetsData() {
//check current user email is verified or not
@@ -628,7 +596,7 @@ function SignYourSelf() {
setIsEmailVerified(isEmailVerified);
}
} catch (e) {
setHandleError("Error: Something went wrong!");
setHandleError(t("something-went-wrong-mssg"));
}
}
if (isEmailVerified) {
@@ -666,17 +634,15 @@ function SignYourSelf() {
}
if (xyPostion.length === 0 || !isSignatureExist) {
setIsAlert({
header: "Fields required",
header: t("fields-required"),
isShow: true,
alertMessage:
"Please ensure there's at least one signature widget added"
alertMessage: t("signature-widget-alert-1")
});
return;
} else if (showAlert) {
setIsAlert({
isShow: true,
alertMessage:
"Please ensure all field is accurately filled and meets all requirements."
alertMessage: t("signature-widget-alert-2")
});
return;
} else {
@@ -710,9 +676,9 @@ function SignYourSelf() {
setIsUiLoading(false);
if (err && err.message.includes("is encrypted.")) {
setIsAlert({
header: "Error",
header: t("error"),
isShow: true,
alertMessage: `Currently encrypted pdf files are not supported.`
alertMessage: t("encrypted-pdf-alert")
});
} else {
console.log("err in signing", err.message);
@@ -721,15 +687,15 @@ function SignYourSelf() {
"PKCS#12 MAC could not be verified. Invalid password?"
) {
setIsAlert({
header: "Error",
header: t("error"),
isShow: true,
alertMessage: `PFX file password is invalid.`
alertMessage: t("encrypted-pdf-alert-1")
});
} else {
setIsAlert({
header: "Error",
header: t("error"),
isShow: true,
alertMessage: `Something went wrong.`
alertMessage: t("something-went-wrong-mssg")
});
}
}
@@ -739,9 +705,9 @@ function SignYourSelf() {
console.log("err in embedselfsign ", err);
setIsUiLoading(false);
setIsAlert({
header: "Error",
header: t("error"),
isShow: true,
alertMessage: "something went wrong, please try again later."
alertMessage: t("something-went-wrong-mssg")
});
}
}
@@ -749,15 +715,15 @@ function SignYourSelf() {
//function for get digital signature
const signPdfFun = async (base64Url, documentId) => {
let isCustomCompletionMail = false;
const getIsSubscribe = await checkIsSubscribed();
const tenantDetails = await getTenantDetails(jsonSender.objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert("User does not exist");
alert(t("user-not-exist"));
} else {
if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject &&
getIsSubscribe
isSubscribe?.isValid
) {
isCustomCompletionMail = true;
}
@@ -783,13 +749,13 @@ function SignYourSelf() {
base64Sign = await fetchImageBase64(base64Sign);
} catch (e) {
console.log("error", e);
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();
const params = {
pdfFile: base64Url,
docId: documentId,
@@ -802,6 +768,7 @@ function SignYourSelf() {
const signedpdf = JSON.parse(JSON.stringify(resSignPdf));
setPdfUrl(signedpdf);
getDocumentDetails(false);
setPdfRotatese64("");
}
};
@@ -822,10 +789,10 @@ function SignYourSelf() {
containerWH
);
if (dragKey >= 0) {
const filterDropPos = xyPostion.filter(
const filterDropPos = xyPostion?.filter(
(data) => data.pageNumber === pageNumber
);
if (filterDropPos.length > 0) {
if (filterDropPos?.length > 0) {
const getXYdata = xyPostion[index].pos;
const getPosData = getXYdata;
const addSign = getPosData.map((url) => {
@@ -896,15 +863,11 @@ function SignYourSelf() {
setIsSignPad(false);
setIsImageSelect(false);
setImage();
if (isDefaultSign) {
const img = new Image();
img.src = defaultSignImg;
if (img.complete) {
imgWH = {
width: img.width,
height: img.height
};
imgWH = { width: img.width, height: img.height };
}
}
const getUpdatePosition = onSaveSign(
@@ -929,19 +892,11 @@ function SignYourSelf() {
const touch = e.touches[0]; // Get the first touch point
mouseX = touch.clientX - divRect.left;
mouseY = touch.clientY - divRect.top;
setSignBtnPosition([
{
xPos: mouseX,
yPos: mouseY
}
]);
setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]);
} else {
mouseX = e.clientX - divRect.left;
mouseY = e.clientY - divRect.top;
const xyPosition = {
xPos: mouseX,
yPos: mouseY
};
const xyPosition = { xPos: mouseX, yPos: mouseY };
setXYSignature(xyPosition);
}
};
@@ -959,9 +914,7 @@ function SignYourSelf() {
const handleDeleteSign = (key) => {
setCurrWidgetsDetails({});
const updateResizeData = [];
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
//delete and update block position
if (filterData.length > 0) {
updateResizeData.push(filterData);
@@ -994,7 +947,7 @@ function SignYourSelf() {
selector: '[data-tut="addWidgets"]',
content: () => (
<TourContentWithBtn
message={`Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.`}
message={t("tour-mssg.signyour-self-2")}
isChecked={handleDontShow}
/>
),
@@ -1005,7 +958,7 @@ function SignYourSelf() {
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`Drag and drop anywhere in this area. You can resize and move it later.`}
message={t("tour-mssg.signyour-self-2")}
isChecked={handleDontShow}
/>
),
@@ -1053,6 +1006,7 @@ function SignYourSelf() {
})
.catch((err) => {
console.log("axois err ", err);
alert(t("something-went-wrong-mssg"));
});
}
};
@@ -1100,7 +1054,11 @@ function SignYourSelf() {
name: dropdownName,
values: dropdownOptions,
isReadOnly: isReadOnly,
isHideLabel: isHideLabel || false
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor || currWidgetsDetails?.options?.fontColor || "black"
}
};
}
@@ -1118,6 +1076,8 @@ function SignYourSelf() {
handleCloseModal();
}
}
setFontSize();
setFontColor();
};
const handleCloseModal = () => {
setCurrWidgetsDetails({});
@@ -1161,6 +1121,56 @@ function SignYourSelf() {
handleTextSettingModal(false);
}
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
//`handleRotationFun` function is used to roatate pdf particular page
const handleRotationFun = async (rotateDegree) => {
const isPlaceholder = handleRotateWarning();
if (isPlaceholder) {
setIsRotate({ status: true, degree: rotateDegree });
} else {
const urlDetails = await rotatePdfPage(
pdfUrl || pdfDetails[0].URL,
rotateDegree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
}
};
//function to show warning when user rotate page and there are some already widgets on that page
const handleRotateWarning = () => {
const placeholderData = xyPostion?.filter(
(data) => data.pageNumber === pageNumber
);
if (placeholderData && placeholderData.length > 0) {
return true;
} else {
return false;
}
};
//function to use remove widgets from current page when user want to rotate page
const handleRemoveWidgets = async () => {
const updatePlaceholder = xyPostion.filter(
(data) => data.pageNumber !== pageNumber
);
const urlDetails = await rotatePdfPage(
pdfUrl || pdfDetails[0].URL,
isRotate.degree,
pageNumber - 1,
pdfRotateBase64
);
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
setPdfRotatese64(urlDetails.base64);
setXyPostion(updatePlaceholder);
setIsRotate({ status: false, degree: 0 });
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Self Sign"} />
@@ -1174,7 +1184,7 @@ function SignYourSelf() {
<div className="absolute h-[100vh] w-full z-[999] flex flex-col justify-center items-center bg-[#e6f2f2] bg-opacity-80">
<Loader />
<span style={{ fontSize: "13px", fontWeight: "bold" }}>
This might take some time
{t("loader")}
</span>
</div>
)}
@@ -1183,7 +1193,6 @@ function SignYourSelf() {
<Confetti width={window.innerWidth} height={window.innerHeight} />
</div>
)}
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
{!isEmailVerified && (
<VerifyEmail
@@ -1220,19 +1229,18 @@ function SignYourSelf() {
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
containerWH={containerWH}
pdfRotateBase64={pdfRotateBase64}
/>
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
handleRotationFun={handleRotationFun}
/>
<div className="w-full md:w-[95%]">
<ModalUi
isOpen={isAlert.isShow}
title={isAlert?.header || "Alert"}
title={isAlert?.header || t("alert")}
handleClose={() =>
setIsAlert({ isShow: false, alertMessage: "" })
}
@@ -1245,10 +1253,8 @@ function SignYourSelf() {
{/* this modal is used show this document is already sign */}
<ModalUi
isOpen={showAlreadySignDoc.status}
title={"Document signed"}
handleClose={() => {
setShowAlreadySignDoc({ status: false });
}}
title={t("document-signed")}
handleClose={() => setShowAlreadySignDoc({ status: false })}
>
<div className="p-[20px] h-full">
<p>{showAlreadySignDoc.mssg}</p>
@@ -1258,13 +1264,13 @@ function SignYourSelf() {
className="op-btn op-btn-ghost shadow-md"
onClick={() => setShowAlreadySignDoc({ status: false })}
>
Close
{t("close")}
</button>
</div>
</ModalUi>
<DropdownWidgetOption
type="checkbox"
title="Checkbox"
title={t("checkbox")}
showDropdown={isCheckbox}
setShowDropdown={setIsCheckbox}
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
@@ -1272,6 +1278,10 @@ function SignYourSelf() {
setCurrWidgetsDetails={setCurrWidgetsDetails}
isSignYourself={true}
handleClose={handleCloseModal}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
<PlaceholderCopy
isPageCopy={isPageCopy}
@@ -1281,6 +1291,7 @@ function SignYourSelf() {
allPages={allPages}
pageNumber={pageNumber}
signKey={signKey}
widgetType={widgetType}
/>
{/* this is modal of signature pad */}
<SignPad
@@ -1310,7 +1321,7 @@ function SignYourSelf() {
isEmail={isEmail}
pdfUrl={pdfUrl}
setIsEmail={setIsEmail}
pdfName={pdfDetails[0] && pdfDetails[0].Name}
pdfDetails={pdfDetails}
setSuccessEmail={setSuccessEmail}
sender={jsonSender}
setIsAlert={setIsAlert}
@@ -1331,12 +1342,12 @@ function SignYourSelf() {
isSignYourself={true}
setIsEmail={setIsEmail}
isCompleted={isCompleted}
handleRotationFun={handleRotationFun}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
widgetsDetails={xyPostion}
/>
<div
ref={divRef}
data-tut="reactourSecond"
className="h-full md:h-[95%]"
>
<div ref={divRef} data-tut="reactourSecond" className="h-full">
{containerWH?.width && containerWH?.height && (
<RenderPdf
pageNumber={pageNumber}
@@ -1375,6 +1386,11 @@ function SignYourSelf() {
handleTextSettingModal={handleTextSettingModal}
setScale={setScale}
scale={scale}
pdfRotateBase64={pdfRotateBase64}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
)}
</div>
@@ -1413,15 +1429,11 @@ function SignYourSelf() {
)}
<ModalUi
isOpen={validateAlert}
title={"Validation alert"}
handleClose={() => {
setValidateAlert(false);
}}
title={t("validation-alert")}
handleClose={() => setValidateAlert(false)}
>
<div className="p-[20px] h-full">
<p>
The input does not meet the criteria set by the regular expression.
</p>
<p>{t("validate-alert-mssg")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
@@ -1429,10 +1441,15 @@ function SignYourSelf() {
type="button"
className="op-btn op-btn-ghost shadow-md"
>
Close
{t("close")}
</button>
</div>
</ModalUi>
<RotateAlert
isRotate={isRotate.status}
setIsRotate={setIsRotate}
handleRemoveWidgets={handleRemoveWidgets}
/>
<TextFontSetting
isTextSetting={isTextSetting}
setIsTextSetting={setIsTextSetting}
+63 -38
View File
@@ -8,9 +8,11 @@ import ModalUi from "../primitives/ModalUi";
import pad from "../assets/images/pad.svg";
import AddTeam from "../components/AddTeam";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribedTeam } from "../constant/Utils";
import { checkIsSubscribed } from "../constant/Utils";
import SubscribeCard from "../primitives/SubscribeCard";
import Title from "../components/Title";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const heading = ["Sr.No", "Name", "Parent Team", "Active"];
const actions = [
@@ -25,6 +27,7 @@ const actions = [
];
const TeamList = () => {
const { t } = useTranslation();
const recordperPage = 10;
const [teamList, setTeamList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
@@ -36,7 +39,7 @@ const TeamList = () => {
const [isActiveModal, setIsActiveModal] = useState({});
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
const [isEditModal, setIsEditModal] = useState({});
const [isAdmin, setIsAdmin] = useState(false);
const startIndex = (currentPage - 1) * recordperPage; // user per page
@@ -95,13 +98,14 @@ const TeamList = () => {
const pageNumbers = getPaginationRange();
useEffect(() => {
fetchTeamList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function fetchTeamList() {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
@@ -120,7 +124,7 @@ const TeamList = () => {
}
} catch (err) {
console.log("Err in fetch teamlist", err);
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
} finally {
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
@@ -131,7 +135,7 @@ const TeamList = () => {
const handleFormModal = () => {
setIsModal(!isModal);
};
// Get current list
// to slice out 10 objects from array for current page
const indexOfLastDoc = currentPage * recordperPage;
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
const currentList = teamList?.slice(indexOfFirstDoc, indexOfLastDoc);
@@ -142,8 +146,19 @@ const TeamList = () => {
};
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
const paginateFront = () => {
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
if (currentPage < lastValue) {
setCurrentPage(currentPage + 1);
}
};
const paginateBack = () => {
if (startIndex > 0) {
setCurrentPage(currentPage - 1);
}
};
const handleActionBtn = (act, item) => {
if (act.action === "edit") {
setIsEditModal({ [item.objectId]: true });
@@ -169,10 +184,10 @@ const TeamList = () => {
// console.log("teamRes ", teamRes);
setIsAlert({
type: !IsActive === false ? "danger" : "success",
msg: !IsActive === false ? "Team disabled." : "Team enabled."
msg: !IsActive === false ? t("team-disabled") : t("team-enabled")
});
} catch (err) {
setIsAlert({ type: "danger", msg: "something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
console.log("err in disable team", err);
} finally {
setIsActLoader({});
@@ -200,10 +215,10 @@ const TeamList = () => {
const data = { Name: team.Name, TeamId: team.objectId };
await Parse.Cloud.run("updateteam", data);
// console.log("updateTeamRes ", updateTeamRes);
setIsAlert({ type: "success", msg: "Team Update successfully." });
setIsAlert({ type: "success", msg: t("Team-update-successfully") });
} catch (Err) {
console.log("Err in update team name"), Err;
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
} finally {
setIsActLoader({});
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
@@ -222,20 +237,16 @@ const TeamList = () => {
<Loader />
</div>
)}
{isSubscribe && isEnableSubscription && !isLoader && (
{validplan[isSubscribe.plan] && isEnableSubscription && !isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
Teams{" "}
{t("report-name.Teams")}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"Teams"} />
<Tooltip message={t("report-heading.Team")} />
</span>
</div>
<div
@@ -251,7 +262,9 @@ const TeamList = () => {
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<React.Fragment key={index}>
<th className="px-4 py-2">{item}</th>
<th className="px-4 py-2">
{t(`report-heading.${item}`)}
</th>
</React.Fragment>
))}
</tr>
@@ -286,16 +299,16 @@ const TeamList = () => {
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"Team status"}
title={t("Team status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to{" "}
{t("are-you-sure")}{" "}
{item?.IsActive
? "disable"
: "enable"}{" "}
this team?
? t("disable")
: t("enable")}{" "}
{t("this-team")}
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
@@ -305,13 +318,13 @@ const TeamList = () => {
}
className="op-btn op-btn-primary"
>
Yes
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
{t("no")}
</button>
</div>
</div>
@@ -341,7 +354,7 @@ const TeamList = () => {
{isEditModal[item.objectId] && (
<ModalUi
isOpen
title={"Edit Team"}
title={t("edit-team")}
handleClose={handleClose}
>
<form
@@ -351,7 +364,7 @@ const TeamList = () => {
}
>
<label className="text-xs font-semibold text-base-content ml-1">
Name of Team{" "}
{t("name-of-team")}{" "}
<span className="text-[red] text-[13px]">
*
</span>
@@ -362,13 +375,21 @@ const TeamList = () => {
onChange={(e) =>
handleEditChange(e, item)
}
onInvalid={(e) =>
e.target.setCustomValidity(
t("input-required")
)
}
onInput={(e) =>
e.target.setCustomValidity("")
}
required
/>
<button
type="submit"
className="op-btn op-btn-primary mt-3"
>
Save
{t("save")}
</button>
</form>
</ModalUi>
@@ -389,7 +410,7 @@ const TeamList = () => {
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
{t("prev")}
</button>
)}
{pageNumbers.map((x, i) => (
@@ -409,7 +430,7 @@ const TeamList = () => {
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
{t("next")}
</button>
)}
</div>
@@ -426,11 +447,13 @@ const TeamList = () => {
alt="img"
/>
</div>
<div className="text-sm font-semibold">No Data Available</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
title={"Add Team"}
title={t("add-team")}
isOpen={isModal}
handleClose={handleFormModal}
>
@@ -447,15 +470,17 @@ const TeamList = () => {
<h1 className="text-[60px] lg:text-[120px] font-semibold">
404
</h1>
<p className="text-[30px] lg:text-[50px]">Page Not Found</p>
<p className="text-[30px] lg:text-[50px]">
{t("page-not-found")}
</p>
</div>
</div>
)}
</>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
{isEnableSubscription && !validplan[isSubscribe.plan] && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAMS"} price={"20"} />
<SubscribeCard plan="TEAMS" />
</div>
)}
</div>
+271 -161
View File
@@ -25,7 +25,13 @@ import {
textInputWidget,
radioButtonWidget,
fetchSubscription,
getContainerScale
getContainerScale,
convertBase64ToFile,
rotatePdfPage,
onClickZoomOut,
onClickZoomIn,
handleRemoveWidgets,
handleRotateWarning
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -40,7 +46,10 @@ import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
import Parse from "parse";
import { useSelector } from "react-redux";
import PdfZoom from "../components/pdf/PdfZoom";
import { useTranslation } from "react-i18next";
import RotateAlert from "../components/RotateAlert";
const TemplatePlaceholder = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const isHeader = useSelector((state) => state.showHeader);
const { templateId } = useParams();
@@ -53,15 +62,18 @@ const TemplatePlaceholder = () => {
const [xySignature, setXYSignature] = useState({});
const [dragKey, setDragKey] = useState();
const [signersdata, setSignersData] = useState([]);
const [signerObjId, setSignerObjId] = useState();
const [signerPos, setSignerPos] = useState([]);
const [isSelectListId, setIsSelectId] = useState();
const [isSendAlert, setIsSendAlert] = useState(false);
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isRotate, setIsRotate] = useState({
status: false,
degree: 0
});
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
message: t("loading-mssg")
});
const [handleError, setHandleError] = useState();
const [currentEmail, setCurrentEmail] = useState();
@@ -71,7 +83,6 @@ const TemplatePlaceholder = () => {
const [tourStatus, setTourStatus] = useState([]);
const [signerUserId, setSignerUserId] = useState();
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
const [contractName, setContractName] = useState("");
const [containerWH, setContainerWH] = useState();
const signRef = useRef(null);
const dragRef = useRef(null);
@@ -89,6 +100,7 @@ const TemplatePlaceholder = () => {
const [isNameModal, setIsNameModal] = useState(false);
const [isTextSetting, setIsTextSetting] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const color = [
"#93a3db",
"#e6c3db",
@@ -179,21 +191,25 @@ const TemplatePlaceholder = () => {
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [divRef.current, isHeader]);
const handleNavigation = () => {
navigate("/subscription");
};
async function checkIsSubscribed() {
const res = await fetchSubscription();
const freeplan = res.plan;
const plan = res.plan;
const billingDate = res.billingDate;
if (freeplan === "freeplan") {
if (plan === "freeplan") {
return true;
} else if (billingDate) {
if (new Date(billingDate) > new Date()) {
setIsSubscribe(true);
return true;
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
} else {
navigate(`/subscription`);
handleNavigation(plan);
}
}
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
@@ -223,8 +239,6 @@ const TemplatePlaceholder = () => {
setPdfDetails(documentData);
setIsSigners(true);
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
setSignerObjId(documentData[0].Signers[0].objectId);
setContractName(documentData[0].Signers[0].className);
setIsSelectId(0);
if (
documentData[0].Placeholders &&
@@ -292,10 +306,10 @@ const TemplatePlaceholder = () => {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setHandleError(t("something-went-wrong-mssg"));
setIsLoading(loadObj);
} else {
setHandleError("No Data Found!");
setHandleError(t("no-data-avaliable"));
const loadObj = {
isLoad: false
};
@@ -304,9 +318,9 @@ const TemplatePlaceholder = () => {
} catch (err) {
console.log("err ", err);
if (err?.response?.data?.code === 101) {
setHandleError("Error: Template not found!");
setHandleError(t("no-data-avaliable"));
} else {
setHandleError("Error: Something went wrong!");
setHandleError(t("something-went-wrong-mssg"));
}
}
const res = await contractUsers();
@@ -331,10 +345,10 @@ const TemplatePlaceholder = () => {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setHandleError(t("something-went-wrong-mssg"));
setIsLoading(loadObj);
} else if (res.length === 0) {
setHandleError("No Data Found!");
setHandleError(t("no-data-avaliable"));
const loadObj = {
isLoad: false
};
@@ -363,21 +377,19 @@ const TemplatePlaceholder = () => {
containerWH
);
const key = randomId();
let filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
const dragTypeValue = item?.text ? item.text : monitor.type;
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
let dropData = [];
let dropData = [],
currentPagePosition;
let placeHolder;
if (item === "onclick") {
// `getBoundingClientRect()` is used to get accurate measurement height of the div
const divHeight = divRef.current.getBoundingClientRect().height;
const dropObj = {
//onclick put placeholder center on pdf
xPosition:
(containerWH.width / 2 - widgetWidth / 2) /
(containerScale * scale),
yPosition:
(containerWH.height / 2 - widgetHeight / 2) /
(containerScale * scale),
xPosition: widgetWidth / 4 + containerWH.width / 2,
yPosition: widgetHeight + divHeight / 2,
isStamp:
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
key: key,
@@ -428,65 +440,40 @@ const TemplatePlaceholder = () => {
pos: dropData
};
}
const { blockColor, Role } = signer;
//adding placholder in existing signer pos array (placaholder)
if (filterSignerPos.length > 0) {
const getPlaceHolder = filterSignerPos[0].placeHolder;
let filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
const getPlaceHolder = filterSignerPos?.placeHolder;
if (getPlaceHolder) {
//checking exist placeholder on same page
currentPagePosition = getPlaceHolder.find(
(data) => data.pageNumber === pageNumber
);
}
//checking current page has already some placeholders then update that placeholder and add upcoming placehoder position
if (getPlaceHolder && currentPagePosition) {
const updatePlace = getPlaceHolder.filter(
(data) => data.pageNumber !== pageNumber
);
const getPageNumer = getPlaceHolder.filter(
(data) => data.pageNumber === pageNumber
const getPos = currentPagePosition?.pos;
const newSignPos = getPos.concat(dropData);
let xyPos = {
pageNumber: pageNumber,
pos: newSignPos
};
updatePlace.push(xyPos);
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
);
//add entry of position for same signer on multiple page
if (getPageNumer.length > 0) {
const getPos = getPageNumer[0].pos;
const newSignPos = getPos.concat(dropData);
let xyPos = {
pageNumber: pageNumber,
pos: newSignPos
};
updatePlace.push(xyPos);
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
);
setSignerPos(updatesignerPos);
} else {
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId
? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
: x
);
setSignerPos(updatesignerPos);
}
setSignerPos(updatesignerPos);
} else {
//adding new placeholder for selected signer in pos array (placeholder)
let placeHolderPos;
if (contractName) {
placeHolderPos = {
signerPtr: {
__type: "Pointer",
className: `${contractName}`,
objectId: signerObjId
},
signerObjId: signerObjId,
blockColor: blockColor ? blockColor : color[isSelectListId],
placeHolder: [placeHolder],
Role: Role ? Role : roleName,
Id: uniqueId
};
} else {
placeHolderPos = {
signerPtr: {},
signerObjId: "",
blockColor: blockColor ? blockColor : color[isSelectListId],
placeHolder: [placeHolder],
Role: Role ? Role : roleName,
Id: uniqueId
};
}
setSignerPos((prev) => [...prev, placeHolderPos]);
//else condition to add placeholder widgets on multiple page first time
const updatesignerPos = signerPos.map((x) =>
x.Id === uniqueId && x?.placeHolder
? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
: x.Id === uniqueId
? { ...x, placeHolder: [placeHolder] }
: x
);
setSignerPos(updatesignerPos);
}
if (dragTypeValue === "dropdown") {
@@ -613,10 +600,6 @@ const TemplatePlaceholder = () => {
//function for delete signature block
const handleDeleteSign = (key, Id) => {
const updateData = [];
// const filterSignerPos = signerPos.filter(
// (data) => data.signerObjId === signerId
// );
const filterSignerPos = signerPos.filter((data) => data.Id === Id);
if (filterSignerPos.length > 0) {
@@ -649,11 +632,11 @@ const TemplatePlaceholder = () => {
setSignerPos(newUpdateSigner);
} else {
const updateFilter = signerPos.filter((data) => data.Id !== Id);
const getRemainPage = filterSignerPos[0].placeHolder.filter(
(data) => data.pageNumber !== pageNumber
);
//condition to check placeholder length is greater than 1 do not need to remove whole placeholder
//array only resove particular widgets
if (getRemainPage && getRemainPage.length > 0) {
const newUpdatePos = filterSignerPos.map((obj) => {
if (obj.Id === Id) {
@@ -667,7 +650,16 @@ const TemplatePlaceholder = () => {
setSignerPos(signerupdate);
} else {
setSignerPos(updateFilter);
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;
});
setSignerPos(updatedData);
}
}
}
@@ -689,19 +681,11 @@ const TemplatePlaceholder = () => {
const touch = e.touches[0]; // Get the first touch point
mouseX = touch.clientX - divRect.left;
mouseY = touch.clientY - divRect.top;
setSignBtnPosition([
{
xPos: mouseX,
yPos: mouseY
}
]);
setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]);
} else {
mouseX = e.clientX - divRect.left;
mouseY = e.clientY - divRect.top;
const xyPosition = {
xPos: mouseX,
yPos: mouseY
};
const xyPosition = { xPos: mouseX, yPos: mouseY };
setXYSignature(xyPosition);
}
};
@@ -710,20 +694,54 @@ const TemplatePlaceholder = () => {
const handleMouseLeave = () => {
setSignBtnPosition([xySignature]);
};
const alertSendEmail = async () => {
if (signerPos.length !== signersdata.length) {
setIsSendAlert(true);
} else {
const isPlaceholderExist = signerPos.every((data) => data.placeHolder);
if (isPlaceholderExist) {
handleSaveTemplate();
} else {
setIsSendAlert(true);
}
};
useEffect(() => {
const timer = setTimeout(() => {
if (signerPos?.length > 0) {
autosavedetails();
}
}, 2000);
return () => clearTimeout(timer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [signerPos]);
// `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 () => {
let signers = [];
if (signersdata?.length > 0) {
signersdata.forEach((x) => {
if (x.objectId) {
const obj = {
__type: "Pointer",
className: "contracts_Contactbook",
objectId: x.objectId
};
signers.push(obj);
}
});
}
try {
const templateCls = new Parse.Object("contracts_Template");
templateCls.id = templateId;
templateCls.set("Placeholders", signerPos);
templateCls.set("Signers", signers);
await templateCls.save();
} catch (err) {
console.log("error in autosave template", err);
}
};
const handleSaveTemplate = async () => {
if (signersdata?.length) {
const loadObj = {
isLoad: true,
message: "This might take some time"
};
const loadObj = { isLoad: true, message: t("loading-mssg") };
setIsLoading(loadObj);
setIsSendAlert(false);
let signers = [];
@@ -739,6 +757,17 @@ const TemplatePlaceholder = () => {
}
});
}
let pdfUrl = pdfDetails[0]?.URL;
if (pdfRotateBase64) {
try {
pdfUrl = await convertBase64ToFile(
pdfDetails[0].Name,
pdfRotateBase64
);
} catch (e) {
console.log("error to convertBase64ToFile in placeholder flow", e);
}
}
try {
const data = {
Placeholders: signerPos,
@@ -749,26 +778,21 @@ const TemplatePlaceholder = () => {
SendinOrder: pdfDetails[0]?.SendinOrder || false,
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
NextReminderDate: pdfDetails[0]?.NextReminderDate
NextReminderDate: pdfDetails[0]?.NextReminderDate,
URL: pdfUrl
};
const updateTemplate = new Parse.Object("contracts_Template");
updateTemplate.id = templateId;
for (const key in data) {
updateTemplate.set(key, data[key]);
}
await updateTemplate.save(null, {
sessionToken: localStorage.getItem("accesstoken")
});
await updateTemplate.save();
setIsCreateDocModal(true);
setIsMailSend(true);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
setIsLoading({ isLoad: false });
} catch (e) {
setIsLoading(false);
alert("Something went wrong, please try again later.");
alert(t("something-went-wrong-mssg"));
console.log("error", e);
}
} else {
@@ -786,7 +810,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourAddbtn"]',
content: () => (
<TourContentWithBtn
message={`Clicking "Add role" button will allow you to add various signer roles. You can attach users to each role in subsequent steps.`}
message={t("tour-mssg.template-placeholder-1")}
isChecked={handleDontShow}
/>
),
@@ -798,7 +822,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourFirst"]',
content: () => (
<TourContentWithBtn
message={`Once roles are added, select a role from list to add a place-holder where he is supposed to sign. The placeholder will appear in the same colour as the role name once you drop it on the document.`}
message={t("tour-mssg.template-placeholder-2")}
isChecked={handleDontShow}
/>
),
@@ -810,7 +834,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="addWidgets"]',
content: () => (
<TourContentWithBtn
message={`Drag or click on a field to add it to the document.`}
message={t("tour-mssg.template-placeholder-3")}
isChecked={handleDontShow}
/>
),
@@ -821,7 +845,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourThird"]',
content: () => (
<TourContentWithBtn
message={`Drag the placeholder for a role anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.`}
message={t("tour-mssg.template-placeholder-4")}
isChecked={handleDontShow}
/>
),
@@ -832,7 +856,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="headerArea"]',
content: () => (
<TourContentWithBtn
message={`Clicking Next will store the current template. After saving, youll be prompted to create a new document from this template if you wish.`}
message={t("tour-mssg.template-placeholder-5")}
isChecked={handleDontShow}
/>
),
@@ -889,19 +913,31 @@ const TemplatePlaceholder = () => {
const handleCreateDocModal = async () => {
setIsCreateDocModal(false);
setIsCreateDoc(true);
let pdfUrl = pdfDetails[0]?.URL;
if (pdfRotateBase64) {
try {
pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfRotateBase64);
} catch (e) {
console.log("error to convertBase64ToFile in placeholder flow", e);
}
}
// handle create document
const res = await createDocument(pdfDetails, signerPos, signersdata);
const res = await createDocument(
pdfDetails,
signerPos,
signersdata,
pdfUrl
);
if (res.status === "success") {
navigate(`/placeHolderSign/${res.id}`, {
state: { title: "Use Template" }
});
setIsCreateDoc(false);
} else {
setHandleError("Error: Something went wrong!");
setHandleError(t("something-went-wrong-mssg"));
setIsCreateDoc(false);
}
};
// `handleAddSigner` is used to open Add Role Modal
const handleAddSigner = () => {
setIsModalRole(true);
@@ -912,8 +948,6 @@ const TemplatePlaceholder = () => {
// save Role in entry in signerList and user
const handleAddRole = (e) => {
e.preventDefault();
setSignerObjId("");
setContractName("");
const count = signersdata.length > 0 ? signersdata.length + 1 : 1;
const Id = randomId();
const index = signersdata.length;
@@ -923,6 +957,15 @@ const TemplatePlaceholder = () => {
blockColor: color[index]
};
setSignersData((prevArr) => [...prevArr, obj]);
const signerPosObj = {
signerPtr: {},
signerObjId: "",
blockColor: color[index],
Role: roleName || "User " + count,
Id: Id
};
setSignerPos((prev) => [...prev, signerPosObj]);
setIsModalRole(false);
setRoleName("");
setUniqueId(Id);
@@ -954,11 +997,12 @@ const TemplatePlaceholder = () => {
setIsMailSend(false);
};
// `handleLinkUser` is used to open Add/Choose Signer Modal when user can link existing or new User with placeholder
// `handleLinkUser` is used to open Add/Choose Signer Modal when user can link existing or new User with placeholder
// and update entry in signersList
const handleLinkUser = (id) => {
setIsAddUser({ [id]: true });
};
// `handleAddUser` is used to adduser
const handleAddUser = (data) => {
const signerPtr = {
__type: "Pointer",
@@ -1087,7 +1131,13 @@ const TemplatePlaceholder = () => {
status: status,
defaultValue: defaultValue,
isReadOnly: isReadOnly || false,
isHideLabel: isHideLabel || false
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
}
};
}
@@ -1119,7 +1169,13 @@ const TemplatePlaceholder = () => {
},
isReadOnly: isReadOnly || false,
defaultValue: defaultValue,
isHideLabel: isHideLabel || false
isHideLabel: isHideLabel || false,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
}
};
}
@@ -1131,7 +1187,13 @@ const TemplatePlaceholder = () => {
name: dropdownName,
status: status,
values: dropdownOptions,
defaultValue: defaultValue
defaultValue: defaultValue,
fontSize:
fontSize || currWidgetsDetails?.options?.fontSize || "12",
fontColor:
fontColor ||
currWidgetsDetails?.options?.fontColor ||
"black"
}
};
}
@@ -1158,6 +1220,8 @@ const TemplatePlaceholder = () => {
}
}
}
setFontSize();
setFontColor();
};
const handleWidgetdefaultdata = (defaultdata) => {
@@ -1248,6 +1312,7 @@ const TemplatePlaceholder = () => {
setCurrWidgetsDetails({});
handleNameModal();
};
const handleNameModal = () => {
setIsNameModal(false);
setCurrWidgetsDetails({});
@@ -1256,6 +1321,38 @@ const TemplatePlaceholder = () => {
setIsCheckbox(false);
};
const clickOnZoomIn = () => {
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
};
const clickOnZoomOut = () => {
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
};
//`handleRotationFun` function is used to roatate pdf particular page
const handleRotationFun = async (rotateDegree) => {
const isRotate = handleRotateWarning(signerPos, pageNumber);
if (isRotate) {
setIsRotate({ status: true, degree: rotateDegree });
} else {
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
rotateDegree,
pageNumber - 1,
pdfRotateBase64
);
setPdfRotatese64(urlDetails.base64);
}
};
const handleRemovePlaceholder = async () => {
handleRemoveWidgets(setSignerPos, signerPos, pageNumber, setIsRotate);
const urlDetails = await rotatePdfPage(
pdfDetails[0].URL,
isRotate.degree,
pageNumber - 1,
pdfRotateBase64
);
setPdfRotatese64(urlDetails.base64);
};
return (
<>
<Title title={"Template"} />
@@ -1303,45 +1400,38 @@ const TemplatePlaceholder = () => {
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
setScale={setScale}
scale={scale}
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
handleRotationFun={handleRotationFun}
/>
<div className="w-full md:w-[95%]">
{/* this modal is used show alert set placeholder for all signers before send mail */}
<ModalUi
isOpen={isSendAlert}
title={"Fields required"}
title={t("fields-required")}
handleClose={() => setIsSendAlert(false)}
>
<div className="h-full p-[20px]">
<p>
Please add at least one signature field for all roles.
</p>
<p>{t("template-signature-field")}</p>
</div>
</ModalUi>
<ModalUi
isOpen={!IsReceipent}
title={"Roles"}
title={t("roles")}
handleClose={() => setIsReceipent(true)}
>
<div className="h-full p-[20px] text-center font-medium">
<p>Please add roles first</p>
<p>{t("template-role-alert")}</p>
</div>
</ModalUi>
{/* this modal is used show send mail message and after send mail success message */}
<ModalUi
isOpen={isCreateDocModal}
title={"Create Document"}
title={t("create-document")}
handleClose={() => setIsCreateDocModal(false)}
>
<div className="h-full p-[20px]">
<p>
Do you want to create a document using the template you
just created ?
</p>
<p>{t("template-created-alert")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
{currentEmail.length > 0 && (
<>
@@ -1350,7 +1440,7 @@ const TemplatePlaceholder = () => {
type="button"
className="op-btn op-btn-primary"
>
Yes
{t("yes")}
</button>
<button
onClick={() => {
@@ -1360,7 +1450,7 @@ const TemplatePlaceholder = () => {
type="button"
className="op-btn op-btn-secondary ml-2"
>
No
{t("no")}
</button>
</>
)}
@@ -1369,24 +1459,24 @@ const TemplatePlaceholder = () => {
{isCreateDoc && <LoaderWithMsg isLoading={isLoading} />}
<ModalUi
isOpen={isShowEmail}
title={"Signers alert"}
title={t("signers-alert")}
handleClose={() => setIsShowEmail(false)}
>
<div className="h-full p-[20px]">
<p>Please select signer for add placeholder!</p>
<p>{t("template-creation-alert-1")}</p>
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
<button
onClick={() => setIsShowEmail(false)}
type="button"
className="op-btn op-btn-primary"
>
Ok
{t("ok")}
</button>
</div>
</ModalUi>
<DropdownWidgetOption
type={radioButtonWidget}
title="Radio group"
title={t("radio-group")}
showDropdown={isRadio}
setShowDropdown={setIsRadio}
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
@@ -1394,10 +1484,14 @@ const TemplatePlaceholder = () => {
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
<DropdownWidgetOption
type="checkbox"
title="Checkbox"
title={t("checkbox")}
showDropdown={isCheckbox}
setShowDropdown={setIsCheckbox}
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
@@ -1405,10 +1499,14 @@ const TemplatePlaceholder = () => {
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
<DropdownWidgetOption
type="dropdown"
title="Dropdown options"
title={t("dropdown-options")}
showDropdown={showDropdown}
setShowDropdown={setShowDropdown}
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
@@ -1416,6 +1514,10 @@ const TemplatePlaceholder = () => {
setCurrWidgetsDetails={setCurrWidgetsDetails}
handleClose={handleNameModal}
isSubscribe={isSubscribe}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
<PlaceholderCopy
isPageCopy={isPageCopy}
@@ -1429,7 +1531,7 @@ const TemplatePlaceholder = () => {
/>
{/* pdf header which contain funish back button */}
<Header
completeBtnTitle={"Next"}
completeBtnTitle={t("next")}
isPlaceholder={true}
pageNumber={pageNumber}
allPages={allPages}
@@ -1443,6 +1545,9 @@ const TemplatePlaceholder = () => {
currentSigner={true}
setIsEditTemplate={handleEditTemplateModal}
dataTut4="reactourFour"
handleRotationFun={handleRotationFun}
clickOnZoomIn={clickOnZoomIn}
clickOnZoomOut={clickOnZoomOut}
/>
<div
ref={divRef}
@@ -1474,7 +1579,6 @@ const TemplatePlaceholder = () => {
signersdata={signersdata}
setIsPageCopy={setIsPageCopy}
setSignKey={setSignKey}
setSignerObjId={setSignerObjId}
isDragging={isDragging}
setShowDropdown={setShowDropdown}
setCurrWidgetsDetails={setCurrWidgetsDetails}
@@ -1488,6 +1592,11 @@ const TemplatePlaceholder = () => {
setScale={setScale}
scale={scale}
setIsSelectId={setIsSelectId}
pdfRotateBase64={pdfRotateBase64}
fontSize={fontSize}
setFontSize={setFontSize}
fontColor={fontColor}
setFontColor={setFontColor}
/>
)}
</div>
@@ -1512,9 +1621,7 @@ const TemplatePlaceholder = () => {
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
isSigners={isSigners}
setIsShowEmail={setIsShowEmail}
isMailSend={isMailSend}
@@ -1526,7 +1633,7 @@ const TemplatePlaceholder = () => {
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
title={"Roles"}
title={t("roles")}
initial={true}
isTemplateFlow={true}
sendInOrder={pdfDetails[0].SendinOrder}
@@ -1544,16 +1651,14 @@ const TemplatePlaceholder = () => {
setSignerPos={setSignerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setRoleName={setRoleName}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
handleAddSigner={handleAddSigner}
setUniqueId={setUniqueId}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
title={"Roles"}
title={t("roles")}
sendInOrder={pdfDetails[0]?.SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
@@ -1573,7 +1678,7 @@ const TemplatePlaceholder = () => {
isDragStamp={isDragStamp}
isSignYourself={false}
addPositionOfSignature={addPositionOfSignature}
title={"Roles"}
title={t("roles")}
initial={true}
isTemplateFlow={true}
/>
@@ -1604,7 +1709,7 @@ const TemplatePlaceholder = () => {
/>
</div>
<ModalUi
title={"Edit Template"}
title={t("edit-template")}
isOpen={isEditTemplate}
handleClose={handleEditTemplateModal}
>
@@ -1627,6 +1732,11 @@ const TemplatePlaceholder = () => {
fontColor={fontColor}
setFontColor={setFontColor}
/>
<RotateAlert
isRotate={isRotate.status}
setIsRotate={setIsRotate}
handleRemoveWidgets={handleRemovePlaceholder}
/>
</DndProvider>
</>
);
+26 -18
View File
@@ -4,8 +4,9 @@ import Parse from "parse";
import { NavLink, useNavigate } from "react-router-dom";
import Alert from "../primitives/Alert";
import Title from "../components/Title";
import { useTranslation } from "react-i18next";
const UpdateExistUserAdmin = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const [formdata, setFormdata] = useState({ email: "", masterkey: "" });
const [loader, setLoader] = useState(true);
@@ -20,7 +21,7 @@ const UpdateExistUserAdmin = () => {
try {
const isAdminExist = await Parse.Cloud.run("checkadminexist");
if (isAdminExist !== "not_exist") {
console.log("isAdminExist ", isAdminExist);
// console.log("isAdminExist ", isAdminExist);
setErrMsg("Admin already exists.");
}
} catch (err) {
@@ -41,19 +42,19 @@ const UpdateExistUserAdmin = () => {
);
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
if (updateUserAsAdmin === "admin_created") {
setIsAlert({ type: "success", msg: "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: "Invalid masterkey" }));
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
} else if (err.code === 101) {
setIsAlert((prev) => ({ ...prev, msg: "User not found" }));
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
} else if (err.code === 137) {
setIsAlert((prev) => ({ ...prev, msg: "Admin already exist" }));
setIsAlert((prev) => ({ ...prev, msg: t("admin-already-exist") }));
} else {
setErrMsg("Something went wrong.");
setErrMsg(t("something-went-wrong-mssg"));
}
} finally {
setIsSubmitLoading(false);
@@ -65,11 +66,7 @@ const UpdateExistUserAdmin = () => {
return (
<div className="h-screen flex justify-center">
<Title title={"Add Admin"} />
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
{loader ? (
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
<Loader />
@@ -90,7 +87,7 @@ const UpdateExistUserAdmin = () => {
</div>
)}
<h2 className="text-[30px] text-center mt-3 font-medium">
Opensign Setup
{t("opensign-Setup")}
</h2>
<NavLink
to="https://discord.com/invite/xe9TDuyAyj"
@@ -98,7 +95,7 @@ const UpdateExistUserAdmin = () => {
rel="noopener noreferrer"
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
>
Join our discord server
{t("join-discord")}
<i
aria-hidden="true"
className="fa-brands fa-discord ml-1"
@@ -107,7 +104,8 @@ const UpdateExistUserAdmin = () => {
</NavLink>
<div className="px-6 py-3 text-xs">
<label>
Email <span className="text-[red] text-[13px]">*</span>
{t("email")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
id="email"
@@ -117,14 +115,20 @@ const UpdateExistUserAdmin = () => {
onChange={(e) =>
setFormdata((prev) => ({
...prev,
email: e.target.value?.toLowerCase()
email: e.target.value
?.toLowerCase()
?.replace(/\s/g, "")
}))
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
<hr className="my-2 border-none" />
<label>
Master key{" "}
{t("master-key")}{" "}
<span className="text-[red] text-[13px]">*</span>
</label>
<input
@@ -137,6 +141,10 @@ const UpdateExistUserAdmin = () => {
masterkey: e.target.value
}))
}
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
/>
@@ -163,7 +171,7 @@ const UpdateExistUserAdmin = () => {
className="op-btn op-btn-primary w-full"
disabled={loader}
>
{loader ? "Loading..." : "Next"}
{loader ? t("loading") : t("next")}
</button>
</div>
</div>
+56 -82
View File
@@ -9,11 +9,14 @@ import Tooltip from "../primitives/Tooltip";
import AddUser from "../components/AddUser";
import SubscribeCard from "../primitives/SubscribeCard";
import { isEnableSubscription } from "../constant/const";
import { checkIsSubscribedTeam } from "../constant/Utils";
import { checkIsSubscribed } from "../constant/Utils";
import Title from "../components/Title";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
// const actions = [];
const UserList = () => {
const { t } = useTranslation();
const [userList, setUserList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isModal, setIsModal] = useState(false);
@@ -24,8 +27,9 @@ const UserList = () => {
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isActiveModal, setIsActiveModal] = useState({});
const [isActLoader, setIsActLoader] = useState({});
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
const [isAdmin, setIsAdmin] = useState(false);
const [formHeader, setFormHeader] = useState(t("add-user"));
const recordperPage = 10;
const startIndex = (currentPage - 1) * recordperPage; // user per page
@@ -81,17 +85,22 @@ const UserList = () => {
return pages;
};
const pageNumbers = getPaginationRange();
// to slice out 10 objects from array for current page
const indexOfLastDoc = currentPage * recordperPage;
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
const currentList = userList?.slice(indexOfFirstDoc, indexOfLastDoc);
useEffect(() => {
fetchUserList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function fetchUserList() {
try {
setIsLoader(true);
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
} else {
setIsSubscribe(true);
setIsSubscribe({ plan: "teams-yearly", isValid: true });
}
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
if (extUser) {
@@ -110,7 +119,7 @@ const UserList = () => {
setUserList(_userRes);
} catch (err) {
console.log("Err in fetch userlist", err);
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
} finally {
setTimeout(() => {
setIsAlert({ type: "success", msg: "" });
@@ -123,13 +132,19 @@ const UserList = () => {
};
// Change page
const paginateFront = () => setCurrentPage(currentPage + 1);
const paginateBack = () => setCurrentPage(currentPage - 1);
// const handleActionBtn = (act, item) => {
// if (act.action === "delete") {
// setIsDeleteModal({ [item.objectId]: true });
// }
// };
const paginateFront = () => {
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
if (currentPage < lastValue) {
setCurrentPage(currentPage + 1);
}
};
const paginateBack = () => {
if (startIndex > 0) {
setCurrentPage(currentPage - 1);
}
};
const handleUserData = (userData) => {
setUserList((prev) => [userData, ...prev]);
};
@@ -166,10 +181,11 @@ const UserList = () => {
await extUser.save();
setIsAlert({
type: !IsDisabled === true ? "danger" : "success",
msg: !IsDisabled === true ? "User deactivated." : "User activated."
msg:
!IsDisabled === true ? t("user-deactivated") : t("user-activated")
});
} catch (err) {
setIsAlert({ type: "danger", msg: "something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
console.log("err in disable team", err);
} finally {
setIsActLoader({});
@@ -193,20 +209,16 @@ const UserList = () => {
<Loader />
</div>
)}
{isSubscribe && !isLoader && (
{validplan[isSubscribe.plan] && !isLoader && (
<>
{isAdmin ? (
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
{isAlert.msg && (
<Alert type={isAlert.type}>
<div className="ml-3">{isAlert.msg}</div>
</Alert>
)}
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">
Users{" "}
{t("report-name.Users")}
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={"users from Teams"} />
<Tooltip message={t("users-from-teams")} />
</span>
</div>
<div
@@ -222,7 +234,7 @@ const UserList = () => {
<tr className="border-y-[1px]">
{heading?.map((item, index) => (
<th key={index} className="px-4 py-2">
{item}
{t(`report-heading.${item}`)}
</th>
))}
</tr>
@@ -230,7 +242,7 @@ const UserList = () => {
<tbody className="text-[12px]">
{userList?.length > 0 && (
<>
{userList.map((item, index) => (
{currentList.map((item, index) => (
<tr className="border-y-[1px]" key={index}>
{heading.includes("Sr.No") && (
<th className="px-4 py-2">
@@ -261,16 +273,16 @@ const UserList = () => {
{isActiveModal[item.objectId] && (
<ModalUi
isOpen
title={"User status"}
title={t("user-status")}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to{" "}
{t("are-you-sure")}{" "}
{item?.IsDisabled
? "activate"
: "deactivate"}{" "}
this user?
? t("activate")
: t("deactivate")}{" "}
{t("this-user")}?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
@@ -280,13 +292,13 @@ const UserList = () => {
}
className="op-btn op-btn-primary"
>
Yes
{t("yes")}
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
{t("no")}
</button>
</div>
</div>
@@ -294,49 +306,6 @@ const UserList = () => {
)}
</td>
)}
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
{actions?.length > 0 &&
actions.map((act, index) => (
<button
key={index}
onClick={() => handleActionBtn(act, item)}
title={act.hoverLabel}
className={`${
act?.btnColor ? act.btnColor : ""
} op-btn op-btn-sm`}
>
<i className={act.btnIcon}></i>
</button>
))}
{isDeleteModal[item.objectId] && (
<ModalUi
isOpen
title={"Delete User"}
handleClose={handleClose}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to delete this user?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="op-btn op-btn-primary"
>
Yes
</button>
<button
onClick={handleClose}
className="op-btn op-btn-secondary"
>
No
</button>
</div>
</div>
</ModalUi>
)}
</td> */}
</tr>
))}
</>
@@ -350,7 +319,7 @@ const UserList = () => {
onClick={() => paginateBack()}
className="op-join-item op-btn op-btn-sm"
>
Prev
{t("prev")}
</button>
)}
{pageNumbers.map((x, i) => (
@@ -370,7 +339,7 @@ const UserList = () => {
onClick={() => paginateFront()}
className="op-join-item op-btn op-btn-sm"
>
Next
{t("next")}
</button>
)}
</div>
@@ -387,18 +356,21 @@ const UserList = () => {
alt="img"
/>
</div>
<div className="text-sm font-semibold">No Data Available</div>
<div className="text-sm font-semibold">
{t("no-data-avaliable")}
</div>
</div>
)}
<ModalUi
title={"Add User"}
isOpen={isModal}
title={formHeader}
handleClose={handleFormModal}
>
<AddUser
setIsAlert={setIsAlert}
handleUserData={handleUserData}
closePopup={handleFormModal}
setFormHeader={setFormHeader}
/>
</ModalUi>
</div>
@@ -408,15 +380,17 @@ const UserList = () => {
<h1 className="text-[60px] lg:text-[120px] font-semibold">
404
</h1>
<p className="text-[30px] lg:text-[50px]">Page Not Found</p>
<p className="text-[30px] lg:text-[50px]">
{t("page-not-found")}
</p>
</div>
</div>
)}
</>
)}
{!isSubscribe && isEnableSubscription && !isLoader && (
{isEnableSubscription && !validplan[isSubscribe.plan] && !isLoader && (
<div data-tut="apisubscribe">
<SubscribeCard plan={"TEAMS"} price={"20"} />
<SubscribeCard plan="TEAMS" />
</div>
)}
</div>
+262 -126
View File
@@ -7,18 +7,27 @@ import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios";
import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, isStaging } from "../constant/const";
import {
checkIsSubscribed,
checkIsSubscribedTeam,
handleSendOTP
copytoData,
handleSendOTP,
openInNewTab
} from "../constant/Utils";
import Upgrade from "../primitives/Upgrade";
import ModalUi from "../primitives/ModalUi";
import Loader from "../primitives/Loader";
import { validplan } from "../json/plansArr";
import { useTranslation } from "react-i18next";
import SelectLanguage from "../components/pdf/SelectLanguage";
import { RWebShare } from "react-web-share";
import Alert from "../primitives/Alert";
import Tour from "reactour";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
function UserProfile() {
const navigate = useNavigate();
const { t } = useTranslation();
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
@@ -32,6 +41,7 @@ function UserProfile() {
const [isDisableDocId, setIsDisableDocId] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isUpgrade, setIsUpgrade] = useState(false);
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
const [publicUserName, setPublicUserName] = useState(
extendUser && extendUser?.[0]?.UserName
);
@@ -46,25 +56,41 @@ function UserProfile() {
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
const [userNameError, setUserNameError] = useState("");
const [tagLine, setTagLine] = useState(
extendUser && extendUser?.[0]?.Tagline
);
const [isTeam, setIsTeam] = useState(false);
const [isPlan, setIsPlan] = useState({ plan: "", isValid: false });
const [tourStatus, setTourStatus] = useState([]);
const [isProfileTour, setIsProfileTour] = useState(false);
const [isDontShow, setIsDontShow] = useState(false);
const getPublicUrl = isStaging
? `https://staging.opensign.me/${extendUser?.[0]?.UserName}`
: `https://opensign.me/${extendUser?.[0]?.UserName}`;
useEffect(() => {
getUserDetail();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const getUserDetail = async () => {
setIsLoader(true);
const extClass = localStorage.getItem("Extand_Class");
const jsonSender = JSON.parse(extClass);
const tourstatuss = jsonSender[0]?.TourStatus && jsonSender[0].TourStatus;
if (!isEnableSubscription) {
setIsProfileTour(true);
} else if (tourstatuss && tourstatuss.length > 0) {
setTourStatus(tourstatuss);
const checkTourRecipients = tourstatuss.filter(
(data) => data.profileTour
);
if (checkTourRecipients && checkTourRecipients.length > 0) {
setIsProfileTour(checkTourRecipients[0]?.profileTour);
}
}
const HeaderDocId = jsonSender[0]?.HeaderDocId;
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
const getIsTeam = await checkIsSubscribedTeam();
setIsSubscribe(getIsSubscribe);
setIsTeam(getIsTeam);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe.isValid);
setIsPlan(subscribe);
}
if (HeaderDocId) {
setIsDisableDocId(HeaderDocId);
@@ -86,7 +112,7 @@ function UserProfile() {
setIsLoader(false);
}
} catch (e) {
alert("something went wrong!");
alert(t("something-went-wrong-mssg"));
}
}
};
@@ -98,10 +124,8 @@ function UserProfile() {
});
if (res) {
setIsLoader(false);
setUserNameError("user name already exist");
setTimeout(() => {
setUserNameError("");
}, 3000);
setIsAlert({ type: "danger", message: t("user-name-exist") });
setTimeout(() => setIsAlert({}), 3000);
return res;
}
} catch (e) {
@@ -140,14 +164,14 @@ function UserProfile() {
Name: res.name,
Phone: res?.phone || ""
});
alert("Profile updated successfully.");
alert(t("profile-update-alert"));
setEditMode(false);
setIsLoader(false);
//navigate("/dashboard/35KBoSgoAK");
}
},
(error) => {
alert("Something went wrong.");
alert(t("something-went-wrong-mssg"));
console.error("Error while updating tour", error);
setIsLoader(false);
}
@@ -161,38 +185,40 @@ function UserProfile() {
// `updateExtUser` is used to update user details in extended class
const updateExtUser = async (obj) => {
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId;
const body = {
Phone: obj?.Phone || "",
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company,
UserName: publicUserName || "",
Tagline: tagLine || ""
};
try {
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
const ExtUserId = extData[0].objectId;
const body = {
Phone: obj?.Phone || "",
Name: obj.Name,
HeaderDocId: isDisableDocId,
JobTitle: jobTitle,
Company: company,
UserName: publicUserName || "",
Tagline: tagLine || "",
Language: obj?.language || ""
};
await axios.put(
parseBaseUrl + "classes/contracts_Users/" + ExtUserId,
body,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
await axios.put(
parseBaseUrl + "classes/contracts_Users/" + ExtUserId,
body,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
}
);
const res = await Parse.Cloud.run("getUserDetails", {
email: extData[0].Email
});
);
const res = await Parse.Cloud.run("getUserDetails");
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
previousPublicUserName.current = publicUserName;
// console.log("updateRes ", updateRes);
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
previousPublicUserName.current = publicUserName;
} catch (e) {
console.log("error in save data in contracts_Users class");
}
};
// file upload function
const fileUpload = async (file) => {
@@ -264,11 +290,12 @@ function UserProfile() {
});
if (resEmail?.message === "Email is verified.") {
setIsEmailVerified(true);
alert(t("Email-verified-alert-1"));
} else if (resEmail?.message === "Email is already verified.") {
setIsEmailVerified(true);
alert(t("Email-verified-alert-2"));
}
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
} catch (error) {
alert(error.message);
@@ -282,7 +309,7 @@ function UserProfile() {
setOtpLoader(true);
await handleSendOTP();
setOtpLoader(false);
alert("OTP sent on you email");
alert(t("otp-sent-alert"));
};
//function to handle onchange username and restrict 6-characters username for free users
const handleOnchangeUserName = (e) => {
@@ -303,6 +330,80 @@ function UserProfile() {
setJobTitle(extendUser?.[0]?.JobTitle);
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
};
const handlePaidRoute = () => {
navigate("/subscription");
};
const copytoclipboard = () => {
copytoData(getPublicUrl);
setIsAlert({ type: "success", message: t("copied") });
setTimeout(() => setIsAlert({}), 3000);
};
const tourConfig = [
{
content: () => (
<TourContentWithBtn
message={t("tour-mssg.public-template")}
isChecked={handleDontShow}
video="https://www.youtube.com/embed/_wB4UA7Jz5Q?si=I40CI3nVUWQzf42Y"
/>
),
position: "top",
style: { fontSize: "13px" }
}
];
//function for update TourStatus
const closeTour = async () => {
try {
setIsProfileTour(true);
if (isDontShow) {
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const profileIndex = tourStatus.findIndex(
(obj) => obj["profileTour"] === false || obj["profileTour"] === true
);
if (profileIndex !== -1) {
updatedTourStatus[profileIndex] = { profileTour: true };
} else {
updatedTourStatus.push({ profileTour: true });
}
} else {
updatedTourStatus = [{ profileTour: true }];
}
await axios.put(
`${localStorage.getItem("baseUrl")}classes/contracts_Users/${
extendUser?.[0]?.objectId
}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
}
}
);
const res = await Parse.Cloud.run("getUserDetails");
const json = JSON.parse(JSON.stringify([res]));
const extRes = JSON.stringify(json);
localStorage.setItem("Extand_Class", extRes);
}
} catch (err) {
console.log("axois err ", err);
alert(t("something-went-wrong-mssg"));
}
};
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
const handleOnlickHelp = () => {
setIsProfileTour(false);
};
return (
<React.Fragment>
<Title title={"Profile"} />
@@ -312,11 +413,19 @@ function UserProfile() {
</div>
) : (
<div className="flex justify-center items-center w-full relative">
{userNameError && (
<div className="z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]">
{userNameError}
</div>
{isAlert.message && (
<Alert className="z-[1000] fixed top-[10%]" type={isAlert.type}>
{isAlert.message}
</Alert>
)}
<Tour
className="md:!min-w-[650px]"
onRequestClose={closeTour}
steps={tourConfig}
isOpen={!isProfileTour}
rounded={5}
closeWithMask={false}
/>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box w-[450px]">
<div className="flex flex-col justify-center items-center my-4">
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
@@ -358,12 +467,12 @@ function UserProfile() {
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Name:</span>{" "}
<span className="font-semibold">{t("name")}:</span>{" "}
{editmode ? (
<input
type="text"
value={name}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
onChange={(e) => SetName(e.target.value)}
/>
) : (
@@ -375,11 +484,11 @@ function UserProfile() {
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Phone:</span>{" "}
<span className="font-semibold">{t("phone")}:</span>{" "}
{editmode ? (
<input
type="text"
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
onChange={(e) => SetPhone(e.target.value)}
value={Phone}
/>
@@ -388,7 +497,7 @@ function UserProfile() {
)}
</li>
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Email:</span>{" "}
<span className="font-semibold">{t("email")}:</span>{" "}
<span>{UserProfile && UserProfile.email}</span>
</li>
<li
@@ -396,12 +505,12 @@ function UserProfile() {
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Company:</span>{" "}
<span className="font-semibold">{t("company")}:</span>{" "}
{editmode ? (
<input
type="text"
value={company}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
onChange={(e) => setCompany(e.target.value)}
/>
) : (
@@ -413,12 +522,12 @@ function UserProfile() {
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">Job title:</span>{" "}
<span className="font-semibold">{t("job-title")}:</span>{" "}
{editmode ? (
<input
type="text"
value={jobTitle}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
onChange={(e) => setJobTitle(e.target.value)}
/>
) : (
@@ -426,7 +535,7 @@ function UserProfile() {
)}
</li>
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Is Email verified:</span>{" "}
<span className="font-semibold">{t("is-email-verified")}:</span>{" "}
<span>
{isEmailVerified ? (
"Verified"
@@ -446,66 +555,71 @@ function UserProfile() {
</li>
{isEnableSubscription && (
<>
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
<li className="flex flex-row justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold flex gap-1">
Public profile :{" "}
<Tooltip
maxWidth="max-w-[250px]"
message={`This is your public URL. Copy or share it with the signer, and you will be able to see all your publicly set templates.`}
/>
{t("public-profile")} :{" "}
<Tooltip handleOnlickHelp={handleOnlickHelp} />
</span>
<div className="flex md:flex-row flex-col md:items-center">
<span className="mb-1 md:mb-1">opensign.me/</span>
{editmode ? (
<input
maxLength={40}
style={{
border:
!isSubscribe &&
publicUserName?.length > 0 &&
publicUserName?.length < 9 &&
"solid red"
}}
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
/>
<div className="flex flex-row items-center gap-1">
{editmode || !extendUser?.[0]?.UserName ? (
<>
<input
maxLength={40}
onChange={handleOnchangeUserName}
value={publicUserName}
disabled={!editmode}
placeholder="enter user name"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
/>
</>
) : (
<input
value={extendUser?.[0]?.UserName}
disabled
placeholder="enter user name"
className="op-input op-input-bordered op-input-xs"
/>
<div className="flex flex-row gap-1 items-center justify-between md:justify-start">
<span
rel="noreferrer"
target="_blank"
onClick={() => openInNewTab(getPublicUrl)}
className="cursor-pointer underline hover:text-blue-800 w-[110px] md:w-[150px] whitespace-nowrap overflow-hidden text-ellipsis"
>
{isStaging
? `staging.opensign.me/${extendUser?.[0]?.UserName}`
: `opensign.me/${extendUser?.[0]?.UserName}`}
</span>
<div className="flex items-center gap-2">
<RWebShare
data={{ url: getPublicUrl, title: "Sign url" }}
>
<button className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm ">
<i className="fa-light fa-share-from-square"></i>{" "}
</button>
</RWebShare>
<button
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
onClick={() => copytoclipboard()}
>
<i className="fa-light fa-link"></i>{" "}
</button>
</div>
</div>
)}
</div>
</li>
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
<li className="flex flex-row justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold flex gap-1">
Tagline :{" "}
<Tooltip
maxWidth="max-w-[250px]"
message={`This Tagline will appear on your OpenSign public profile for ex. https://opensign.me/alex`}
/>
{t("tagline")} :{" "}
<Tooltip handleOnlickHelp={handleOnlickHelp} />
</span>
<div className="flex md:flex-row flex-col md:items-center">
{editmode ? (
<div className="flex flex-row md:items-center gap-1">
{editmode || !extendUser?.[0]?.Tagline ? (
<input
maxLength={40}
onChange={handleOnchangeTagLine}
value={tagLine}
disabled={!editmode}
placeholder="enter tagline"
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
/>
) : (
<input
value={extendUser?.[0]?.Tagline}
disabled
placeholder="enter tagline"
className="op-input op-input-bordered op-input-xs"
/>
<span>{extendUser?.[0]?.Tagline}</span>
)}
</div>
</li>
@@ -516,26 +630,32 @@ function UserProfile() {
<div className="flex justify-between items-center py-2">
<span
className={
isTeam ? "font-semibold" : "font-semibold text-gray-300"
validplan[isPlan.plan]
? "font-semibold"
: "font-semibold text-gray-300"
}
>
Disable DocumentId :{" "}
{t("disable-documentId")} :{" "}
<Tooltip
url={
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
}
/>
{!isTeam && isEnableSubscription && <Upgrade />}
{!validplan[isPlan.plan] && isEnableSubscription && (
<Upgrade />
)}
</span>
<label
className={`${
isTeam
validplan[isPlan.plan]
? `${editmode ? "cursor-pointer" : ""}`
: "pointer-events-none opacity-50"
} relative block items-center mb-0`}
>
<input
disabled={isTeam ? false : true}
disabled={
validplan[isPlan.plan] && editmode ? false : true
}
type="checkbox"
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
checked={isDisableDocId}
@@ -545,6 +665,17 @@ function UserProfile() {
</div>
</li>
)}
<li
className={`flex justify-between items-center border-b-[1px] border-gray-300 break-all ${
editmode ? "py-1.5" : "py-2"
}`}
>
<span className="font-semibold">{t("language")}:</span>{" "}
<SelectLanguage
isProfile={true}
updateExtUser={updateExtUser}
/>
</li>
</ul>
<div
className={`${
@@ -555,6 +686,7 @@ function UserProfile() {
type="button"
onClick={(e) => {
if (
editmode &&
!isSubscribe &&
publicUserName?.length > 0 &&
publicUserName?.length < 9
@@ -566,7 +698,7 @@ function UserProfile() {
}}
className="op-btn op-btn-primary"
>
{editmode ? "Save" : "Edit"}
{editmode ? t("save") : t("edit")}
</button>
<button
type="button"
@@ -577,14 +709,14 @@ function UserProfile() {
editmode ? "op-btn-ghost" : "op-btn-secondary"
}`}
>
{editmode ? "Cancel" : "Change Password"}
{editmode ? t("cancel") : t("change-password")}
</button>
</div>
</div>
{isVerifyModal && (
<ModalUi
isOpen
title={"OTP verification"}
title={t("otp-verification")}
handleClose={handleCloseVerifyModal}
>
{otpLoader ? (
@@ -594,32 +726,37 @@ function UserProfile() {
) : (
<form onSubmit={(e) => handleVerifyEmail(e)}>
<div className="px-6 py-3 text-base-content">
<label className="mb-2">Enter OTP</label>
<label className="mb-2">{t("enter-otp")}</label>
<input
onInvalid={(e) =>
e.target.setCustomValidity(t("input-required"))
}
onInput={(e) => e.target.setCustomValidity("")}
required
type="tel"
pattern="[0-9]{4}"
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
placeholder="Enter OTP received over email"
placeholder={t("otp-placeholder")}
value={otp}
onChange={(e) => setOtp(e.target.value)}
/>
</div>
<div className="px-6 mb-3">
<button type="submit" className="op-btn op-btn-primary">
Verify
{t("verify")}
</button>
<button
className="op-btn op-btn-secondary ml-2"
onClick={(e) => handleResend(e)}
>
Resend
{t("resend")}
</button>
</div>
</form>
)}
</ModalUi>
)}
{isUpgrade && (
<div className="op-modal op-modal-open">
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
@@ -633,17 +770,16 @@ function UserProfile() {
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
<div className="op-card-body">
<h2 className="op-card-title">Upgrade to Plan</h2>
<p>
To have a username less than 8 character please
subscribe
</p>
<h2 className="op-card-title">
{t("upgrade-to")} {t("plan")}
</h2>
<p>{t("user-name-limit-char")}</p>
<div className="op-card-actions justify-end">
<button
onClick={() => navigate("/subscription")}
onClick={() => handlePaidRoute()}
className="op-btn op-btn-accent"
>
Upgrade Now
{t("upgrade-now")}
</button>
</div>
</div>
+25 -23
View File
@@ -10,19 +10,16 @@ import Tooltip from "../primitives/Tooltip";
import Loader from "../primitives/Loader";
import SubscribeCard from "../primitives/SubscribeCard";
import Tour from "reactour";
const tourSteps = [
{
selector: '[data-tut="webhooksubscribe"]',
content: "Upgrade now to set webhook"
}
];
import { useTranslation } from "react-i18next";
function Webhook() {
const { t } = useTranslation();
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [webhook, setWebhook] = useState();
const [isLoader, setIsLoader] = useState(true);
const [isModal, setIsModal] = useState(false);
const [isSubscribe, setIsSubscribe] = useState(false);
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
const [error, setError] = useState("");
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
const [isTour, setIsTour] = useState(false);
@@ -30,11 +27,16 @@ function Webhook() {
fetchWebhook();
// eslint-disable-next-line
}, []);
const tourSteps = [
{
selector: '[data-tut="webhooksubscribe"]',
content: t("tour-mssg.webhook-1")
}
];
const fetchWebhook = async () => {
if (isEnableSubscription) {
const getIsSubscribe = await checkIsSubscribed();
setIsSubscribe(getIsSubscribe);
const subscribe = await checkIsSubscribed();
setIsSubscribe(subscribe);
}
try {
const extRes = await Parse.Cloud.run("getUserDetails");
@@ -64,13 +66,13 @@ function Webhook() {
const res = await axios.post(url, params, { headers: headers });
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsAlert({ type: "success", msg: "Webhook added successfully." });
setIsAlert({ type: "success", msg: t("webhook-added") });
} else {
console.error("Error while generating webhook");
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
}
} catch (error) {
setIsAlert({ type: "danger", msg: "Something went wrong." });
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
console.log("err while generating webhook", error);
} finally {
setIsLoader(false);
@@ -87,7 +89,7 @@ function Webhook() {
};
const handleModal = () => {
if (!isSubscribe && isEnableSubscription) {
if (!isSubscribe?.isValid && isEnableSubscription) {
setIsTour(true);
} else {
setIsModal(!isModal);
@@ -105,7 +107,7 @@ function Webhook() {
<>
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
OpenSign Webhook{" "}
OpenSign {t("webhook")}{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
isSubscribe={true}
@@ -114,7 +116,7 @@ function Webhook() {
<ul className={"w-full flex flex-col p-2 text-sm"}>
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5">
<span className="">Webhook:</span>{" "}
<span className="">{t("webhook")}:</span>{" "}
<span id="token" className=" md:text-end cursor-pointer">
{webhook ? webhook : "_____"}
</span>
@@ -124,7 +126,7 @@ function Webhook() {
onClick={handleModal}
className="op-btn op-btn-primary"
>
{webhook ? "Update Webhook" : "Add Webhook"}
{webhook ? t("update-webhook") : t("add-webhook")}
</button>
</li>
</ul>
@@ -138,18 +140,18 @@ function Webhook() {
}
className="op-btn op-btn-secondary mt-2 mb-3 px-7"
>
View Docs
{t("view-docs")}
</button>
</div>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
title={t("add-webhook")}
handleClose={handleModal}
>
{error && <Alert type="danger">{error}</Alert>}
<div className="m-[20px]">
<div className="text-lg font-normal text-base-content">
<label className="text-sm ml-2">Webhook</label>
<label className="text-sm ml-2">{t("webhook")}</label>
<input
value={webhook}
onChange={(e) => setWebhook(e.target.value)}
@@ -163,19 +165,19 @@ function Webhook() {
onClick={handleSubmit}
className="op-btn op-btn-primary ml-[2px]"
>
Yes
{t("yes")}
</button>
<button
onClick={handleModal}
className="op-btn op-btn-secondary"
>
No
{t("no")}
</button>
</div>
</div>
</ModalUi>
</div>
{!isSubscribe && isEnableSubscription && (
{isEnableSubscription && !isSubscribe?.isValid && (
<div data-tut="webhooksubscribe">
<SubscribeCard />
</div>
+128 -104
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";
const AddContact = (props) => {
const { t } = useTranslation();
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
@@ -44,111 +46,127 @@ const AddContact = (props) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
try {
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")
});
}
if (localStorage.getItem("TenantId")) {
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("password", email);
if (phone) {
_user.set("phone", phone);
}
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");
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();
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();
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
}
} else {
alert(t("add-signer-alert"));
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();
}
setIsLoader(false);
// Reset the form fields
setAddYourself(false);
setName("");
setPhone("");
setEmail("");
}
// console.log("err", err);
setIsLoader(false);
alert(t("something-went-wrong-mssg"));
}
} catch (err) {
// console.log("err", err);
} else {
setIsLoader(false);
alert("something went wrong!");
alert(t("something-went-wrong-mssg"));
}
};
@@ -175,7 +193,7 @@ const AddContact = (props) => {
</div>
)}
<div className="w-full mx-auto p-[8px]">
<div className="text-[14px] font-[700]">Add Contact</div>
<div className="text-[14px] font-[700]">{t("add-contact")}</div>
{isUserExist && (
<div className="mb-[0.75rem] flex items-center mt-1">
<input
@@ -189,14 +207,14 @@ const AddContact = (props) => {
htmlFor="addYourself"
className="ml-[0.5rem] text-base-content mb-0"
>
Add Yourself
{t("add-yourself")}
</label>
</div>
)}
<form className="text-base-content" onSubmit={handleSubmit}>
<div className="mb-[0.75rem]">
<label htmlFor="name" className="text-[13px]">
Name
{t("name")}
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
@@ -204,6 +222,8 @@ const AddContact = (props) => {
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
@@ -211,14 +231,18 @@ const AddContact = (props) => {
</div>
<div className="mb-[0.75rem]">
<label htmlFor="email" className="text-[13px]">
Email
{t("email")}
<span className="text-[13px] text-[red]"> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
onChange={(e) =>
setEmail(e.target.value?.toLowerCase()?.replace(/\s/g, ""))
}
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
onInput={(e) => e.target.setCustomValidity("")}
required
disabled={addYourself}
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
@@ -226,7 +250,7 @@ const AddContact = (props) => {
</div>
<div className="mb-[0.75rem]">
<label htmlFor="phone" className="text-[13px]">
Phone
{t("phone")}
</label>
<input
type="text"
@@ -240,14 +264,14 @@ const AddContact = (props) => {
<div className="mt-6 flex justify-start gap-2">
<button type="submit" className="op-btn op-btn-primary">
Submit
{t("submit")}
</button>
<button
type="button"
onClick={() => handleReset()}
className="op-btn op-btn-secondary"
>
Reset
{t("reset")}
</button>
</div>
</form>
+9 -3
View File
@@ -1,6 +1,6 @@
import React from "react";
const Alert = ({ children, type }) => {
const Alert = ({ children, type, className }) => {
const textcolor = type ? theme(type) : theme();
function theme(color) {
switch (color) {
@@ -18,9 +18,15 @@ const Alert = ({ children, type }) => {
}
return (
children && (
<div className="z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm">
<div
className={`${
className
? className
: "z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm"
} `}
>
<div className={`op-alert ${textcolor}`}>
<span>{children}</span>
<span className="ml-3">{children}</span>
</div>
</div>
)
@@ -1,8 +1,10 @@
import React, { useState, useEffect } from "react";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import Loader from "./Loader";
import { useTranslation } from "react-i18next";
const LottieWithLoader = () => {
const { t } = useTranslation();
const [isLoaded, setIsLoaded] = useState(false);
const [hasError, setHasError] = useState(false);
const [animationSrc, setAnimationSrc] = useState(null);
@@ -34,7 +36,7 @@ const LottieWithLoader = () => {
<Loader />
</div>
)}
{hasError && <div className="error">Failed to load animation</div>}
{hasError && <div className="error">{t("faild-animation")}</div>}
{isLoaded && animationSrc && (
<DotLottieReact
src={animationSrc}
File diff suppressed because it is too large Load Diff

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