mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-03 08:18:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
716023b534 | ||
|
|
67b3417ac9 | ||
|
|
f3667f6aaf | ||
|
|
7b35e71f3d | ||
|
|
51031878eb | ||
|
|
19d96230cd | ||
|
|
dbee7e2894 | ||
|
|
656241da54 | ||
|
|
2a3947ea1d | ||
|
|
9c1b4261dc | ||
|
|
7c37d8128e | ||
|
|
404785bf29 | ||
|
|
dc7fd34b07 | ||
|
|
86a2975b3c | ||
|
|
f824fc7c81 | ||
|
|
244cc30eb6 | ||
|
|
a833ed8060 | ||
|
|
1e869d2d2b | ||
|
|
64df5cfff3 | ||
|
|
d8b2a80e90 | ||
|
|
10061c061e | ||
|
|
6a4124fdf0 | ||
|
|
faaa915163 | ||
|
|
3fb911298d | ||
|
|
761f81d27c | ||
|
|
d4b7c8ae97 | ||
|
|
23679e58e1 | ||
|
|
f3e39f18ef | ||
|
|
55a4cda351 | ||
|
|
f0f270b44d | ||
|
|
529de2bd90 | ||
|
|
51dd83df09 | ||
|
|
9abe4ffd8e | ||
|
|
cdd92f6990 | ||
|
|
b08fa9bf60 | ||
|
|
f0bcc3c3e7 | ||
|
|
b9c45f24f5 | ||
|
|
d5f3967558 | ||
|
|
d3fccc9b87 | ||
|
|
7e6c03faf5 | ||
|
|
02b2a52280 | ||
|
|
6eb708b155 | ||
|
|
83e6444ddc | ||
|
|
168fe4d680 | ||
|
|
0a10c9a7c9 | ||
|
|
204c530712 | ||
|
|
5e55f0debf | ||
|
|
f8bd5a6c86 | ||
|
|
101e7a9a49 | ||
|
|
271e79d6a2 | ||
|
|
2ca8f8aff8 | ||
|
|
bc6891e746 | ||
|
|
26aaeccf11 | ||
|
|
91b512b693 | ||
|
|
88d7917a84 | ||
|
|
7ae0964dd1 | ||
|
|
edb0c305dd | ||
|
|
0321f35869 | ||
|
|
1870f8e90e | ||
|
|
6e112b904f | ||
|
|
dca8d2533e | ||
|
|
c57957b56d | ||
|
|
1f5ab87653 | ||
|
|
d5513e90bd | ||
|
|
00fcd17285 | ||
|
|
356ab4e7cb | ||
|
|
572cb55bf8 | ||
|
|
08b83469e3 | ||
|
|
6939150d2b | ||
|
|
cf9a4d8a26 | ||
|
|
a5f8e4fc58 | ||
|
|
775e325503 | ||
|
|
0c472ba22c | ||
|
|
4c0d15d6ef | ||
|
|
28cea81f61 | ||
|
|
d975dae88f | ||
|
|
30297584d5 | ||
|
|
1105f8e608 | ||
|
|
eeae097a54 | ||
|
|
8199e5a253 | ||
|
|
08c7f4b491 | ||
|
|
634744c22d | ||
|
|
45f4bdd1c5 | ||
|
|
c8f5c80d5b | ||
|
|
5556fafa94 | ||
|
|
20c21cb964 | ||
|
|
d2de889995 | ||
|
|
05cf3ef7fe | ||
|
|
6bd8a8fea8 | ||
|
|
4cba1a2fd2 | ||
|
|
86f0622c4a | ||
|
|
85ad7ca187 | ||
|
|
879279a382 | ||
|
|
74df84e449 | ||
|
|
2862ae3302 | ||
|
|
7d0dfe480c | ||
|
|
c47748ed4d | ||
|
|
396504138c | ||
|
|
6e067a160c | ||
|
|
66a56757d1 | ||
|
|
088ec5580a | ||
|
|
63b2693fe0 | ||
|
|
76a69d8f3b | ||
|
|
5cd0f55cb2 | ||
|
|
db640b04a6 | ||
|
|
0665bcf30a | ||
|
|
7e8ed42f46 | ||
|
|
8d7b1a5b9b | ||
|
|
c6829a7d5e | ||
|
|
bef8fbf183 | ||
|
|
fca95f09cc | ||
|
|
4d6cdf8823 | ||
|
|
e34f52dcc5 | ||
|
|
91cc4e6f3d | ||
|
|
429b7dcb0e | ||
|
|
5ffa681a80 | ||
|
|
91364db608 | ||
|
|
2433c68f27 | ||
|
|
6ea254d9ac | ||
|
|
a4d1df8324 | ||
|
|
ba15611903 | ||
|
|
eda6113792 | ||
|
|
4f1509c836 | ||
|
|
dfcf32ef3e | ||
|
|
4a95257ee2 | ||
|
|
ee1e13ef61 | ||
|
|
c9c099ffc5 | ||
|
|
a80f4fb3c9 | ||
|
|
941f3ac47f | ||
|
|
80d3200d57 | ||
|
|
55926e25d2 | ||
|
|
a31f3ebad7 | ||
|
|
dc9c32c6ee | ||
|
|
dac3aac2a1 | ||
|
|
40196e823c | ||
|
|
7708b7e938 | ||
|
|
8bbc5a9bc6 | ||
|
|
4dbdfb1b77 | ||
|
|
05c51edce3 | ||
|
|
379e8cd95e | ||
|
|
9fd00d4427 | ||
|
|
ca114cf124 | ||
|
|
75128784db | ||
|
|
722f0e277e | ||
|
|
7089998966 | ||
|
|
52211ddfa4 | ||
|
|
2b8b762d35 | ||
|
|
04c228cd36 | ||
|
|
915f8e7700 | ||
|
|
218cea00b5 | ||
|
|
f38f5ea670 | ||
|
|
a9c08c8a9f | ||
|
|
9406c4f718 | ||
|
|
791d8beb06 | ||
|
|
57a9f3aca0 | ||
|
|
ae8bf187c8 | ||
|
|
88f2ab1f8e | ||
|
|
76f5136073 | ||
|
|
0f84cd8949 | ||
|
|
adfdf7606f | ||
|
|
ce671d4294 | ||
|
|
2e7cf7a9df | ||
|
|
f699d93e44 | ||
|
|
531ef4d0b2 | ||
|
|
4c6dbb6690 | ||
|
|
0beb60d1b5 | ||
|
|
893c9d7c42 | ||
|
|
b7a22d94db | ||
|
|
3d854756d1 | ||
|
|
251e7058f1 | ||
|
|
368eb24217 | ||
|
|
f89ef54b82 | ||
|
|
b9d9f8772b | ||
|
|
f2ee03e850 | ||
|
|
92e59a0072 | ||
|
|
5394c0b520 | ||
|
|
e4a9c6f8c2 | ||
|
|
e559d037c3 | ||
|
|
c9909a0869 | ||
|
|
988fc1bbfd | ||
|
|
1831bbcd70 | ||
|
|
ced9bbdbd5 | ||
|
|
15d57d937b | ||
|
|
555979869d | ||
|
|
4e6477297e | ||
|
|
e9e06fc53b | ||
|
|
ecb2818589 | ||
|
|
c442689573 | ||
|
|
88919f2634 | ||
|
|
1a1bc41283 | ||
|
|
4188caed33 | ||
|
|
50557d8bc5 | ||
|
|
0f1ef0406e | ||
|
|
ffc742b460 | ||
|
|
0c04cc6ce6 | ||
|
|
4e6dfed838 | ||
|
|
fdc0524ad2 | ||
|
|
096d5962a3 | ||
|
|
9ea36f94a7 | ||
|
|
2b66df992f | ||
|
|
ff3c31975e | ||
|
|
beaa5eca69 | ||
|
|
b98040faac | ||
|
|
fbbff0df61 | ||
|
|
07d5f393c0 | ||
|
|
8220b15a29 | ||
|
|
7cf638b2be | ||
|
|
9852c87be7 | ||
|
|
30fb8f31e3 | ||
|
|
ec4d875547 | ||
|
|
1268f65516 | ||
|
|
6551b2537a | ||
|
|
43eee63df2 | ||
|
|
4caf9f598a | ||
|
|
107eaca8f0 | ||
|
|
cc11848346 | ||
|
|
b138ccf625 | ||
|
|
27ff7baa6d | ||
|
|
c8ecd34bd3 | ||
|
|
d4a4eb3248 | ||
|
|
f782733397 | ||
|
|
7720eddc47 | ||
|
|
71170a373a | ||
|
|
8f080080a7 | ||
|
|
71b2958c14 | ||
|
|
f69aafcbfa | ||
|
|
5f6a423697 | ||
|
|
5d245927c4 | ||
|
|
17c96fb849 | ||
|
|
b7c28cdf36 | ||
|
|
d05aa0d350 | ||
|
|
875dc086bf | ||
|
|
4719ebd52a | ||
|
|
c8a436a7df | ||
|
|
8d6f4dd64e | ||
|
|
63486927d6 | ||
|
|
c36a70f8d3 | ||
|
|
6cb30a6597 | ||
|
|
bc89b33468 | ||
|
|
3fc41f4255 | ||
|
|
ff2f7f6586 | ||
|
|
93f33d73d9 | ||
|
|
402c176277 | ||
|
|
b20aa33a04 | ||
|
|
a64da4cd85 | ||
|
|
e80bcfc586 | ||
|
|
de5b8160a2 | ||
|
|
83df969751 | ||
|
|
f04fd16c9e |
@@ -232,6 +232,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "1024mb",
|
||||
"name": "1024mb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9301204?v=4",
|
||||
"profile": "https://github.com/1024mb",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -147,7 +147,8 @@ We would like to thank all our contributors and users for their support and feed
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a><a href="#security-Zathiel" title="Security">🛡️</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1024mb"><img src="https://avatars.githubusercontent.com/u/9301204?v=4?s=100" width="100px;" alt="1024mb"/><br /><sub><b>1024mb</b></sub></a><br /><a href="#translation-1024mb" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Generated
+1862
-1346
File diff suppressed because it is too large
Load Diff
+33
-28
@@ -4,16 +4,17 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@lottiefiles/dotlottie-react": "^0.8.9",
|
||||
"@lottiefiles/dotlottie-react": "^0.9.0",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.1.3",
|
||||
"@react-pdf/renderer": "^3.4.4",
|
||||
"@radix-ui/themes": "^3.1.4",
|
||||
"@react-pdf/renderer": "^4.0.0",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"axios": "^1.7.5",
|
||||
"axios": "^1.7.7",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^23.14.0",
|
||||
"i18next": "^23.15.2",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.6.1",
|
||||
"i18next-http-backend": "^2.6.2",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"nth-check": "^2.1.1",
|
||||
@@ -22,10 +23,10 @@
|
||||
"print-js": "^1.6.0",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
"react-bootstrap": "^2.10.5",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-datepicker": "^7.3.0",
|
||||
"react-datepicker": "^7.4.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
@@ -33,17 +34,18 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-i18next": "^15.0.2",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-quill-new": "^3.3.0",
|
||||
"react-pdf": "^9.1.1",
|
||||
"react-quill-new": "^3.3.3",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-rnd": "^10.4.11",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-rnd": "^10.4.13",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-select": "^5.8.1",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.4",
|
||||
@@ -59,7 +61,7 @@
|
||||
"build-webpack": "webpack --config webpack.config.js",
|
||||
"build": "npm run version && npm run build-webpack && react-scripts build",
|
||||
"build-watch": "webpack --config webpack.config.js --watch",
|
||||
"start-dev": "concurrently \"npm run build-watch\" \"react-scripts start\"",
|
||||
"start-dev": "react-scripts start",
|
||||
"start": "serve -s build",
|
||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||
@@ -93,33 +95,36 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.4",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/runtime-corejs2": "^7.25.4",
|
||||
"@babel/core": "^7.25.7",
|
||||
"@babel/preset-env": "^7.25.7",
|
||||
"@babel/preset-react": "^7.25.7",
|
||||
"@babel/runtime-corejs2": "^7.25.7",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-loader": "^9.1.3",
|
||||
"babel-loader": "^9.2.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.0.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"daisyui": "^4.12.12",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"lint-staged": "^15.2.8",
|
||||
"eslint-plugin-react": "^7.37.1",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"postcss": "^8.4.45",
|
||||
"postcss": "^8.4.47",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"overrides": {
|
||||
"nth-check": "$nth-check",
|
||||
"ws": "$ws"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,9 @@
|
||||
"Share": "Share",
|
||||
"View": "View",
|
||||
"option": "Option",
|
||||
"Embed": "Embed"
|
||||
"Embed": "Embed",
|
||||
"Copy TemplateId":"Copy TemplateId",
|
||||
"Copy Public URL":"Copy Public URL"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Sr.No",
|
||||
@@ -151,7 +153,8 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Created Date",
|
||||
"Type": "Type"
|
||||
"Type": "Type",
|
||||
"Logs": "Logs"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -262,9 +265,10 @@
|
||||
"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": " Here’s 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.",
|
||||
"public-url": "Public profile needed",
|
||||
"public-url-copy": "Here’s your public URL: ",
|
||||
"public-url-copy-mssg":"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 complete your OpenSign public profile setup. Once done, you’ll be able to make templates public. This process only needs to be completed once.",
|
||||
"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",
|
||||
@@ -572,9 +576,10 @@
|
||||
"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.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "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-6": "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.",
|
||||
@@ -630,12 +635,53 @@
|
||||
"reason":"Reason",
|
||||
"decline-by":"Declined/revoked by",
|
||||
"document-declined": "Document declined",
|
||||
"public-template-mssg-1":"To integrate OpenSign into your React or Next.js project, simply run the following command:",
|
||||
"public-template-mssg-2" :"Ensure you have npm or yarn set up in your project. If you’re using Yarn, you can replace npm install with yarn add @opensign/react.",
|
||||
"public-template-mssg-3" :"Need more details or examples?",
|
||||
"public-template-mssg-4": "Visit the",
|
||||
"public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.",
|
||||
"public-template-mssg-6" :"Before you can use this code snippet, you must make this template public.",
|
||||
"copy-code":"COPY",
|
||||
"copied-code":"COPIED",
|
||||
"Installation":"Installation",
|
||||
"Usage" :"Usage",
|
||||
"insufficient-credits":"Insufficient Signing Credits",
|
||||
"insufficient-credits-mssg":"The owner of this document currently lacks the necessary OpenSign credits for you to sign. Please reach out to the owner if you require further details.",
|
||||
"angular-npm-mssg-1": "To integrate OpenSign into your Angular project, simply run the following command:",
|
||||
"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"
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month":"Sent this month",
|
||||
"available-seats":"Available seats",
|
||||
"buy-users":"Buy more users",
|
||||
"isenable-otp": "Enable OTP verification",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Would you like to enable the verification process using a one-time password (OTP)?",
|
||||
"p2": "Selecting this option will enable OTP verification. Users will receive a verification code via email, which they must enter to sign the document.",
|
||||
"p3": "Selecting this option will disable OTP verification, allowing users to sign the document directly without additional steps.",
|
||||
"p4": "Please choose the option that best suits your document signing requirements."
|
||||
},
|
||||
"advanced-options":"Advanced options",
|
||||
"hide-advanced-options":"Hide Advanced options",
|
||||
"download-files":"Download files",
|
||||
"download-pdf":"Download Pdf",
|
||||
"pdf-certificate":"Download Pdf + Certificate",
|
||||
"document-logs":"Document logs",
|
||||
"server-down": "Unable to connect to the OpenSign server. If you are self-hosting OpenSign, please ensure that all the steps in the documentation have been followed correctly. If you're running OpenSign locally, you might be accessing it through an incorrect port number.",
|
||||
"admin-exists": "Admin already exists. Please login to the application using admin credentials in order to manage users.",
|
||||
"enable-tour":"Enable Tour",
|
||||
"istourenabled-help":{
|
||||
"p1":"This will enable a guided tour for signers, providing instructions during the signing process.",
|
||||
"p2":"This will disable the guided tour, ensuring a faster, uninterrupted signing experience.",
|
||||
"p3":"Tip: If your signers are familiar with OpenSign and frequently use it, you may want to disable the guided tour for a more seamless experience. Choose the option that best fits your document signing needs."
|
||||
},
|
||||
"form-title-1":"Document Flow Setting",
|
||||
"form-title-2":"Security Setting",
|
||||
"public-tour-message":"The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it.",
|
||||
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>"
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,687 @@
|
||||
{
|
||||
"header-news": "Nueva funcionalidad: ¡Los perfiles públicos ahora están disponibles! Crea y comparte plantillas públicas fácilmente",
|
||||
"header-news-btn": "Configurar ahora",
|
||||
"create-account": "Crear cuenta",
|
||||
"login": "Iniciar sesión",
|
||||
"language": "Idioma",
|
||||
"name": "Nombre",
|
||||
"phone": "Teléfono",
|
||||
"phone-optional": "opcional",
|
||||
"email": "Correo",
|
||||
"company": "Compañía",
|
||||
"job-title": "Título del puesto",
|
||||
"profile": "Perfil",
|
||||
"log-out": "Cerrar sesión",
|
||||
"is-email-verified": "El correo está verificado",
|
||||
"public-profile": "Perfil público",
|
||||
"tagline": "Eslogan ",
|
||||
"has-requested-you-to-sign": "ha solicitado que firmes",
|
||||
"has-requested-you-to-review-and-sign": " ha solicitado que revises y firmes",
|
||||
"input-required": "Por favor, rellena este campo.",
|
||||
"sort-order": {
|
||||
"Ascending": "Ascendente",
|
||||
"Descending": "Descendente",
|
||||
"Name": "Nombre",
|
||||
"Date": "Fecha"
|
||||
},
|
||||
"context-menu": {
|
||||
"Download": "Descargar",
|
||||
"Rename": "Renombrar",
|
||||
"Move": "Mover",
|
||||
"Delete": "Eliminar"
|
||||
},
|
||||
"action": "Acción",
|
||||
"add": "Agregar",
|
||||
"add-new-folder": "Agregar nueva carpeta",
|
||||
"disable-documentId": "Desactivar ID de documento",
|
||||
"edit": "Editar",
|
||||
"change-password": "Cambiar contraseña",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"upgrade-now": "Mejorar ahora",
|
||||
"upgrade-to": "Mejorar a",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
||||
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
||||
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
||||
"tour-content": "No mostrar esto otra vez",
|
||||
"pro": "PRO",
|
||||
"team": "EQUIPO",
|
||||
"user": "Usuario",
|
||||
"docs": "Documentación",
|
||||
"session-expired": "Tu sesión ha expirado.",
|
||||
"public-profile-help": "Esta es tu URL pública. Cópiala o compártela con el firmante, y podrás ver todas tus plantillas públicas.",
|
||||
"tagline-help": "Este eslogan aparecerá en tu perfil público de OpenSign, por ejem. https://opensign.me/alex",
|
||||
"welcome": "¡Bienvenido de nuevo!",
|
||||
"Login-to-your-account": "Ingresa a tu cuenta",
|
||||
"password": "Contraseña",
|
||||
"forgot-password": "¿Olvidaste la contraseña?",
|
||||
"loading": "Cargando...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Ingresar con SSO",
|
||||
"login-page": "Página de inicio de sesión",
|
||||
"or": "O",
|
||||
"signup-page": "Página de registro",
|
||||
"password-length": "La contraseña debe tener 8 caracteres",
|
||||
"password-case": "La contraseña debe incluir mayúsculas, minúsculas y números",
|
||||
"password-special-char": " La contraseña debe incluir caracteres especiales",
|
||||
"agreee": "Estoy de acuerdo con los",
|
||||
"term": "Términos de Servicio",
|
||||
"subscribe-to-opensign": "Suscribirse al boletín de OpenSign",
|
||||
"register": "Registrarse",
|
||||
"sidebar": {
|
||||
"Dashboard": "Panel",
|
||||
"Sign yourself": "Firma tu mismo",
|
||||
"Request signatures": "Solicitar firmas",
|
||||
"Templates": "Plantillas",
|
||||
"Templates-Children": {
|
||||
"Create template": "Crear plantilla",
|
||||
"Manage templates": "Administrar plantillas"
|
||||
},
|
||||
"OpenSign™ Drive": "OpenSign™ Drive",
|
||||
"Reports": "Reportes",
|
||||
"Reports-Children": {
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In Progress": "En progreso",
|
||||
"Completed": "Completado",
|
||||
"Drafts": "Borradores",
|
||||
"Declined": "Rechazados",
|
||||
"Expired": "Expirados",
|
||||
"Contactbook": "Agenda de contactos"
|
||||
},
|
||||
"Settings": "Ajustes",
|
||||
"Settings-Children": {
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
}
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necesitan tu firma",
|
||||
"Out for signatures": "En espera de ser firmados"
|
||||
},
|
||||
"report-name": {
|
||||
"Draft Documents": "Documentos borradores",
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In-progress documents": "Documentos en progreso",
|
||||
"Completed Documents": "Documentos completados",
|
||||
"Declined Documents": "Documentos rechazados",
|
||||
"Expired Documents": "Documentos expirados",
|
||||
"Recently sent for signatures": "Recientemente enviados para ser firmados",
|
||||
"Recent signature requests": "Solicitudes de firma recientes",
|
||||
"Drafts": "Borradores",
|
||||
"Contactbook": "Agenda de contactos",
|
||||
"Templates": "Plantillas",
|
||||
"Teams": "Equipos",
|
||||
"Users": "Usuarios"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "firmar",
|
||||
"Sign": "FIRMAR",
|
||||
"Resend": "Reenviar",
|
||||
"Revoke": "Revocar",
|
||||
"Delete": "Eliminar",
|
||||
"Use": "Usar",
|
||||
"Quick send": "Envío rápido",
|
||||
"Edit": "Editar",
|
||||
"Share with team": "Compartir con el equipo",
|
||||
"Share": "Compartir",
|
||||
"View": "Ver",
|
||||
"option": "Opción",
|
||||
"Embed": "Incrustar",
|
||||
"Copy TemplateId": "Copiar ID de plantilla",
|
||||
"Copy Public URL": "Copiar URL pública"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
"Title": "Título",
|
||||
"Email": "Correo",
|
||||
"Phone": "Teléfono",
|
||||
"File": "Archivo",
|
||||
"Owner": "Propietario",
|
||||
"Signers": "Firmantes",
|
||||
"Note": "Nota",
|
||||
"Folder": "Carpeta",
|
||||
"Reason": "Razón",
|
||||
"Parent Team": "Equipo padre",
|
||||
"Active": "Activo",
|
||||
"Role": "Rol",
|
||||
"Team": "Equipo",
|
||||
"Name": "Nombre",
|
||||
"Status": "Estado",
|
||||
"created-date": "Fecha de creación",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Registros"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
|
||||
"Need your sign": "Esta es una lista de documentos que esperan tu firma.",
|
||||
"In-progress documents": "Esta es una lista de documentos que has enviado a otras partes para su firma.",
|
||||
"Completed Documents": "Esta es una lista de documentos en los que todas las partes han firmado.",
|
||||
"Declined Documents": "Esta es una lista de documentos que han sido rechazados por uno de los firmantes.",
|
||||
"Expired Documents": "Esta es una lista de documentos que han alcanzado su fecha de caducidad.",
|
||||
"Contactbook": "Esta es una lista de contactos/firmantes añadidos por ti. Aparecerán como sugerencias cuando intentes añadir firmantes a un nuevo documento.",
|
||||
"Templates": "Esta es una lista de plantillas que están a tu disposición para crear documentos. Puedes hacer clic en el botón «usar» para crear un nuevo documento utilizando una plantilla, modifica el documento y añade firmantes en el siguiente paso."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla"
|
||||
},
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "archivo seleccionado",
|
||||
"template-title": "Título de la plantilla",
|
||||
"document-title": "Título del documento",
|
||||
"description": "Descripción",
|
||||
"time-to-complete": "Plazo de finalización (días)",
|
||||
"send-in-order": "Enviar en orden",
|
||||
"send-in-order-help": {
|
||||
"p1": "Elige cómo quieres que se envíen las solicitudes de firma a los firmantes del documento:",
|
||||
"p2": "Seleccionando esta opción se enviará inicialmente la solicitud de firma al primer firmante. Una vez que el primer firmante complete su parte, el siguiente firmante en la secuencia recibirá la solicitud. Este proceso continúa hasta que todos los firmantes hayan firmado el documento. Este método garantiza que el documento se firme en un orden específico.",
|
||||
"p3": "Seleccionando esta opción se enviarán los enlaces para la firma a todos los firmantes simultáneamente. Cada firmante puede firmar el documento cuando le convenga, independientemente de si otros firmantes han completado sus firmas. Este método es más rápido, pero no impone ningún orden de firma entre los participantes.",
|
||||
"p4": "Selecciona la opción que mejor se adapte a las necesidades de tu tramitación de documentos."
|
||||
},
|
||||
"no": "No",
|
||||
"auto-reminder": "Recordatorio automático",
|
||||
"remind-once": " Recordar una vez cada (Días)",
|
||||
"next": "Siguiente",
|
||||
"select-folder": "Seleccionar carpeta",
|
||||
"OpenSign-drive": "OpenSign™ Drive",
|
||||
"drive-document-status": {
|
||||
"Completed": "Completado",
|
||||
"Declined": "Rechazado",
|
||||
"Draft": "Borrador",
|
||||
"Expired": "Expirado",
|
||||
"In Progress": "En progreso"
|
||||
},
|
||||
"select-folder-help": "Si no seleccionas una carpeta, tu documento firmado se guardará en la carpeta principal de OpenSign Drive.",
|
||||
"no-data": "No se encontraron datos",
|
||||
"save-here": "Guardar aquí",
|
||||
"back": "Atrás",
|
||||
"add-folder": "Añadir carpeta",
|
||||
"create-folder": "Crear carpeta",
|
||||
"parent-folder": "Carpeta superior",
|
||||
"create": "Crear",
|
||||
"signers": "Firmantes",
|
||||
"signers-help": "Empieza a escribir el nombre de un contacto para ver los firmantes sugeridos entre tus contactos guardados o añade otros nuevos. Organiza el orden de firma añadiendo firmantes en la secuencia deseada. Utiliza el botón «+» para añadir nuevos firmantes y la «X» para quitar todos. Cada firmante recibirá una solicitud por correo electrónico para que firme el documento según el orden indicado.",
|
||||
"add-signer": "Añadir firmante",
|
||||
"contact-not-found": "Contacto no encontrado",
|
||||
"add-yourself": "Añadirte a ti mismo",
|
||||
"submit": "Enviar",
|
||||
"reset": "Volver a empezar",
|
||||
"my-signature": "Mi firma",
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen",
|
||||
"initials": "Iniciales",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
"regenerate-token": "Regenerar token activo",
|
||||
"generate-token": "Generar token activo",
|
||||
"view-docs": "Ver documentación",
|
||||
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
||||
"yes": "Sí",
|
||||
"copied": "Copiado",
|
||||
"something-went-wrong-mssg": "Algo salió mal, por favor, intenta de nuevo más tarde.",
|
||||
"token-generated": "Token generado exitosamente.",
|
||||
"webhook": "Webhook",
|
||||
"update-webhook": "Actualizar webhook",
|
||||
"add-webhook": "Agregar webhook",
|
||||
"webhook-added": "Webhook agregado exitosamente.",
|
||||
"team-disabled": "Equipo deshabilitado",
|
||||
"Team enabled": "Equipo habilitado",
|
||||
"are-you-sure": "¿En definitiva quieres ",
|
||||
"disable": "deshabilitar",
|
||||
"enable": "habilitar",
|
||||
"this-team": " este equipo?",
|
||||
"edit-team": "Editar equipo",
|
||||
"name-of-team": "Nombre del equipo",
|
||||
"prev": "Anterior",
|
||||
"no-data-avaliable": "No hay datos disponibles",
|
||||
"add-team": "Agregar equipo",
|
||||
"page-not-found": "Página no encontrada",
|
||||
"users-from-teams": "usuarios de equipos",
|
||||
"user-status": "Estado del usuario",
|
||||
"activate": "activar",
|
||||
"deactivate": "desactivar",
|
||||
"this-user": " este usuario",
|
||||
"delete-user": "Eliminar usuario",
|
||||
"delete": "eliminar",
|
||||
"add-user": "Agregar usuario",
|
||||
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
|
||||
"Team status": "Estado del equipo",
|
||||
"user-deactivated": "Usuario desactivado.",
|
||||
"user-activated": "Usuario activado.",
|
||||
"public": "Público",
|
||||
"download": "Descargar",
|
||||
"view": "Ver",
|
||||
"make-template-public": "Convertir la plantilla en pública",
|
||||
"make-template-private": "Convertir la plantilla en privada",
|
||||
"make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?",
|
||||
"make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.",
|
||||
"public-role": "Rol público",
|
||||
"public-url": "Se requiere perfil público",
|
||||
"public-url-copy": "Aquí está tu URL pública: ",
|
||||
"public-url-copy-mssg": "Cópiala o compártela con el firmante y podrás ver todas tus plantillas públicas.",
|
||||
"add-public-url-alert": "Por favor, completa la configuración de tu perfil público OpenSign. Una vez terminado, podrás crear plantillas públicas. Este proceso solo necesita ser completado una vez.",
|
||||
"share-with-alert": "No puedes compartir una plantilla si alguno de los roles ya tiene contactos asignados. Elimina todas las asignaciones de contactos de los roles antes de compartir la plantilla.",
|
||||
"share-with": "Compartir con",
|
||||
"team-not-found": "Equipo no encontrado",
|
||||
"roles": "Roles",
|
||||
"delete-document": "Eliminar documento",
|
||||
"delete-document-alert": "¿En definitiva quieres eliminar este documento?",
|
||||
"quick-send": "Envío rápido",
|
||||
"add-new": "Agregar nuevo",
|
||||
"send": "Enviar",
|
||||
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
||||
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
||||
"quick-send-alert-3": "Por favor, añade al menos un rol a esta plantilla para poder hacer un «envío rápido» de copias a varios firmantes.",
|
||||
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
||||
"copy-link": "Copiar enlace",
|
||||
"copy": "Copiar",
|
||||
"revoke-document": "Revocar documento",
|
||||
"revoke-document-alert": "¿En definitiva quieres revocar este documento?",
|
||||
"resend-mail": "Reenviar correo",
|
||||
"resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe!",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
"document-sent-alert": "¡Documento enviado exitosamente!",
|
||||
"template-share-alert": "Plantilla compartida exitosamente.",
|
||||
"template-public-alert-1": "Has convertido exitosamente la plantilla en pública.",
|
||||
"template-public-alert-2": "Has convertido exitosamente la plantilla en privada.",
|
||||
"template-public-alert-3": "Tienes que seleccionar un rol para los firmantes públicos.",
|
||||
"template-public-alert-4": "Por favor, asegúrate de que has añadido al menos un widget de firma para cada firmante.",
|
||||
"template-public-alert-5": "Dado que esta plantilla tiene «enviar en orden» habilitado, el rol público debe estar posicionado arriba al inicio.",
|
||||
"template-public-alert-6": "Por favor, enlaza firmantes a todos los roles excepto al usuario de rol público.",
|
||||
"template-public-alert-7": "Por favor, asigna al menos un rol público para convertir está plantilla en pública.",
|
||||
"loader": " Esto podría llevar algún tiempo",
|
||||
"pages": "Páginas",
|
||||
"document-signed": "Documento firmado",
|
||||
"close": "Cerrar",
|
||||
"validation-alert": "Alerta de validación",
|
||||
"validate-alert-mssg": " Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"otp-verification": "Verificación OTP",
|
||||
"enter-otp": "Ingresar OTP",
|
||||
"verify": "Verificar",
|
||||
"verify-email-1": "Verificar correo",
|
||||
"resend": "Reenviar",
|
||||
"contact-details": "Detalles del contacto",
|
||||
"verify-email": "¡Por favor, verifica tu correo!",
|
||||
"send-otp": " Enviar OTP",
|
||||
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
||||
"loading-doc": "Cargando documento...",
|
||||
"widgets-name": {
|
||||
"signature": "firma",
|
||||
"stamp": "sello",
|
||||
"initials": "iniciales",
|
||||
"name": "nombre",
|
||||
"job title": "título del puesto",
|
||||
"company": "compañía",
|
||||
"date": "fecha",
|
||||
"text": "texto",
|
||||
"text input": "entrada de texto",
|
||||
"checkbox": "casilla",
|
||||
"dropdown": "desplegable",
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
"please-add": "Por favor, agrega un",
|
||||
"signed-by": "Firmado por",
|
||||
"more": "más",
|
||||
"sent": "enviado",
|
||||
"print": "Imprimir",
|
||||
"certificate": "Certificado",
|
||||
"decline": "Rechazar",
|
||||
"finish": "Finalizar",
|
||||
"mail": "Correo",
|
||||
"sign-now": "Firmar ahora",
|
||||
"successfully-signed": "¡Firmado exitosamente!",
|
||||
"email-mssg": " Los destinatarios agregados aquí recibirán una copia del documento firmado.",
|
||||
"email-error-1": " por favor, proporciona una dirección de correo correcta",
|
||||
"email-error-2": "Solo puedes enviar hasta a 10 destinatarios a la vez.",
|
||||
"Email-verified-alert-1": "El correo está verificado.",
|
||||
"Email-verified-alert-2": "El correo ya ha sido verificado.",
|
||||
"upload-stamp-image": "Subir imagen del sello",
|
||||
"draw": "Dibujar",
|
||||
"type": "Escribir",
|
||||
"color-type": {
|
||||
"red": "rojo",
|
||||
"blue": "azul",
|
||||
"black": "negro",
|
||||
"yellow": "amarillo"
|
||||
},
|
||||
"my-initials": "Mis iniciales",
|
||||
"upload": "Subir",
|
||||
"initial-teb": "Iniciales",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "Tu firma",
|
||||
"copy-title": "Copiar widget a",
|
||||
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?",
|
||||
"reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
|
||||
"reset-password-alert-2": "Por favor, configura un adaptador de correo ",
|
||||
"reset-password-alert-3": "Restablece tu contraseña",
|
||||
"faild-animation": "Error al cargar la animación",
|
||||
"apply": "Aplicar",
|
||||
"copy-type": {
|
||||
"All pages": "Todas las páginas",
|
||||
"All pages but last": "Todas las páginas menos la última",
|
||||
"All pages but first": "Todas las páginas menos la primera",
|
||||
"Next to current widget": "Junto al widget actual"
|
||||
},
|
||||
"options": "Opciones",
|
||||
"minimun-check": "Chequeo mínimo",
|
||||
"maximum-check": "Chequeo máximo",
|
||||
"default-value": "Valor por defecto",
|
||||
"select": "Seleccionar",
|
||||
"read-only": "Es de solo lectura",
|
||||
"hide-labels": "Esconder etiquetas",
|
||||
"checkbox": "Casilla",
|
||||
"alert": "Alerta",
|
||||
"zoom-in": "Acercar",
|
||||
"zoom-out": "Alejar",
|
||||
"document-signed-alert": "¡Felicitaciones! 🎉 ¡Este documento ha sido firmado por ti exitosamente!",
|
||||
"otp-sent-alert": "OTP enviado a tu correo",
|
||||
"fields-required": "Campos requeridos",
|
||||
"signature-widget-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma agregado",
|
||||
"signature-widget-alert-2": "Por favor, asegúrate de que todos los campos están correctamente rellenados y de que cumplen todos los requisitos.",
|
||||
"encrypted-pdf-alert": "Actualmente los archivos PDF encriptados no están soportados.",
|
||||
"encrypted-pdf-alert-1": "La contraseña del archivo PFX es inválida.",
|
||||
"user-not-exist": "El usuario no existe",
|
||||
"template-signature-field": "Por favor, agrega al menos un campo de firma para todos los roles.",
|
||||
"template-role-alert": "Por favor, primero agrega roles",
|
||||
"create-document": "Crear documento",
|
||||
"template-created-alert": "¿Quieres crear un documento usando la plantilla que acabas de crear?",
|
||||
"signers-alert": "alerta de firmantes",
|
||||
"template-creation-alert-1": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"ok": "OK",
|
||||
"radio-group": "Grupo de radio",
|
||||
"dropdown-options": "Opciones del desplegable",
|
||||
"add-role": "Agregar rol",
|
||||
"role-ex": " por ejem.: Cliente, RRHH, Director, Administrador, Estudiante, etc.",
|
||||
"add/choose-signer": "Agregar/Elegir firmante",
|
||||
"choose-from-contacts": "Elegir desde los contactos",
|
||||
"select-signer": " Por favor, selecciona el firmante",
|
||||
"edit-template": "Editar plantilla",
|
||||
"widget-info": "Información del widget",
|
||||
"validation": "Validación",
|
||||
"invalid-default-value": "valor por defecto inválido",
|
||||
"hint": "Pista",
|
||||
"color": "color",
|
||||
"role-help": {
|
||||
"p1": "¿Qué son los roles de una plantilla?",
|
||||
"p2": "Empieza por especificar cada uno de los roles necesarios para completar el documento. Piensa en las partes implicadas en el proceso de firma y cuáles son sus responsabilidades. Los roles más habituales son RRHH para los documentos internos, Cliente para los contratos o Proveedor para los convenios comerciales.",
|
||||
"p3": "¿Por qué preasignar usuarios a algunos roles?",
|
||||
"p4": "Para los roles que consistentemente involucran al mismo individuo (por ejem., la firma del Gerente en las ofertas de empleo), puedes preasignar un usuario a un rol dentro de una plantilla. Este paso es opcional pero recomendado para la eficiencia y consistencia entre documentos",
|
||||
"p5": "¿Cuando especifico el usuario asignado a cada rol?",
|
||||
"p6": "Cuando crees un documento usando tu plantilla, se te pedirá que asignes usuarios a cada rol definido. Si un rol ya tiene un usuario asignado, este será rellenado con anticipación, pero puedes modificarlo según lo necesites antes de enviar el documento."
|
||||
},
|
||||
"add-recipients": "Agregar destinatarios",
|
||||
"loading-mssg": "Esto podría llevar algún tiempo",
|
||||
"send-mail": "Enviar correo",
|
||||
"placeholder-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma para cada destinatario.",
|
||||
"placeholder-alert-2": "Por favor, confirma que hayas rellenado el campo de texto.",
|
||||
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
|
||||
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
|
||||
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"email-subject": "asunto del correo",
|
||||
"email-body": "cuerpo del correo",
|
||||
"email-placeholder": "agregar cuerpo del correo",
|
||||
"reset-to-default": "Restablecer a por defecto",
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"Mails Sent": "Correos enviados",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
"alert-message": "Mensaje de alerta",
|
||||
"document-decline": "Rechazar documento",
|
||||
"decline-alert-1": "¿En definitiva quieres rechazar este documento?",
|
||||
"decline-alert-2": "¡Has rechazado este documento!",
|
||||
"decline-alert-3": "No puedes firmar este documento ya que ha sido rechazado/revocado.",
|
||||
"sign here": "Firma aquí",
|
||||
"guest-email-alert": "el código de verificación ha sido enviado a tu correo",
|
||||
"get-otp-alert": "recibirás un código de verificación por correo",
|
||||
"user-already-exist": "El usuario ya existe.",
|
||||
"server-error": "¡Error interno del servidor!",
|
||||
"fill-required-details!": "¡Por favor, rellena los detalles requeridos!",
|
||||
"sign-up-form": "Formulario de registro",
|
||||
"sign-up": "Registrarse",
|
||||
"folder-already-exist!": "¡la carpeta ya existe!",
|
||||
"please-select-position!": "¡por favor, selecciona una posición!",
|
||||
"default-sign-alert": "¿En definitiva quieres firmar automáticamente en todos los lugares solicitados?",
|
||||
"Your-Signature": "Tu firma",
|
||||
"auto-sign-all": "Firmar todo automáticamente",
|
||||
"generating-certificate": "Generando certificado",
|
||||
"pdf-download": "Descargar PDF",
|
||||
"generate-certificate-alert": " Tu certificado de finalización se está generando. Por favor, espera un momento. Si la descarga no inicia pronto, presiona el botón otra vez.",
|
||||
"folder-already-exist": "¡La carpeta ya existe!",
|
||||
"folder-created-successfully": "¡Carpeta creada exitosamente!",
|
||||
"fill-folder-name": "Por favor, rellena el nombre de la carpeta",
|
||||
"already-exists-this-username": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"verification-code-sent": "¡El correo de verificación ha sido enviado a tu correo!",
|
||||
"registered-user-successfully": "Usuario registrado exitosamente.",
|
||||
"role-not-found": "Rol no encontrado.",
|
||||
"do-not-access": "No tienes acceso, por favor, contacta al administrador.",
|
||||
"add-admin": "Agregar administrador",
|
||||
"opensign-setup": "Instalación de OpenSign",
|
||||
"join-discord": "Únete a nuestro servidor de Discord",
|
||||
"admin-already-exist": "El administrador ya existe.",
|
||||
"password-update-alert-1": "Contraseña actualizada exitosamente.",
|
||||
"password-update-alert-2": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-3": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-4": "Tu contraseña y su confirmación no son iguales.",
|
||||
"current-password": "Contraseña actual",
|
||||
"new-password": "Contraseña nueva",
|
||||
"confirm-password": "Confirmar contraseña",
|
||||
"file-alert-1": "El tamaño del archivo seleccionado es muy grande. Por favor, selecciona un archivo con un tamaño menor a",
|
||||
"file-alert-2": "Por favor, selecciona un archivo.",
|
||||
"file-alert-3": "Por favor, espera mientras se sube el documento.",
|
||||
"enter-pdf-password": "Ingresa la contraseña del PDF",
|
||||
"correct-password": "Por favor, proporciona la contraseña correcta",
|
||||
"decrypting-pdf": " Desencriptando PDF, por favor, espera...",
|
||||
"invalid-otp": "OTP inválido",
|
||||
"user-not-found": "¡Usuario no encontrado!",
|
||||
"enter-otp-alert": "¡Por favor, ingresa el OTP!",
|
||||
"get-verification-code": "Obtener código de verificación",
|
||||
"get-verification-code-2": "Obtendrás un código de verificación por correo",
|
||||
"enter-verification-code": "Ingresar código de verificación",
|
||||
"provide-your-details": "Proporciona tus detalles",
|
||||
"provide-email": "Por favor, proporciona un correo.",
|
||||
"additional-info": "Información adicional",
|
||||
"signature-saved-alert": "Firma guardada exitosamente.",
|
||||
"fill-field": "Por favor, rellena este campo",
|
||||
"error-template": "Error: ¡Plantilla no encontrada!",
|
||||
"error": "Error",
|
||||
"invalid-document": "Error: ¡Documento inválido!",
|
||||
"document-not-found": "¡Documento no encontrado!",
|
||||
"document-signed-alert-1": "Has firmado el documento exitosamente. Puedes descargar o imprimir una copia del documento parcialmente firmado. Una copia del documento firmado digitalmente será enviada al propietario mediante correo electrónico una vez haya sido firmado por todos los firmantes.",
|
||||
"encrypted-pdf-not-support": "Los archivos PDF encriptados no están soportados actualmente.",
|
||||
"document-signed-alert-4": " ¡Felicitaciones! 🎉 ¡Este documento ha sido firmado exitosamente por todos los participantes!",
|
||||
"document-signed-alert-5": "Este documento ha sido firmado por todos los firmantes.",
|
||||
"document-signed-alert-6": "Este documento ha sido rechazado por uno o más destinatarios.",
|
||||
"document-signed-alert-7": "Este documento ya no está disponible.",
|
||||
"document-signed-alert-8": "Este documento ya ha sido enviado para ser firmado.",
|
||||
"yet-to-sign": "Todavía por firmar",
|
||||
"validation-alert-1": "Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"account-already-exists": "¡La cuenta ya existe!",
|
||||
"choose-password": "Elegir contraseña",
|
||||
"enter-password": "Ingresar contraseña",
|
||||
"sure": "claro",
|
||||
"subscribed-alert": "¡Ya te has suscrito al plan!",
|
||||
"monthly": "Mensual",
|
||||
"yearly-upto": "Anual (hasta 66% de descuento)",
|
||||
"billed-yearly": "Facturado anualmente",
|
||||
"billed-monthly": "Facturado mensualmente",
|
||||
"plansubscription-1": "Alójalo por tu cuenta gratuitamente",
|
||||
"visit-github": "Visitar GitHub",
|
||||
"user-already-exist-name": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"do-not-access-contact-admin": "No tienes acceso, por favor, contacta a un administrador.",
|
||||
"filed-required-correctly": "Por favor, rellena correctamente los detalles requeridos.",
|
||||
"admin-created": "Administrador creado",
|
||||
"invalid-masterkey": "Llave maestra inválida",
|
||||
"opensign-Setup": "Instalación de OpenSign",
|
||||
"master-key": " Llave maestra",
|
||||
"profile-update-alert": "Perfil actualizado exitosamente.",
|
||||
"date": "Fecha",
|
||||
"report-not-found": "Reporte no encontrado",
|
||||
"please-wait": "por favor, espera",
|
||||
"select-foler": "Seleccionar carpeta",
|
||||
"generated-on": "Generado el",
|
||||
"text-field": "Campo de texto",
|
||||
"font-size": "Tamaño de la fuente",
|
||||
"widget-status": {
|
||||
"Required": "Requerido",
|
||||
"Optional": "Opcional"
|
||||
},
|
||||
"all-right": "Todos los derechos reservados",
|
||||
"version": "versión",
|
||||
"social-media": {
|
||||
"github": "GitHub",
|
||||
"linked-in": "LinkedIn",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord"
|
||||
},
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.",
|
||||
"home-layout-2": "Para subir documentos para firmar tú mismo o para solicitar la firma de otros, simplemente selecciona los botones correspondientes.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar OpenSign! Si necesitas soporte siéntete libre de contactarnos.",
|
||||
"generate-token": "Mejora ahora para generar un token API de producción.",
|
||||
"opensign-drive-1": "Haz clic en los enlaces para navegar fácilmente por la estructura jerárquica de carpetas y ver los documentos de cada carpeta.",
|
||||
"opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
|
||||
"opensign-drive-3": "Ordena tus documentos por «Fecha» o «Nombre» usando este menú.",
|
||||
"opensign-drive-4": "Haz clic en este menú para mostrar los documentos en vista de lista.",
|
||||
"opensign-drive-5": "La lista de documentos es mostrada de acuerdo a la opción de ordenamiento seleccionada. Los íconos situados junto a cada documento indican su estado actual.",
|
||||
"opensign-drive-6": "Haz clic con el botón derecho en un documento para ver opciones como «Descargar», «Renombrar», «Mover» y «Eliminar». Haz clic en el documento para abrirlo.",
|
||||
"opensign-drive-7": "Haz clic con el botón derecho en cualquier carpeta para ver las opciones. Elige «Renombrar» para cambiar el nombre de la carpeta o haz clic en ella para navegar por su contenido.",
|
||||
"pdf-request-file-1": "Lista de firmantes que todavía tienen que firmar este documento.",
|
||||
"pdf-request-file-2": "Haz clic en cualquiera de los marcadores de posición que aparecen en el documento para firmar. A continuación, verás opciones para dibujar tu firma, escribirla o subir una imagen .",
|
||||
"pdf-request-file-3": "Haz clic en los botones «Atrás», «Rechazar» o «Finalizar» para navegar por el documento. Utiliza el menú de tres puntos para ver otras opciones, como el botón «Descargar».",
|
||||
"pdf-request-file-4": "Lista de firmantes que ya han firmado el documento.",
|
||||
"pdf-request-file-5": "Puedes hacer clic en «Firmar todo automáticamente» para firmar de manera automática en todos los lugares que deban ser firmados por ti. Asegúrate de revisar correctamente el documento antes de hacer clic en ese botón.",
|
||||
"placeholder-sign-1": "Selecciona un destinatario de esta lista para agregar un marcador de posición dónde este destinatario tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del destinatario una vez lo sueltes en el documento.",
|
||||
"placeholder-sign-2": "Hacer clic en el botón «Agregar destinatarios» te permitirá añadir más firmantes.",
|
||||
"placeholder-sign-3": "Haz clic en este botón para agregar más destinatarios/firmantes al documento.",
|
||||
"placeholder-sign-4": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los marcadores de posición existentes en la plantilla. Para tu comodidad, estos marcadores de posición coincidirán con el color del nombre del destinatario, haciéndolos fácilmente identificables.",
|
||||
"placeholder-sign-6": "Hacer clic en «Enviar» guardará el documento. En el siguiente paso podrás personalizar los correos que serán enviados a los destinatarios o copiar los enlaces para firmar y compartirlos con los destinatarios por tu cuenta.",
|
||||
"report-1": "Haz clic en el botón «Agregar» para crear una nueva plantilla. Las plantillas son documentos reusables diseñados para generar rápidamente nuevos documentos con la misma estructura pero diferentes firmantes. Por ejemplo, una plantilla de RR.HH. para el proceso de incorporación podría tener roles predefinidos como «Director de RR.HH.» y «Nuevo empleado». Cada vez que utilices la plantilla, puedes asignar el rol de «Nuevo empleado» a los distintos miembros del personal que se incorporen, mientras que el rol de « Director de RR.HH.» permanece constante, lo que facilita un proceso de incorporación fluido para cada persona contratada.",
|
||||
"redirect": "Haz clic en el botón «Usar» para crear un nuevo documento desde una plantilla existente.",
|
||||
"bulksend": "Para crear y enviar rápidamente múltiples documentos usando una plantilla existente, haz clic en el botón «Envío rápido».",
|
||||
"option": "Este menú revela más opciones como «Editar» y «Eliminar». Usa el botón «Editar» para agregar roles, modificar campos y actualizar tu plantilla. Los cambios aplicarán para todos los documentos futuros que sean creados usando esta plantilla pero no afectarán a los documentos ya existentes. Con el botón «Eliminar» puedes eliminar la plantilla.",
|
||||
"signyour-self-1": "Para personalizar el documento antes de que sea firmado, selecciona y arrastra al documento los widgets que prefieras. Escoge el espacio perfecto para cada modificación para adaptar el documento a tus necesidades.",
|
||||
"signyour-self-2": "Arrastra y suelta en cualquier lugar de esta área. Después podrás cambiar el tamaño y moverlo.",
|
||||
"template-placeholder-1": "Hacer clic en el botón «Agregar rol» te permitirá agregar varios roles de firmantes. Puedes asociar usuarios a cada rol en los siguientes pasos.",
|
||||
"template-placeholder-2": "Una vez que los roles hayan sido agregados, selecciona un rol de la lista para agregar un marcador de posición donde éste tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del rol una vez lo sueltes en el documento.",
|
||||
"template-placeholder-3": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"template-placeholder-4": "Arrastra el marcador de posición para un rol a cualquier lugar del documento. Recuerda, aparecerá con el mismo color que el nombre del rol para poder referenciarlo fácilmente.",
|
||||
"template-placeholder-5": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"webhook-1": "Mejora ahora para configurar un webhook",
|
||||
"Need your Signature": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan por tu revisión.",
|
||||
"Out for signatures": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan tu firma.",
|
||||
"Recent signature requests": "Esta es una lista de documentos que esperan por tu firma.",
|
||||
"Recently sent for signatures": "Esta es una lista de documentos que has enviado a otras partes para que sea firmado.",
|
||||
"Drafts": "Estos son documentos que has empezado pero que todavía no finalizas para su envío.",
|
||||
"public-template": "Este video muestra cómo puedes configurar tu perfil público personalizado, como «https://opensign.me/tu-nombre-de-usuario». También aprenderás cómo personalizar tu eslogan y cómo hacer tus plantillas disponibles para su firmado público."
|
||||
},
|
||||
"enter-email-plaholder": "Agrega una dirección de correo y presiona «Enter»",
|
||||
"success-email-alert": "¡Correo enviado exitosamente!",
|
||||
"expired-doc-title": "Documento expirado",
|
||||
"expired-on-mssg": "Este documento expiro el {{expiredDate}} y ya no está disponible para ser firmado.",
|
||||
"signature-validate-alert": "Por favor, confirma que has seleccionado al menos {{minRequiredCount}} casillas.",
|
||||
"signature-validate-alert-2": "Asegúrate de que este campo esté correctamente rellenado y que cumpla con todos los requisitos.",
|
||||
"remaing-users": "Usuarios restantes",
|
||||
"Role": "Rol",
|
||||
"additional-users": "Por favor, compra el complemento Usuarios.",
|
||||
"Quantity-of-users": "Cantidad de usuarios",
|
||||
"Price": "Precio",
|
||||
"Total-price-for-next-time": "Precio total para la próxima vez",
|
||||
"Proceed": "Proceder",
|
||||
"Select": "Seleccionar",
|
||||
"Add-seats": "Agregar plazas",
|
||||
"user-name-exist": "el nombre de usuario ya existe",
|
||||
"format": "formato",
|
||||
"select-date-format": "Selecciona un formato de fecha",
|
||||
"quantity-of-credits": "Cantidad de créditos premium",
|
||||
"remaining-credits": "Créditos premium disponibles:",
|
||||
"remaining-credits-help": "Usa créditos premium para la API para la firma de documentos, envío en masa, o para incorporar la integración de OpenSign en tu página web. Te quedan {{allowedcredits}} créditos incluidos y {{addoncredits}} créditos comprados adicionalmente.",
|
||||
"additional-credits": "Por favor, compra créditos premium",
|
||||
"quota-err-quicksend": "Cuota alcanzada, no tienes créditos suficientes.",
|
||||
"buy-credits": "Comprar créditos premium",
|
||||
"rotate-right": "Rotar a la derecha",
|
||||
"rotate-left": "Rotar a la izquierda",
|
||||
"rotate-alert-mssg": "Todos los widgets de esta página se perderán. ¿En definitiva quieres proceder?",
|
||||
"templateid": "ID de plantilla",
|
||||
"bulk-send-subcription-alert": "Por favor, mejora al plan a Professional o Team para usar envío en masa.",
|
||||
"generate-test-token": "Generar token de prueba",
|
||||
"regenerate-test-token": "Regenerar token de prueba",
|
||||
"help-test-token": "Este token puede ser usado para probar las API en el punto de acceso «https://sandbox.opensignlabs.com/api/v1», permitiéndote realizar firmas de documentos ilimitadas. Por favor, ten en cuenta que el API de pruebas firmará tus documentos con certificados autofirmados, que podrían no ser reconocidos por Adobe como válidos. Una vez hayas completado tus pruebas, puedes mejorar a uno de nuestros planes de pago para generar un token de producción.",
|
||||
"help-api-token": "Este token puede ser usado para acceder a las API de producción en el punto de acceso «{{origin}}/api/v1». Solo puede ser generado con uno de nuestros planes de pago.",
|
||||
"reason": "Razón",
|
||||
"decline-by": "Rechazado/revocado por",
|
||||
"document-declined": "Documento rechazado",
|
||||
"public-template-mssg-1": "Para integrar OpenSign a tu proyecto React o Next.js, simplemente ejecuta los siguientes comandos:",
|
||||
"public-template-mssg-2": "Asegúrate de tener «npm» o «yarn» configurado en tu proyecto. Si estás usando «yarn», puedes reemplazar «npm install» con «yarn add @opensign/react».",
|
||||
"public-template-mssg-3": "¿Necesitas más detalles o ejemplos?",
|
||||
"public-template-mssg-4": "Visita la",
|
||||
"public-template-mssg-5": " «npm» para las últimas actualizaciones, documentación detallada e historial de versiones.",
|
||||
"public-template-mssg-6": "Antes de que puedas usar este fragmento de código, debes convertir esta plantilla en pública.",
|
||||
"copy-code": "COPIAR",
|
||||
"copied-code": "COPIADO",
|
||||
"Installation": "Instalación",
|
||||
"Usage": "Uso",
|
||||
"insufficient-credits": "Créditos de firma insuficientes",
|
||||
"insufficient-credits-mssg": "El propietario de este documento actualmente carece de los créditos OpenSign necesarios para que puedas firmarlo. Por favor, contacta al propietario si necesitas más detalles.",
|
||||
"angular-npm-mssg-1": "Para integrar OpenSign a tu proyecto Angular, simplemente ejecuta los siguientes comandos:",
|
||||
"quota-mail-info-head": "Límite mensual de solicitudes de firma por correo",
|
||||
"quota-mail-info": "Puedes enviar hasta 15 solicitudes de firma por correo cada mes. Mejora ahora para enviar solicitudes de firma sin límite.",
|
||||
"quota-mail": "Has alcanzado tu límite de 15 solicitudes de firma por correo para este mes. Mejora ahora para continuar enviando correos.",
|
||||
"quota-mail-tip": "Consejo: Todavía puedes solciitar la firma de <1>documentos ilimitados</1> compartiendo manualmente los enlaces de solicitud de firma.",
|
||||
"quota-mail-head": "Cuota alcanzada",
|
||||
"unauthorized-modal": "No tienes permisos para realizar esta acción, por favor, contacta a {{adminEmail}}.",
|
||||
"sent-this-month": "Enviados este mes",
|
||||
"available-seats": "Plazas disponibles",
|
||||
"buy-users": "Comprar más usuarios",
|
||||
"isenable-otp": "Habilitar verificación con OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "¿Te gustaría habilitar el proceso de verificación usando una contraseña de un solo uso (OTP)?",
|
||||
"p2": "Seleccionar esta opción habilitará la verificación con OTP. Los usuarios recibirán un código de verificación por correo, la cual tendrán que ingresar para poder firmar el documento.",
|
||||
"p3": "Seleccionar esta opción deshabilitará la verificación con OTP, lo cual permitirá a los usuarios firmar el documento directamente sin pasos adicionales.",
|
||||
"p4": "Por favor, selecciona la opción que mejor se adapte a tus requisitos para la firma de documentos."
|
||||
},
|
||||
"advanced-options": "Opciones avanzadas",
|
||||
"hide-advanced-options": "Ocultar las opciones avanzadas",
|
||||
"download-files": "Descargar archivos",
|
||||
"download-pdf": "Descargar PDF",
|
||||
"pdf-certificate": "Descargar PDF + Certificado",
|
||||
"document-logs": "Registros de documentos",
|
||||
"server-down": "No se pudo conectar al servidor de OpenSign. Si estás alojando OpenSign por tu cuenta, por favor, asegúrate de que todos los pasos de la documentación han sido seguidos correctamente. Si estás ejecutando OpenSign localmente, puedes estar accediendo a OpenSign por un número de puerto incorrecto.",
|
||||
"admin-exists": "El administrador ya existe. Por favor, inicia sesión en la aplicación usando las credenciales de administrador para poder administrar los usuarios.",
|
||||
"public-tour-message": "Por favor, convierte la plantilla en pública para copiar la URL pública",
|
||||
"add-user-template": "Es necesario agregar un rol antes de poder agregarle campos.",
|
||||
"pdf-uncompatible": "Este pdf no es compatible con opensign, comuníquese con <support@opensignlabs.com>",
|
||||
"enable-tour": "Habilitar recorrido",
|
||||
"istourenabled-help":{
|
||||
"p1": "Esto permitirá realizar una visita guiada para los firmantes, brindando instrucciones durante el proceso de firma.",
|
||||
"p2": "Esto desactivará la visita guiada, lo que garantizará una experiencia de firma más rápida e ininterrumpida.",
|
||||
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
|
||||
},
|
||||
"form-title-1": "Configuración del flujo de documentos",
|
||||
"form-title-2": "Configuración de seguridad"
|
||||
|
||||
}
|
||||
@@ -136,7 +136,8 @@
|
||||
"Name": "Nom",
|
||||
"Status": "Statut",
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir"
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
@@ -150,7 +151,9 @@
|
||||
"Share": "Partager",
|
||||
"View": "Voir",
|
||||
"option": "Option",
|
||||
"Embed": "Intégrer"
|
||||
"Embed": "Intégrer",
|
||||
"Copy TemplateId": "Copier l'ID du modèle",
|
||||
"Copy Public URL":"Copier l'URL publique"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
@@ -261,9 +264,10 @@
|
||||
"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.",
|
||||
"public-url": "Profil public requis",
|
||||
"public-url-copy": "Voici votre URL publique : ",
|
||||
"public-url-copy-mssg": "copiez-la ou partagez-le avec le signataire et vous pouvoir voir tous vos paramètres publics modèles.",
|
||||
"add-public-url-alert": "Veuillez terminer la configuration de votre profil public OpenSign. Une fois cela fait, vous pourrez rendre les modèles publics. Ce processus ne doit être effectué qu’une seule fois.",
|
||||
"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",
|
||||
@@ -571,9 +575,10 @@
|
||||
"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.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "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-6": "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'.",
|
||||
@@ -629,12 +634,54 @@
|
||||
"reason" :"Raison",
|
||||
"decline-by" :"Refusé/révoqué par",
|
||||
"document-declined":"document refusé",
|
||||
"public-template-mssg-1" :"Pour intégrer OpenSign dans votre projet React ou Next.js, exécutez simplement la commande suivante :",
|
||||
"public-template-mssg-2" :"Assurez-vous que npm ou Yarn est configuré dans votre projet. Si vous utilisez Yarn, vous pouvez remplacer npm install par Yarn Add @opensign/react.",
|
||||
"public-template-mssg-3" :"Besoin de plus de détails ou d'exemples ?",
|
||||
"public-template-mssg-4": "Visitez le",
|
||||
"public-template-mssg-5": "npm pour les dernières mises à jour, une documentation détaillée et l'historique des versions.",
|
||||
"public-template-mssg-6":"Avant de pouvoir utiliser cet extrait de code, vous devez rendre ce modèle public.",
|
||||
"copy-code":"COPIE",
|
||||
"copied-code":"COPIÉ",
|
||||
"Installation":"Installation",
|
||||
"Usage" :"Usage",
|
||||
"insufficient-credits":"Crédits de signature insuffisants",
|
||||
"insufficient-credits-mssg" :"Le propriétaire de ce document ne dispose actuellement pas des crédits OpenSign nécessaires pour que vous puissiez le signer. Veuillez contacter le propriétaire si vous avez besoin de plus de détails.",
|
||||
"angular-npm-mssg-1" :"Pour intégrer OpenSign dans votre projet Angular, exécutez simplement la commande suivante :",
|
||||
"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"
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month":"envoyé ce mois-ci",
|
||||
"available-seats":"Disponible sièges",
|
||||
"buy-users":"Acheter plus d'utilisateurs",
|
||||
"isenable-otp": "Activer la vérification OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Souhaitez-vous activer le processus de vérification à l'aide d'un mot de passe à usage unique (OTP)?",
|
||||
"p2": "La sélection de cette option activera la vérification OTP. Les utilisateurs recevront un code de vérification par e-mail, qu'ils devront saisir pour signer le document.",
|
||||
"p3": "La sélection de cette option désactivera la vérification OTP, permettant aux utilisateurs de signer le document directement sans étapes supplémentaires.",
|
||||
"p4": "Veuillez choisir l'option qui correspond le mieux à vos exigences en matière de signature de documents."
|
||||
},
|
||||
"advanced-options":"Options avancées",
|
||||
"hide-advanced-options": "Masquer les options avancées",
|
||||
"download-files" :"Télécharger des fichiers",
|
||||
"download-pdf" :"Télécharger le PDF",
|
||||
"pdf-certificate" :"Télécharger Pdf + Certificat",
|
||||
"document-logs":"Journaux de documents",
|
||||
"server-down": "Impossible de se connecter au serveur OpenSign. Si vous hébergez vous-même OpenSign, veuillez vous assurer que toutes les étapes de la documentation ont été correctement suivies. Si vous exécutez OpenSign localement, vous y accédez peut-être via un numéro de port incorrect.",
|
||||
"admin-exists": "L'administrateur existe déjà. Veuillez vous connecter à l'application en utilisant les informations d'identification de l'administrateur afin de gérer les utilisateurs.",
|
||||
"enable-tour": "Activer la visite",
|
||||
"istourenabled-otp-help":{
|
||||
"p1": "Cela permettra une visite guidée pour les signataires, fournissant des instructions pendant le processus de signature.",
|
||||
"p2": "Cela désactivera la visite guidée, garantissant ainsi une expérience de signature plus rapide et ininterrompue.",
|
||||
"p3": "Conseil : si vos signataires connaissent OpenSign et l'utilisent fréquemment, vous souhaiterez peut-être désactiver la visite guidée pour une expérience plus fluide. Choisissez l'option qui correspond le mieux à vos besoins en matière de signature de documents."
|
||||
},
|
||||
"form-title-1": "Paramètre du flux de documents",
|
||||
"form-title-2": "Paramètre de sécurité",
|
||||
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
|
||||
"add-user-template":"Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
|
||||
"pdf-uncompatible":"Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>"
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -301,6 +301,9 @@ const AddUser = (props) => {
|
||||
setAllowedUser(amount.quantity);
|
||||
setPlanInfo((obj) => ({ ...obj, totalAllowedUser: _resAddon.addon }));
|
||||
}
|
||||
if (props.handleBuyUsers) {
|
||||
props.handleBuyUsers(amount.quantity, _resAddon.addon);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
@@ -495,8 +498,7 @@ const AddUser = (props) => {
|
||||
{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
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -31,7 +31,6 @@ const BulkSendUi = (props) => {
|
||||
const [isFreePlan, setIsFreePlan] = useState(false);
|
||||
const [admin, setAdmin] = useState({
|
||||
objectId: "",
|
||||
name: "",
|
||||
email: "",
|
||||
isAdmin: true
|
||||
});
|
||||
@@ -63,8 +62,7 @@ const BulkSendUi = (props) => {
|
||||
setAdmin((obj) => ({
|
||||
...obj,
|
||||
isAdmin: false,
|
||||
name: extUser?.CreatedBy?.name,
|
||||
email: extUser?.CreatedBy?.email
|
||||
email: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
}
|
||||
setAdmin((obj) => ({ ...obj, objectId: subscription?.adminId }));
|
||||
@@ -491,10 +489,7 @@ const BulkSendUi = (props) => {
|
||||
</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
|
||||
})}
|
||||
{t("unauthorized-modal", { adminEmail: admin?.email })}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -20,6 +20,9 @@ const Header = ({ showSidebar }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
const current_notification_version = "1"; // Update this with each new feature release
|
||||
// Get the dismissed version from local storage
|
||||
const dismissedVersion = localStorage.getItem("notificationDismissedVersion");
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -30,9 +33,15 @@ const Header = ({ showSidebar }) => {
|
||||
);
|
||||
const [emailUsed, setEmailUsed] = useState(0);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
|
||||
const [showNotification, setShowNotification] = useState(
|
||||
(!dismissedVersion || dismissedVersion !== current_notification_version) &&
|
||||
true
|
||||
);
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
showSidebar();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
checkSubscription();
|
||||
@@ -58,6 +67,13 @@ const Header = ({ showSidebar }) => {
|
||||
} catch (err) {
|
||||
console.log("err in while fetching monthlyfreeEmails", err);
|
||||
}
|
||||
} else {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
} else {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +131,34 @@ const Header = ({ showSidebar }) => {
|
||||
const handleMailUsed = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
// Handle dismissing the notification
|
||||
const dismissNotification = () => {
|
||||
// Store the current version as dismissed in local storage
|
||||
localStorage.setItem(
|
||||
"notificationDismissedVersion",
|
||||
current_notification_version
|
||||
);
|
||||
setShowNotification(false);
|
||||
};
|
||||
return (
|
||||
<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")}
|
||||
{isEnableSubscription && showNotification && (
|
||||
<div className="flex justify-between items-center shadow py-1 bg-[#CAE4FA] p-1">
|
||||
<div className="text-center text-[14px] ml-auto">
|
||||
{t("header-news")} —
|
||||
<span
|
||||
className="cursor-pointer font-medium underline text-blue-800"
|
||||
onClick={() => navigate("/profile")}
|
||||
>
|
||||
{" " + t("header-news-btn") + "."}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="ml-auto mr-1 cursor-pointer"
|
||||
onClick={() => dismissNotification()}
|
||||
>
|
||||
{" " + t("header-news-btn") + "."}
|
||||
</span>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="op-navbar bg-base-100 shadow">
|
||||
|
||||
@@ -2,13 +2,13 @@ import React, { useState, useEffect, useRef } from "react";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import axios from "axios";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { saveAs } from "file-saver";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Table from "react-bootstrap/Table";
|
||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import FolderModal from "../shared/fields/FolderModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { handleDownloadPdf } from "../../constant/Utils";
|
||||
|
||||
function DriveBody(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -100,16 +100,17 @@ function DriveBody(props) {
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
const signedUrl = data.SignedUrl;
|
||||
const signerExist = data?.Signers;
|
||||
const isDecline = data?.IsDeclined;
|
||||
const isPlaceholder = data?.Placeholders;
|
||||
const signedUrl = data?.SignedUrl;
|
||||
const isSignYourself = data?.IsSignyourself;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
else if ((!signerExist && !isPlaceholder) || isSignYourself) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
@@ -136,12 +137,10 @@ function DriveBody(props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (selectType, data) => {
|
||||
const handleMenuItemClick = async (selectType, data) => {
|
||||
switch (selectType) {
|
||||
case "Download": {
|
||||
const pdfName = data && data.Name;
|
||||
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
await handleDownloadPdf([data]);
|
||||
break;
|
||||
}
|
||||
case "Rename": {
|
||||
@@ -264,12 +263,6 @@ function DriveBody(props) {
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import React from "react";
|
||||
|
||||
function BorderResize({ right, top }) {
|
||||
function BorderResize(props) {
|
||||
const getHeight = () => {
|
||||
const height = props.posHeight(props.pos, props.isSignYourself);
|
||||
if (height > 14) {
|
||||
return "14px";
|
||||
} else {
|
||||
return `${height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${right ? `-right-[12px]` : "-right-[1px]"} ${
|
||||
top ? `-bottom-[11px]` : "-bottom-[1px]"
|
||||
} absolute inline-block w-[14px] h-[14px] hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
style={{
|
||||
width: getHeight() || "14px",
|
||||
height: getHeight() || "14px"
|
||||
}}
|
||||
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${
|
||||
props.top ? `-bottom-[12px]` : "-bottom-[2px] "
|
||||
} absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
@@ -14,7 +16,11 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
Description: template?.Description || "",
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||
AutomaticReminders: template?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
||||
IsTourEnabled: template?.IsTourEnabled
|
||||
? `${template?.IsTourEnabled}`
|
||||
: "false"
|
||||
});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -40,8 +46,9 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
||||
const AutoReminder = formData?.AutomaticReminders || false;
|
||||
|
||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||
let reminderDate = {};
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||
@@ -49,7 +56,13 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
reminderDate = { NextReminderDate: ReminderDate };
|
||||
}
|
||||
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
|
||||
const data = {
|
||||
...formData,
|
||||
SendinOrder: isChecked,
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
IsTourEnabled: isTourEnabled,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
};
|
||||
const handleAutoReminder = () => {
|
||||
@@ -184,6 +197,123 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
<span className={isSubscribe ? "" : " text-gray-300"}>
|
||||
{t("isenable-otp")}{" "}
|
||||
<a data-tooltip-id="isenableotp-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<Tooltip id="isenableotp-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("isenable-otp")}</p>
|
||||
<p>{t("isenable-otp-help.p1")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("isenable-otp-help.p2")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("isenable-otp-help.p3")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("isenable-otp-help.p4")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1 `}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsEnableOTP"
|
||||
checked={formData.IsEnableOTP === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1 `}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="IsEnableOTP"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.IsEnableOTP === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
<span>
|
||||
{t("enable-tour")}
|
||||
<a data-tooltip-id="istourenabled-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
</span>
|
||||
<Tooltip id="istourenabled-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("enable-tour")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("istourenabled-help.p1")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("istourenabled-help.p2")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("istourenabled-help.p3")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className={` flex items-center gap-2 ml-2 mb-1 `}>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsTourEnabled"
|
||||
checked={formData.IsTourEnabled === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className={` flex items-center gap-2 ml-2 mb-1 `}>
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="IsTourEnabled"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.IsTourEnabled === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-[1rem] flex justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { handleDownloadPdf, handleToPrint } from "../../constant/Utils";
|
||||
import { handleToPrint } from "../../constant/Utils";
|
||||
import { themeColor, emailRegex } from "../../constant/const";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
@@ -15,7 +15,8 @@ function EmailComponent({
|
||||
sender,
|
||||
setIsAlert,
|
||||
extUserId,
|
||||
activeMailAdapter
|
||||
activeMailAdapter,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
@@ -29,6 +30,27 @@ function EmailComponent({
|
||||
const pdfName = pdfDetails[0]?.Name;
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const FileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
let presignedUrl = pdfUrl;
|
||||
try {
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl, docId: docId, fileAdapterId: FileAdapterId },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
presignedUrl = axiosRes.data.result;
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
}
|
||||
for (let i = 0; i < emailList.length; i++) {
|
||||
try {
|
||||
const imgPng =
|
||||
@@ -46,7 +68,7 @@ function EmailComponent({
|
||||
mailProvider: activeMailAdapter,
|
||||
extUserId: extUserId,
|
||||
pdfName: pdfName,
|
||||
url: pdfUrl,
|
||||
url: presignedUrl,
|
||||
recipient: emailList[i],
|
||||
subject: `${sender.name} has signed the doc - ${pdfName}`,
|
||||
from: sender.email,
|
||||
@@ -129,6 +151,11 @@ function EmailComponent({
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
@@ -154,7 +181,9 @@ function EmailComponent({
|
||||
<div className="flex flex-row">
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
@@ -163,9 +192,10 @@ function EmailComponent({
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
{t("download")}
|
||||
@@ -261,11 +291,7 @@ function EmailComponent({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
onClick={() => handleClose()}
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import React, { useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import { copytoData } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function EmbedTab(props) {
|
||||
const { t } = useTranslation();
|
||||
const tabName = [
|
||||
{ title: "React/Next.js", icon: "fa-brands fa-react", color: "#61dafb" },
|
||||
{ title: "JavaScript", icon: "fa-brands fa-js", color: "#ffd43b" },
|
||||
{ title: "Angular", icon: "fa-brands fa-angular", color: "#ff5733" }
|
||||
];
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
// State to track if the code has been copied
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const reactCode = [
|
||||
{
|
||||
id: 0,
|
||||
title: "Installation",
|
||||
codeString: `
|
||||
npm install @opensign/react`
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: "Usage",
|
||||
codeString: `
|
||||
import React from "react";
|
||||
import Opensign from "@opensign/react";
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<Opensign
|
||||
onLoad={() => console.log("success")}
|
||||
onLoadError={(error) => console.log(error)}
|
||||
templateId= "${props.templateId}"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const angularCode = [
|
||||
{
|
||||
id: 0,
|
||||
title: "Installation",
|
||||
codeString: `
|
||||
npm install @opensign/angular`
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: "Usage",
|
||||
codeString: `
|
||||
import { Component } from '@angular/core';
|
||||
import {OpensignComponent} from "@opensign/angular"
|
||||
|
||||
@Component({
|
||||
selector:'app-root',
|
||||
standalone: true,
|
||||
imports: [OpensignComponent],
|
||||
template:\`<opensign templateId="${props.templateId}"
|
||||
(onLoad)="handleLoad()"
|
||||
(onLoadError)="handleError($event)"
|
||||
></opensign>\`,
|
||||
})
|
||||
export class AppComponent{
|
||||
handleLoad() {
|
||||
console.log("success");
|
||||
}
|
||||
handleError(error: string) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
`
|
||||
}
|
||||
];
|
||||
const jsCodeString = `
|
||||
<script
|
||||
src= "https://app.opensignlabs.com/static/js/public-template.bundle.js"
|
||||
id="opensign-script"
|
||||
templateId=${props.templateId}
|
||||
></script>
|
||||
`;
|
||||
|
||||
const handleCopy = (code, ind) => {
|
||||
copytoData(code);
|
||||
setIsCopied({ ...isCopied, [ind]: true });
|
||||
setTimeout(() => setIsCopied(false), 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-4 border-t-[1px]">
|
||||
<div className="flex justify-center items-center mt-2">
|
||||
<div role="tablist" className="op-tabs op-tabs-bordered">
|
||||
{tabName.map((tabData, ind) => (
|
||||
<div
|
||||
onClick={() => setActiveTab(ind)}
|
||||
key={ind}
|
||||
role="tab"
|
||||
className={`${
|
||||
activeTab === ind ? "op-tab-active" : ""
|
||||
} op-tab flex items-center`}
|
||||
>
|
||||
<i
|
||||
className={`${tabData.icon}`}
|
||||
style={{ color: tabData.color }}
|
||||
></i>
|
||||
<span className="ml-1 text-[10px] font-medium md:font-normal md:text-[15px]">
|
||||
{tabData.title}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{activeTab === 0 ? (
|
||||
<div className="mt-4">
|
||||
{reactCode.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind}>
|
||||
<p className="font-medium text-[18px]">
|
||||
{t(`${data.title}`)}
|
||||
</p>
|
||||
{ind === 0 && (
|
||||
<p className="text-[13px] mt-2">
|
||||
{t("public-template-mssg-1")}
|
||||
</p>
|
||||
)}
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(data.codeString, ind)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[ind] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{data.codeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<p className="font-medium text-[15px]">
|
||||
{t("public-template-mssg-3")}
|
||||
</p>
|
||||
<p className="my-[6px]">
|
||||
{" "}
|
||||
{t("public-template-mssg-4")}
|
||||
<a
|
||||
href="https://www.npmjs.com/package/@opensign/react"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="cursor-pointer text-blue-700 "
|
||||
>
|
||||
{" "}
|
||||
OpenSign React package{" "}
|
||||
</a>
|
||||
{t("public-template-mssg-5")}
|
||||
</p>
|
||||
</div>
|
||||
) : activeTab === 1 ? (
|
||||
<div className="mt-4">
|
||||
<div>
|
||||
{/* <p className="font-medium text-[18px]">{t(`${data.title}`)}</p> */}
|
||||
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(jsCodeString, 0)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[0] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{jsCodeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
activeTab === 2 && (
|
||||
<div className="mt-4">
|
||||
{angularCode.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind}>
|
||||
<p className="font-medium text-[18px]">
|
||||
{t(`${data.title}`)}
|
||||
</p>
|
||||
{ind === 0 && (
|
||||
<p className="text-[13px] mt-2">
|
||||
{t("angular-npm-mssg-1")}
|
||||
</p>
|
||||
)}
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(data.codeString, ind)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[ind] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{data.codeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<p className="font-medium text-[15px]">
|
||||
{t("public-template-mssg-3")}
|
||||
</p>
|
||||
<p className="my-[6px]">
|
||||
{" "}
|
||||
{t("public-template-mssg-4")}
|
||||
<a
|
||||
href="https://www.npmjs.com/package/@opensign/angular"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="cursor-pointer text-blue-700 "
|
||||
>
|
||||
{" "}
|
||||
OpenSign Angular package{" "}
|
||||
</a>
|
||||
{t("public-template-mssg-5")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmbedTab;
|
||||
@@ -39,7 +39,8 @@ function Header({
|
||||
clickOnZoomOut,
|
||||
handleRotationFun,
|
||||
isDisableRotate,
|
||||
templateId
|
||||
templateId,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
@@ -94,9 +95,13 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
@@ -142,7 +147,7 @@ function Header({
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
@@ -209,57 +214,67 @@ function Header({
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfDetails[0]?.URL,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">{t("download")}</span>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">
|
||||
{t("download")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{!isDisableRotate && (
|
||||
<>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
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>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-right")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
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>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-left")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
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>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">{t("zoom-in")}</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
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>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("zoom-out")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
@@ -342,7 +357,9 @@ function Header({
|
||||
alreadySign ? (
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
@@ -370,9 +387,13 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
@@ -397,11 +418,7 @@ function Header({
|
||||
{templateId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
@@ -427,11 +444,7 @@ function Header({
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
@@ -459,7 +472,9 @@ function Header({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
@@ -469,9 +484,9 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("download")}</span>
|
||||
|
||||
@@ -651,6 +651,21 @@ function Placeholder(props) {
|
||||
}
|
||||
}
|
||||
};
|
||||
//function to calculate font size
|
||||
const calculateFont = (size, isMinHeight) => {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
const fontSize = (size || 12) * containerScale * props.scale;
|
||||
//isMinHeight to set text box minimum height
|
||||
if (isMinHeight) {
|
||||
return fontSize * 1.5 + "px";
|
||||
} else {
|
||||
return fontSize + "px";
|
||||
}
|
||||
};
|
||||
|
||||
const getCursor = () => {
|
||||
if (props.data && props.isNeedSign) {
|
||||
@@ -725,7 +740,9 @@ function Placeholder(props) {
|
||||
: props?.pos?.zIndex
|
||||
? props.pos.zIndex
|
||||
: "5",
|
||||
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
|
||||
background: props.data
|
||||
? props.data?.blockColor + "b0"
|
||||
: "rgba(203, 233, 237, 0.69)"
|
||||
}}
|
||||
onDrag={() => {
|
||||
setDraggingEnabled(true);
|
||||
@@ -743,12 +760,16 @@ function Placeholder(props) {
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself)
|
||||
}}
|
||||
minHeight={calculateFont(props.pos.options?.fontSize, true)}
|
||||
maxHeight="auto"
|
||||
onResizeStart={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
setTimeout(() => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
}, 50);
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
@@ -783,18 +804,28 @@ function Placeholder(props) {
|
||||
h: ref.offsetHeight / (props.scale * containerScale)
|
||||
});
|
||||
}}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
onClick={() => !props.isResize && handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.isShowBorder &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId ? (
|
||||
<BorderResize right={-12} top={-11} />
|
||||
<BorderResize
|
||||
right={-12}
|
||||
top={-11}
|
||||
pos={props.pos}
|
||||
posHeight={props.posHeight}
|
||||
isSignYourself={props.isSignYourself}
|
||||
/>
|
||||
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||
props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" ? (
|
||||
<BorderResize />
|
||||
<BorderResize
|
||||
posHeight={props.posHeight}
|
||||
isSignYourself={props.isSignYourself}
|
||||
pos={props.pos}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
@@ -860,6 +891,7 @@ function Placeholder(props) {
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -887,6 +919,7 @@ function Placeholder(props) {
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -29,7 +29,7 @@ function PlaceholderType(props) {
|
||||
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 fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||
const fontColor = props.pos.options?.fontColor || "black";
|
||||
const months = [
|
||||
"January",
|
||||
|
||||
@@ -65,7 +65,8 @@ function RenderPdf({
|
||||
fontSize,
|
||||
setFontSize,
|
||||
fontColor,
|
||||
setFontColor
|
||||
setFontColor,
|
||||
isResize
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = window.innerWidth < 767;
|
||||
@@ -104,6 +105,7 @@ function RenderPdf({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const posHeight = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
@@ -207,6 +209,7 @@ function RenderPdf({
|
||||
pageNumber={pageNumber}
|
||||
ispublicTemplate={ispublicTemplate}
|
||||
handleUserDetails={handleUserDetails}
|
||||
isResize={isResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -357,6 +360,7 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -417,6 +421,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -548,6 +554,7 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -611,6 +618,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ function SelectLanguage(props) {
|
||||
const { i18n } = useTranslation();
|
||||
const languages = [
|
||||
{ value: "en", text: "English" },
|
||||
{ value: "es", text: "Española" },
|
||||
{ value: "fr", text: "Français" }
|
||||
];
|
||||
const [lang, setLang] = useState(i18next.language || "en");
|
||||
|
||||
@@ -31,8 +31,8 @@ function SignPad({
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const [textWidth, setTextWidth] = useState(0);
|
||||
const [textHeight, setTextHeight] = useState(0);
|
||||
const [signatureType, setSignatureType] = useState("draw");
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
@@ -51,7 +51,7 @@ function SignPad({
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
const currentUserName = jsonSender && jsonSender?.name;
|
||||
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
@@ -137,7 +137,7 @@ function SignPad({
|
||||
(isTab === "draw" && isSignImg) ||
|
||||
(isTab === "image" && image) ||
|
||||
(isTab === "mysignature" && isDefaultSign) ||
|
||||
(isTab === "type" && textWidth)
|
||||
(isTab === "type" && signValue)
|
||||
? false
|
||||
: image
|
||||
? false
|
||||
@@ -169,10 +169,10 @@ function SignPad({
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedName = currentUserName && currentUserName.trim();
|
||||
const trimmedName = currentUserName && currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setSignValue(userName || "");
|
||||
setFontSelect("Fasthand");
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -200,8 +200,10 @@ function SignPad({
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
if (isTab === "type") {
|
||||
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const trimmedName = signValue
|
||||
? signValue?.trim()
|
||||
: currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
@@ -594,7 +596,7 @@ function SignPad({
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current?.toDataURL())
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ const SuggestionInput = (props) => {
|
||||
|
||||
const handleInputChange = async (e) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value?.toLowerCase());
|
||||
setInputValue(value?.toLowerCase()?.replace(/\s/g, ""));
|
||||
if (props.onChange) {
|
||||
props.onChange(value);
|
||||
props.onChange(value?.toLowerCase()?.replace(/\s/g, ""));
|
||||
}
|
||||
|
||||
if (value.trim() === "") {
|
||||
|
||||
+441
-338
@@ -33,14 +33,15 @@ export const openInNewTab = (url, target) => {
|
||||
export async function fetchSubscription(
|
||||
extUserId,
|
||||
contactObjId,
|
||||
isGuestSign = false
|
||||
isGuestSign = false,
|
||||
isPublic = false
|
||||
) {
|
||||
try {
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
// console.log("extClass ", extClass);
|
||||
let extUser;
|
||||
if (extClass && extClass.length > 0) {
|
||||
if (extClass && extClass.length > 0 && !isPublic) {
|
||||
extUser = extClass[0].objectId;
|
||||
} else {
|
||||
extUser = extUserId;
|
||||
@@ -54,7 +55,7 @@ export async function fetchSubscription(
|
||||
};
|
||||
const params = isGuestSign
|
||||
? { contactId: contactObjId }
|
||||
: { extUserId: extUser };
|
||||
: { extUserId: extUser, ispublic: isPublic };
|
||||
const tenatRes = await axios.post(url, params, { headers: headers });
|
||||
let plan, status, billingDate, adminId;
|
||||
if (isGuestSign) {
|
||||
@@ -708,7 +709,10 @@ export const createDocument = async (
|
||||
Signers: signers,
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5)
|
||||
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5),
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false,
|
||||
IsTourEnabled: Doc?.IsTourEnabled || false,
|
||||
FileAdapterId: Doc?.FileAdapterId || ""
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -1084,18 +1088,22 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
||||
const textContent = documentId && `OpenSign™ DocumentId: ${documentId} `;
|
||||
const pages = pdfDoc.getPages();
|
||||
const page = pages[i];
|
||||
const getObj = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
10,
|
||||
5,
|
||||
1,
|
||||
page.getSize(),
|
||||
fontSize,
|
||||
rgb(0.5, 0.5, 0.5),
|
||||
font,
|
||||
page
|
||||
);
|
||||
page.drawText(textContent, getObj);
|
||||
try {
|
||||
const getObj = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
10,
|
||||
5,
|
||||
1,
|
||||
page.getSize(),
|
||||
fontSize,
|
||||
rgb(0.5, 0.5, 0.5),
|
||||
font,
|
||||
page
|
||||
);
|
||||
page.drawText(textContent, getObj);
|
||||
} catch (err) {
|
||||
console.log("Err in embed docId on page", i + 1, err?.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1319,7 +1327,9 @@ export const multiSignEmbed = async (
|
||||
);
|
||||
pdfDoc.registerFontkit(fontkit);
|
||||
const font = await pdfDoc.embedFont(fontBytes, { subset: true });
|
||||
let hasError = false;
|
||||
for (let item of widgets) {
|
||||
if (hasError) break; // Stop the outer loop if an error occurred
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
item?.pageNumber,
|
||||
@@ -1332,8 +1342,17 @@ export const multiSignEmbed = async (
|
||||
if (signyourself) {
|
||||
updateItem = item.pos;
|
||||
} else {
|
||||
// Checking required and optional widget types
|
||||
// For both required and optional widgets, handle signurl, defaultValue, and response as the widget's data
|
||||
// If the widget type is checkbox or radio (whether required or optional), we don't need to validate its value.
|
||||
// Instead, add an empty checkbox/radio, or if a value exists, mark the checkbox/radio as checked.
|
||||
updateItem = item.pos.filter(
|
||||
(data) => data?.options?.status === "required"
|
||||
(data) =>
|
||||
data?.options?.SignUrl ||
|
||||
data?.options?.defaultValue ||
|
||||
data?.options?.response ||
|
||||
data?.type === "checkbox" ||
|
||||
data?.type === radioButtonWidget
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -1348,323 +1367,328 @@ export const multiSignEmbed = async (
|
||||
widgetsPositionArr.map(async (url) => {
|
||||
let signUrl = url.SignUrl && url.SignUrl;
|
||||
if (signUrl) {
|
||||
if (url.ImageType === "image/png") {
|
||||
//function for convert signature png base64 url to jpeg base64
|
||||
const newUrl = await convertPNGtoJPEG(signUrl);
|
||||
signUrl = newUrl;
|
||||
}
|
||||
const res = await fetch(signUrl);
|
||||
return res.arrayBuffer();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
widgetsPositionArr.forEach(async (position, id) => {
|
||||
let img;
|
||||
if (["signature", "stamp", "initials", "image"].includes(position.type)) {
|
||||
for (let [id, position] of widgetsPositionArr.entries()) {
|
||||
if (hasError) break; // Stop the inner loop if an error occurred
|
||||
try {
|
||||
let img;
|
||||
if (
|
||||
(position.ImageType && position.ImageType === "image/png") ||
|
||||
position.ImageType === "image/jpeg"
|
||||
["signature", "stamp", "initials", "image"].includes(position.type)
|
||||
) {
|
||||
img = await pdfDoc.embedJpg(images[id]);
|
||||
} else {
|
||||
img = await pdfDoc.embedPng(images[id]);
|
||||
}
|
||||
} else if (!position.type) {
|
||||
if (
|
||||
(position.ImageType && position.ImageType === "image/png") ||
|
||||
position.ImageType === "image/jpeg"
|
||||
) {
|
||||
img = await pdfDoc.embedJpg(images[id]);
|
||||
} else {
|
||||
img = await pdfDoc.embedPng(images[id]);
|
||||
}
|
||||
}
|
||||
let widgetWidth, widgetHeight;
|
||||
widgetWidth = placeholderWidth(position);
|
||||
widgetHeight = placeholderHeight(position);
|
||||
const xPos = (position) => {
|
||||
const resizePos = position.xPosition;
|
||||
//first two condition handle to old data already saved from mobile view which scale point diffrent
|
||||
if (isMobile && position.isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
const x = resizePos * (position.scale / scale);
|
||||
return x * scale;
|
||||
} else if (position.isMobile && position.scale) {
|
||||
const x = resizePos * position.scale;
|
||||
return x;
|
||||
} else {
|
||||
return resizePos;
|
||||
}
|
||||
};
|
||||
const yPos = (position) => {
|
||||
const resizePos = position.yPosition;
|
||||
|
||||
if (position.isMobile && position.scale) {
|
||||
if (position.IsResize) {
|
||||
const y = resizePos * position.scale;
|
||||
return y;
|
||||
if (position.ImageType && position.ImageType === "image/jpeg") {
|
||||
img = await pdfDoc.embedJpg(images[id]);
|
||||
} else {
|
||||
const y = resizePos * position.scale;
|
||||
return y;
|
||||
img = await pdfDoc.embedPng(images[id]);
|
||||
}
|
||||
} else {
|
||||
return resizePos;
|
||||
}
|
||||
};
|
||||
const color = position?.options?.fontColor;
|
||||
const updateColorInRgb = getWidgetsFontColor(color);
|
||||
const fontSize = parseInt(position?.options?.fontSize || 13);
|
||||
const widgetTypeExist = [
|
||||
textWidget,
|
||||
textInputWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"date",
|
||||
"email"
|
||||
].includes(position.type);
|
||||
if (position.type === "checkbox") {
|
||||
let checkboxGapFromTop, isCheck;
|
||||
let y = yPos(position);
|
||||
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();
|
||||
|
||||
if (
|
||||
position?.options?.response &&
|
||||
position?.options?.response?.length > 0
|
||||
) {
|
||||
isCheck = position?.options?.response?.includes(ind);
|
||||
} else if (position?.options?.defaultValue) {
|
||||
isCheck = position?.options?.defaultValue?.includes(ind);
|
||||
}
|
||||
|
||||
const checkbox = form.createCheckBox(checkboxRandomId);
|
||||
|
||||
if (ind > 0) {
|
||||
y = y + checkboxGapFromTop;
|
||||
} else {
|
||||
checkboxGapFromTop = fontSize + 5 || 26;
|
||||
}
|
||||
|
||||
if (!position?.options?.isHideLabel) {
|
||||
// below line of code is used to embed label with radio button in pdf
|
||||
const optionsPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
xPos(position) + checkboxTextGapFromLeft,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
optionsFontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
);
|
||||
page.drawText(item, optionsPosition);
|
||||
}
|
||||
let checkboxObj = {
|
||||
x: xPos(position),
|
||||
y: y,
|
||||
width: checkboxSize,
|
||||
height: checkboxSize
|
||||
};
|
||||
checkboxObj = getWidgetPosition(page, checkboxObj, 1);
|
||||
checkbox.addToPage(page, checkboxObj);
|
||||
|
||||
//applied which checkbox should be checked
|
||||
if (isCheck) {
|
||||
checkbox.check();
|
||||
} else {
|
||||
checkbox.uncheck();
|
||||
}
|
||||
checkbox.enableReadOnly();
|
||||
});
|
||||
}
|
||||
} else if (widgetTypeExist) {
|
||||
const fontSize = calculateFontSize(
|
||||
position,
|
||||
containerScale,
|
||||
signyourself
|
||||
);
|
||||
parseInt(fontSize);
|
||||
let textContent;
|
||||
if (position?.options?.response) {
|
||||
textContent = position.options?.response;
|
||||
} else if (position?.options?.defaultValue) {
|
||||
textContent = position?.options?.defaultValue;
|
||||
}
|
||||
const fixedWidth = widgetWidth; // Set your fixed width
|
||||
const isNewOnEnterLineExist = textContent.includes("\n");
|
||||
|
||||
// Function to break text into lines based on the fixed width
|
||||
const NewbreakTextIntoLines = (textContent, width) => {
|
||||
const lines = [];
|
||||
let currentLine = "";
|
||||
|
||||
for (const word of textContent.split(" ")) {
|
||||
//get text line width
|
||||
const lineWidth = font.widthOfTextAtSize(
|
||||
`${currentLine} ${word}`,
|
||||
fontSize
|
||||
);
|
||||
//check text content line width is less or equal to container width
|
||||
if (lineWidth <= width) {
|
||||
currentLine += ` ${word}`;
|
||||
} else {
|
||||
lines.push(currentLine.trim());
|
||||
currentLine = `${word}`;
|
||||
}
|
||||
} else if (!position.type) {
|
||||
// to handle old widget when only stamp and signature are exists
|
||||
if (position.ImageType && position.ImageType === "image/jpeg") {
|
||||
img = await pdfDoc.embedJpg(images[id]);
|
||||
} else {
|
||||
img = await pdfDoc.embedPng(images[id]);
|
||||
}
|
||||
}
|
||||
let widgetWidth, widgetHeight;
|
||||
widgetWidth = placeholderWidth(position);
|
||||
widgetHeight = placeholderHeight(position);
|
||||
const xPos = (position) => {
|
||||
const resizePos = position.xPosition;
|
||||
//first two condition handle to old data already saved from mobile view which scale point diffrent
|
||||
if (isMobile && position.isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
const x = resizePos * (position.scale / scale);
|
||||
return x * scale;
|
||||
} else if (position.isMobile && position.scale) {
|
||||
const x = resizePos * position.scale;
|
||||
return x;
|
||||
} else {
|
||||
return resizePos;
|
||||
}
|
||||
lines.push(currentLine.trim());
|
||||
return lines;
|
||||
};
|
||||
const yPos = (position) => {
|
||||
const resizePos = position.yPosition;
|
||||
|
||||
// Function to break text into lines based on when user go next line on press enter button
|
||||
const breakTextIntoLines = (textContent, width) => {
|
||||
const lines = [];
|
||||
|
||||
for (const word of textContent.split("\n")) {
|
||||
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
|
||||
//checking string length to container width
|
||||
//if string length is less then container width it means user press enter button
|
||||
if (lineWidth <= width) {
|
||||
lines.push(word);
|
||||
}
|
||||
//else adjust text content according to width and send it in new line
|
||||
else {
|
||||
const newLine = NewbreakTextIntoLines(word, width);
|
||||
lines.push(...newLine);
|
||||
if (position.isMobile && position.scale) {
|
||||
if (position.IsResize) {
|
||||
const y = resizePos * position.scale;
|
||||
return y;
|
||||
} else {
|
||||
const y = resizePos * position.scale;
|
||||
return y;
|
||||
}
|
||||
} else {
|
||||
return resizePos;
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
const color = position?.options?.fontColor;
|
||||
const updateColorInRgb = getWidgetsFontColor(color);
|
||||
const fontSize = parseInt(position?.options?.fontSize || 12);
|
||||
const widgetTypeExist = [
|
||||
textWidget,
|
||||
textInputWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"date",
|
||||
"email"
|
||||
].includes(position.type);
|
||||
if (position.type === "checkbox") {
|
||||
let checkboxGapFromTop, isCheck;
|
||||
let y = yPos(position);
|
||||
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();
|
||||
|
||||
//check if text content have `\n` string it means user press enter to go next line and handle condition
|
||||
//else auto adjust text content according to container width
|
||||
const lines = isNewOnEnterLineExist
|
||||
? breakTextIntoLines(textContent, fixedWidth)
|
||||
: NewbreakTextIntoLines(textContent, fixedWidth);
|
||||
// Set initial y-coordinate for the first line
|
||||
let x = xPos(position);
|
||||
let y = yPos(position);
|
||||
// Embed each line on the page
|
||||
for (const line of lines) {
|
||||
const textPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
x,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
fontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
if (
|
||||
position?.options?.response &&
|
||||
position?.options?.response?.length > 0
|
||||
) {
|
||||
isCheck = position?.options?.response?.includes(ind);
|
||||
} else if (position?.options?.defaultValue) {
|
||||
isCheck = position?.options?.defaultValue?.includes(ind);
|
||||
}
|
||||
|
||||
const checkbox = form.createCheckBox(checkboxRandomId);
|
||||
|
||||
if (ind > 0) {
|
||||
y = y + checkboxGapFromTop;
|
||||
} else {
|
||||
checkboxGapFromTop = fontSize + 5 || 26;
|
||||
}
|
||||
|
||||
if (!position?.options?.isHideLabel) {
|
||||
// below line of code is used to embed label with radio button in pdf
|
||||
const optionsPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
xPos(position) + checkboxTextGapFromLeft,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
optionsFontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
);
|
||||
page.drawText(item, optionsPosition);
|
||||
}
|
||||
let checkboxObj = {
|
||||
x: xPos(position),
|
||||
y: y,
|
||||
width: checkboxSize,
|
||||
height: checkboxSize
|
||||
};
|
||||
checkboxObj = getWidgetPosition(page, checkboxObj, 1);
|
||||
checkbox.addToPage(page, checkboxObj);
|
||||
|
||||
//applied which checkbox should be checked
|
||||
if (isCheck) {
|
||||
checkbox.check();
|
||||
} else {
|
||||
checkbox.uncheck();
|
||||
}
|
||||
checkbox.enableReadOnly();
|
||||
});
|
||||
}
|
||||
} else if (widgetTypeExist) {
|
||||
const fontSize = calculateFontSize(
|
||||
position,
|
||||
containerScale,
|
||||
signyourself
|
||||
);
|
||||
page.drawText(line, textPosition);
|
||||
y += 18; // Adjust the line height as needed
|
||||
}
|
||||
} else if (position.type === "dropdown") {
|
||||
const fontsize = parseInt(position?.options?.fontSize) || 12;
|
||||
parseInt(fontSize);
|
||||
let textContent;
|
||||
if (position?.options?.response) {
|
||||
textContent = position.options?.response;
|
||||
} else if (position?.options?.defaultValue) {
|
||||
textContent = position?.options?.defaultValue;
|
||||
}
|
||||
const fixedWidth = widgetWidth; // Set your fixed width
|
||||
const isNewOnEnterLineExist = textContent.includes("\n");
|
||||
|
||||
const dropdownRandomId = "dropdown" + randomId();
|
||||
const dropdown = form.createDropdown(dropdownRandomId);
|
||||
dropdown.addOptions(position?.options?.values);
|
||||
if (position?.options?.response) {
|
||||
dropdown.select(position.options?.response);
|
||||
} 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 = getWidgetPosition(page, dropdownObj, 1);
|
||||
const dropdownSelected = { ...dropdownOption, font: font };
|
||||
dropdown.defaultUpdateAppearances(font);
|
||||
dropdown.addToPage(page, dropdownSelected);
|
||||
dropdown.enableReadOnly();
|
||||
} else if (position.type === radioButtonWidget) {
|
||||
const radioRandomId = "radio" + randomId();
|
||||
const radioGroup = form.createRadioGroup(radioRandomId);
|
||||
let radioOptionGapFromTop;
|
||||
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 = fontSize + 10 || 25;
|
||||
}
|
||||
if (!position?.options?.isHideLabel) {
|
||||
// below line of code is used to embed label with radio button in pdf
|
||||
// Function to break text into lines based on the fixed width
|
||||
const NewbreakTextIntoLines = (textContent, width) => {
|
||||
const lines = [];
|
||||
let currentLine = "";
|
||||
|
||||
const optionsPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
xPos(position) + radioTextGapFromLeft,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
optionsFontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
for (const word of textContent.split(" ")) {
|
||||
//get text line width
|
||||
const lineWidth = font.widthOfTextAtSize(
|
||||
`${currentLine} ${word}`,
|
||||
fontSize
|
||||
);
|
||||
|
||||
page.drawText(item, optionsPosition);
|
||||
//check text content line width is less or equal to container width
|
||||
if (lineWidth <= width) {
|
||||
currentLine += ` ${word}`;
|
||||
} else {
|
||||
lines.push(currentLine.trim());
|
||||
currentLine = `${word}`;
|
||||
}
|
||||
}
|
||||
let radioObj = {
|
||||
x: xPos(position),
|
||||
y: y,
|
||||
width: radioSize,
|
||||
height: radioSize
|
||||
};
|
||||
lines.push(currentLine.trim());
|
||||
return lines;
|
||||
};
|
||||
|
||||
radioObj = getWidgetPosition(page, radioObj, 1);
|
||||
radioGroup.addOptionToPage(item, page, radioObj);
|
||||
});
|
||||
}
|
||||
if (position?.options?.response) {
|
||||
radioGroup.select(position.options?.response);
|
||||
} else if (position?.options?.defaultValue) {
|
||||
radioGroup.select(position?.options?.defaultValue);
|
||||
}
|
||||
radioGroup.enableReadOnly();
|
||||
} else {
|
||||
const signature = {
|
||||
x: xPos(position),
|
||||
y: yPos(position),
|
||||
width: widgetWidth,
|
||||
height: widgetHeight
|
||||
};
|
||||
// Function to break text into lines based on when user go next line on press enter button
|
||||
const breakTextIntoLines = (textContent, width) => {
|
||||
const lines = [];
|
||||
|
||||
const imageOptions = getWidgetPosition(page, signature, 1);
|
||||
page.drawImage(img, imageOptions);
|
||||
for (const word of textContent.split("\n")) {
|
||||
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
|
||||
//checking string length to container width
|
||||
//if string length is less then container width it means user press enter button
|
||||
if (lineWidth <= width) {
|
||||
lines.push(word);
|
||||
}
|
||||
//else adjust text content according to width and send it in new line
|
||||
else {
|
||||
const newLine = NewbreakTextIntoLines(word, width);
|
||||
lines.push(...newLine);
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
//check if text content have `\n` string it means user press enter to go next line and handle condition
|
||||
//else auto adjust text content according to container width
|
||||
const lines = isNewOnEnterLineExist
|
||||
? breakTextIntoLines(textContent, fixedWidth)
|
||||
: NewbreakTextIntoLines(textContent, fixedWidth);
|
||||
// Set initial y-coordinate for the first line
|
||||
let x = xPos(position);
|
||||
let y = yPos(position);
|
||||
// Embed each line on the page
|
||||
for (const line of lines) {
|
||||
const textPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
x,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
fontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
);
|
||||
page.drawText(line, textPosition);
|
||||
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);
|
||||
if (position?.options?.response) {
|
||||
dropdown.select(position.options?.response);
|
||||
} 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 = getWidgetPosition(page, dropdownObj, 1);
|
||||
const dropdownSelected = { ...dropdownOption, font: font };
|
||||
dropdown.defaultUpdateAppearances(font);
|
||||
dropdown.addToPage(page, dropdownSelected);
|
||||
dropdown.enableReadOnly();
|
||||
} else if (position.type === radioButtonWidget) {
|
||||
const radioRandomId = "radio" + randomId();
|
||||
const radioGroup = form.createRadioGroup(radioRandomId);
|
||||
let radioOptionGapFromTop;
|
||||
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 = fontSize + 10 || 25;
|
||||
}
|
||||
if (!position?.options?.isHideLabel) {
|
||||
// below line of code is used to embed label with radio button in pdf
|
||||
|
||||
const optionsPosition = compensateRotation(
|
||||
page.getRotation().angle,
|
||||
xPos(position) + radioTextGapFromLeft,
|
||||
y,
|
||||
1,
|
||||
page.getSize(),
|
||||
optionsFontSize,
|
||||
updateColorInRgb,
|
||||
font,
|
||||
page
|
||||
);
|
||||
|
||||
page.drawText(item, optionsPosition);
|
||||
}
|
||||
let radioObj = {
|
||||
x: xPos(position),
|
||||
y: y,
|
||||
width: radioSize,
|
||||
height: radioSize
|
||||
};
|
||||
|
||||
radioObj = getWidgetPosition(page, radioObj, 1);
|
||||
radioGroup.addOptionToPage(item, page, radioObj);
|
||||
});
|
||||
}
|
||||
if (position?.options?.response) {
|
||||
radioGroup.select(position.options?.response);
|
||||
} else if (position?.options?.defaultValue) {
|
||||
radioGroup.select(position?.options?.defaultValue);
|
||||
}
|
||||
radioGroup.enableReadOnly();
|
||||
} else {
|
||||
const signature = {
|
||||
x: xPos(position),
|
||||
y: yPos(position),
|
||||
width: widgetWidth,
|
||||
height: widgetHeight
|
||||
};
|
||||
|
||||
const imageOptions = getWidgetPosition(page, signature, 1);
|
||||
page.drawImage(img, imageOptions);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in embed widget on page ", pageNo, err?.message);
|
||||
hasError = true; // Set the flag to stop both loops
|
||||
break; // Exit inner loop
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!hasError) {
|
||||
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
|
||||
// console.log("pdf", pdfBytes);
|
||||
return pdfBytes;
|
||||
} else {
|
||||
return {
|
||||
error:
|
||||
"This pdf is not compatible with opensign please contact <support@opensignlabs.com>"
|
||||
};
|
||||
}
|
||||
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
|
||||
//console.log("pdf", pdfBytes);
|
||||
return pdfBytes;
|
||||
};
|
||||
|
||||
// function for validating URLs
|
||||
@@ -1741,9 +1765,7 @@ export const contactBook = async (objectId) => {
|
||||
|
||||
//function for getting document details from contract_Documents class
|
||||
export const contractDocument = async (documentId) => {
|
||||
const data = {
|
||||
docId: documentId
|
||||
};
|
||||
const data = { docId: documentId };
|
||||
const documentDeatils = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/getDocument`, data, {
|
||||
headers: {
|
||||
@@ -1936,7 +1958,11 @@ export const getAppLogo = async () => {
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getlogo ", err);
|
||||
return { logo: appInfo.applogo, user: "exist" };
|
||||
if (err?.message?.includes("valid JSON")) {
|
||||
return { logo: appInfo.applogo, user: "exist", error: "invalid_json" };
|
||||
} else {
|
||||
return { logo: appInfo.applogo, user: "exist" };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2039,30 +2065,45 @@ export const fetchUrl = async (url, pdfName) => {
|
||||
console.error("Error downloading the file:", error);
|
||||
}
|
||||
};
|
||||
//handle download signed pdf
|
||||
export const handleDownloadPdf = async (
|
||||
pdfDetails,
|
||||
export const getSignedUrl = async (
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
docId,
|
||||
fileAdapterId,
|
||||
templateId
|
||||
) => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
setIsDownloading("pdf");
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
//use only axios here due to public template sign
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{
|
||||
url: pdfUrl,
|
||||
docId: docId || "",
|
||||
fileAdapterId: fileAdapterId,
|
||||
templateId: templateId || ""
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
}
|
||||
);
|
||||
const url = axiosRes.data.result;
|
||||
return url;
|
||||
};
|
||||
//handle download signed pdf
|
||||
export const handleDownloadPdf = async (pdfDetails, setIsDownloading) => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0]?.Name;
|
||||
const pdfUrl = pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL;
|
||||
setIsDownloading && setIsDownloading("pdf");
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const fileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
try {
|
||||
const url = await getSignedUrl(pdfUrl, docId, fileAdapterId);
|
||||
await fetchUrl(url, pdfName);
|
||||
setIsDownloading("");
|
||||
setIsDownloading && setIsDownloading("");
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
setIsDownloading("");
|
||||
@@ -2075,16 +2116,25 @@ export const sanitizeFileName = (pdfName) => {
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
//function for print digital sign pdf
|
||||
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||
export const handleToPrint = async (
|
||||
event,
|
||||
pdfUrl,
|
||||
setIsDownloading,
|
||||
pdfDetails
|
||||
) => {
|
||||
event.preventDefault();
|
||||
setIsDownloading("pdf");
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const FileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
//`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign
|
||||
//if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl },
|
||||
{ url: pdfUrl, docId: docId, fileAdapterId: FileAdapterId },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
@@ -2124,13 +2174,18 @@ export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||
//handle download signed pdf
|
||||
export const handleDownloadCertificate = async (
|
||||
pdfDetails,
|
||||
setIsDownloading
|
||||
setIsDownloading,
|
||||
isZip
|
||||
) => {
|
||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||
try {
|
||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
if (isZip) {
|
||||
return certificateUrl;
|
||||
} else {
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
}
|
||||
@@ -2156,7 +2211,13 @@ export const handleDownloadCertificate = async (
|
||||
if (doc?.CertificateUrl) {
|
||||
await fetch(doc?.CertificateUrl);
|
||||
const certificateUrl = doc?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
if (isZip) {
|
||||
setIsDownloading("");
|
||||
return certificateUrl;
|
||||
} else {
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
}
|
||||
|
||||
setIsDownloading("");
|
||||
} else {
|
||||
setIsDownloading("certificate");
|
||||
@@ -2456,3 +2517,45 @@ export const handleRotateWarning = (signerPos, pageNumber) => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// `generateTitleFromFilename` to generate Title of document from file name
|
||||
export function generateTitleFromFilename(filename) {
|
||||
try {
|
||||
// Step 1: Trim whitespace
|
||||
let title = filename.trim();
|
||||
|
||||
// Step 2: Remove the file extension (everything after the last '.')
|
||||
const lastDotIndex = title.lastIndexOf(".");
|
||||
if (lastDotIndex > 0) {
|
||||
title = title.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
// Step 3: Replace special characters (except Unicode letters, digits, spaces, and hyphens)
|
||||
title = title.replace(/[^\p{L}\p{N}\s-]/gu, " ");
|
||||
|
||||
// Step 4: Replace multiple spaces with a single space
|
||||
title = title.replace(/\s+/g, " ");
|
||||
|
||||
// Step 5: Capitalize first letter of each word (Title Case), handling Unicode characters
|
||||
title = title.replace(
|
||||
/\p{L}\S*/gu,
|
||||
(txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||
);
|
||||
|
||||
// Step 6: Restrict length of title (optional, let's say 100 characters)
|
||||
if (title.length > 100) {
|
||||
title = title.substring(0, 100).trim();
|
||||
}
|
||||
|
||||
// Step 7: Handle empty or invalid title by falling back to "Untitled Document"
|
||||
if (!title || title.length === 0) {
|
||||
return "Untitled Document";
|
||||
}
|
||||
|
||||
return title;
|
||||
} catch (error) {
|
||||
// Handle unexpected errors gracefully by returning a default title
|
||||
console.error("Error generating title from filename:", error);
|
||||
return "Untitled Document";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ i18n
|
||||
interpolation: {
|
||||
escapeValue: false // Not needed for react as it escapes by default
|
||||
},
|
||||
whitelist: ["en", "fr"] // List of allowed languages
|
||||
whitelist: ["en", "es", "fr"] // List of allowed languages
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
|
||||
export default function reportJson(id) {
|
||||
// console.log("json ", json);
|
||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
@@ -10,9 +12,43 @@ export default function reportJson(id) {
|
||||
"Owner",
|
||||
"Signers"
|
||||
];
|
||||
const iphead = [
|
||||
"Sr.No",
|
||||
"Title",
|
||||
"Note",
|
||||
"Folder",
|
||||
"File",
|
||||
"Status",
|
||||
"Signers"
|
||||
];
|
||||
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||
const templateSubAction = isEnableSubscription
|
||||
? [
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Embed",
|
||||
hoverLabel: "Embed",
|
||||
btnIcon: "fa-light fa-code",
|
||||
action: "Embed"
|
||||
},
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Copy TemplateId",
|
||||
hoverLabel: "Copy TemplateId",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
action: "CopyTemplateId"
|
||||
},
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Copy Public URL",
|
||||
hoverLabel: "Copy Public URL",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
action: "CopyPublicURL"
|
||||
}
|
||||
]
|
||||
: [];
|
||||
switch (id) {
|
||||
// draft documents report
|
||||
case "ByHuevtCFY":
|
||||
@@ -63,7 +99,7 @@ export default function reportJson(id) {
|
||||
case "1MwEuxLEkF":
|
||||
return {
|
||||
reportName: "In-progress documents",
|
||||
heading: head,
|
||||
heading: iphead,
|
||||
actions: [
|
||||
{
|
||||
btnId: "8901",
|
||||
@@ -354,13 +390,7 @@ export default function reportJson(id) {
|
||||
redirectUrl: "template",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Embed",
|
||||
hoverLabel: "Embed",
|
||||
btnIcon: "fa-light fa-code",
|
||||
action: "Embed"
|
||||
},
|
||||
...templateSubAction,
|
||||
{
|
||||
btnId: "1834",
|
||||
btnLabel: "Delete",
|
||||
@@ -382,18 +412,15 @@ export default function reportJson(id) {
|
||||
if (item.action === "option") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
newItem.subaction = [
|
||||
{
|
||||
btnId: "1873",
|
||||
btnLabel: "Share with team",
|
||||
hoverLabel: "Share with team",
|
||||
btnIcon: "fa-light fa-share-nodes",
|
||||
redirectUrl: "",
|
||||
action: "sharewith"
|
||||
},
|
||||
...newItem.subaction
|
||||
];
|
||||
|
||||
//splice method used to add `Share with team` option on second index of list
|
||||
newItem.subaction.splice(1, 0, {
|
||||
btnId: "1873",
|
||||
btnLabel: "Share with team",
|
||||
hoverLabel: "Share with team",
|
||||
btnIcon: "fa-light fa-share-nodes",
|
||||
redirectUrl: "",
|
||||
action: "sharewith"
|
||||
});
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
|
||||
@@ -224,23 +224,13 @@ const HomeLayout = () => {
|
||||
|
||||
async function checkTourStatus() {
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails");
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
// console.log("res ", res.data.TourStatus);
|
||||
if (cloudRes) {
|
||||
const extUser = JSON.parse(JSON.stringify(cloudRes));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||
const tourStatus = extUser?.TourStatus || [];
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter((obj) => obj["loginTour"]);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const loginTour = filteredtourStatus[0]["loginTour"];
|
||||
// console.log("loginTour", loginTour);
|
||||
if (loginTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
const loginTour = tourStatus.find((obj) => obj.loginTour)?.loginTour;
|
||||
setIsTour(!loginTour);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
@@ -252,9 +242,9 @@ const HomeLayout = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
const handleLoginBtn = async () => {
|
||||
try {
|
||||
Parse?.User?.logOut();
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
|
||||
@@ -45,8 +45,10 @@ const AddAdmin = () => {
|
||||
setState((prev) => ({ ...prev, loading: true }));
|
||||
try {
|
||||
const app = await getAppLogo();
|
||||
if (app?.user === "exist") {
|
||||
setErrMsg(t("admin-already-exist"));
|
||||
if (app?.error === "invalid_json") {
|
||||
setErrMsg(t("server-down"));
|
||||
} else if (app?.user === "exist") {
|
||||
setErrMsg(t("admin-exists"));
|
||||
}
|
||||
} catch (err) {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
|
||||
@@ -74,7 +74,7 @@ const DebugPdf = () => {
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(2);
|
||||
const firstPage = await pdf.getPage(pdf?.numPages > 1 ? 2 : 1);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
|
||||
+796
-263
File diff suppressed because it is too large
Load Diff
@@ -28,7 +28,6 @@ function GenerateToken() {
|
||||
plan: "",
|
||||
isValid: false,
|
||||
adminId: "",
|
||||
adminName: "",
|
||||
adminEmail: ""
|
||||
});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
@@ -154,8 +153,7 @@ function GenerateToken() {
|
||||
} else {
|
||||
setIsSubscribe((obj) => ({
|
||||
...obj,
|
||||
adminName: extUser?.CreatedBy?.name,
|
||||
adminEmail: extUser?.CreatedBy?.email
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
setIsModal((obj) => ({ ...obj, unauthorized: !obj.unauthorized }));
|
||||
}
|
||||
@@ -447,7 +445,6 @@ function GenerateToken() {
|
||||
>
|
||||
<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>
|
||||
|
||||
@@ -28,6 +28,28 @@ function GuestLogin() {
|
||||
const [contactId, setContactId] = useState(contactBookId);
|
||||
const [sendmail, setSendmail] = useState();
|
||||
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||
const navigateToDoc = async (docId, contactId) => {
|
||||
try {
|
||||
const docDetails = await Parse.Cloud.run("getDocument", {
|
||||
docId: docId
|
||||
});
|
||||
if (!docDetails.error) {
|
||||
if (sendmail === "false") {
|
||||
navigate(
|
||||
`/load/recipientSignPdf/${docId}/${contactId}?sendmail=${sendmail}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/load/recipientSignPdf/${docId}/${contactId}`);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting doc", err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
handleServerUrl();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -70,13 +92,14 @@ function GuestLogin() {
|
||||
"linkcontacttodoc",
|
||||
params
|
||||
);
|
||||
// console.log("linkContactRes ", linkContactRes);
|
||||
setContactId(linkContactRes?.contactId);
|
||||
await navigateToDoc(checkSplit[0], linkContactRes?.contactId);
|
||||
} catch (err) {
|
||||
console.log("Err in link ext contact", err);
|
||||
}
|
||||
} else {
|
||||
setContactId(checkSplit[2]);
|
||||
await navigateToDoc(checkSplit[0], checkSplit[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,15 +137,12 @@ function GuestLogin() {
|
||||
if (OTP) {
|
||||
setLoading(true);
|
||||
try {
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail/`;
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email,
|
||||
otp: OTP
|
||||
};
|
||||
let body = { email: email, otp: OTP };
|
||||
let user = await axios.post(url, body, { headers: headers });
|
||||
if (user.data.result === "Invalid Otp") {
|
||||
alert(t("invalid-otp"));
|
||||
@@ -140,7 +160,6 @@ function GuestLogin() {
|
||||
`Parse/${parseId}/currentUser`,
|
||||
JSON.stringify(_user)
|
||||
);
|
||||
// console.log("contractUserDetails ", contractUserDetails);
|
||||
if (contractUserDetails && contractUserDetails.length > 0) {
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
@@ -174,10 +193,15 @@ function GuestLogin() {
|
||||
try {
|
||||
setLoading(true);
|
||||
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
|
||||
// console.log("linkContactRes ", linkContactRes);
|
||||
setContactId(linkContactRes.contactId);
|
||||
setEnterOtp(true);
|
||||
await SendOtp();
|
||||
const IsEnableOTP = await navigateToDoc(
|
||||
documentId,
|
||||
linkContactRes.contactId
|
||||
);
|
||||
if (!IsEnableOTP) {
|
||||
setEnterOtp(true);
|
||||
await SendOtp();
|
||||
}
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
@@ -208,7 +232,7 @@ function GuestLogin() {
|
||||
<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")}
|
||||
{t("get-otp-alert")}
|
||||
</legend>
|
||||
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||
<input
|
||||
@@ -236,7 +260,7 @@ function GuestLogin() {
|
||||
>
|
||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2">
|
||||
{t("get-verification-code-2")}
|
||||
{t("guest-email-alert")}
|
||||
</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>
|
||||
|
||||
@@ -51,6 +51,7 @@ function Login() {
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [image, setImage] = useState();
|
||||
const [isLoginSSO, setIsLoginSSO] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExt();
|
||||
@@ -59,7 +60,9 @@ function Login() {
|
||||
|
||||
const checkUserExt = async () => {
|
||||
const app = await getAppLogo();
|
||||
if (!isEnableSubscription && app?.user === "not_exist") {
|
||||
if (app?.error === "invalid_json") {
|
||||
setErrMsg(t("server-down"));
|
||||
} else if (!isEnableSubscription && app?.user === "not_exist") {
|
||||
navigate("/addadmin");
|
||||
}
|
||||
if (app?.logo) {
|
||||
@@ -620,7 +623,11 @@ function Login() {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
return errMsg ? (
|
||||
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Title title={"Login Page"} />
|
||||
{state.loading && (
|
||||
|
||||
@@ -2,12 +2,13 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import "../styles/managesign.css";
|
||||
import "../styles/signature.css";
|
||||
import { toDataUrl } from "../constant/Utils";
|
||||
import { generateTitleFromFilename, toDataUrl } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||
const ManageSign = () => {
|
||||
const { t } = useTranslation();
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
@@ -23,9 +24,11 @@ const ManageSign = () => {
|
||||
const [Initials, setInitials] = useState("");
|
||||
const [isInitials, setIsInitials] = useState(false);
|
||||
const [id, setId] = useState("");
|
||||
const [imgInitials, setImgInitials] = useState("");
|
||||
const canvasRef = useRef(null);
|
||||
const imageRef = useRef(null);
|
||||
const initailsRef = useRef(null);
|
||||
const imgInitialsRef = useRef(null);
|
||||
useEffect(() => {
|
||||
fetchUserSign();
|
||||
// eslint-disable-next-line
|
||||
@@ -46,14 +49,23 @@ const ManageSign = () => {
|
||||
if (signRes) {
|
||||
const res = signRes.toJSON();
|
||||
setId(res.objectId);
|
||||
setSignName(res?.SignatureName);
|
||||
setImage(res.ImageURL);
|
||||
if (res?.SignatureName) {
|
||||
const sanitizename = generateTitleFromFilename(res?.SignatureName);
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
setSignName(replaceSpace);
|
||||
}
|
||||
setImage(res?.ImageURL);
|
||||
if (res && res.Initials) {
|
||||
setInitials(res.Initials);
|
||||
// setInitials(res.Initials);
|
||||
setIsInitials(true);
|
||||
setImgInitials(res?.Initials);
|
||||
}
|
||||
} else {
|
||||
setSignName(User?.get("name") || "");
|
||||
if (User?.get("name")) {
|
||||
const sanitizename = generateTitleFromFilename(User?.get("name"));
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
setSignName(replaceSpace);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
@@ -86,8 +98,14 @@ const ManageSign = () => {
|
||||
if (initailsRef.current) {
|
||||
initailsRef.current.clear();
|
||||
}
|
||||
if (imgInitialsRef.current) {
|
||||
imgInitialsRef.current.value = "";
|
||||
}
|
||||
setImgInitials("");
|
||||
setInitials("");
|
||||
setIsValue(true);
|
||||
if (image) {
|
||||
setIsValue(true);
|
||||
}
|
||||
setIsInitials(false);
|
||||
};
|
||||
|
||||
@@ -101,40 +119,54 @@ const ManageSign = () => {
|
||||
const base64Img = await toDataUrl(file);
|
||||
setImage(base64Img);
|
||||
setIsValue(true);
|
||||
} else {
|
||||
setImage("");
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const isUrl = image.includes("https");
|
||||
|
||||
const isUrl = image?.includes("https") || image?.includes("http");
|
||||
if (!isvalue) {
|
||||
setWarning(true);
|
||||
setTimeout(() => setWarning(false), 1000);
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
const replaceSpace = signName.replace(/ /g, "_");
|
||||
const sanitizename = generateTitleFromFilename(signName);
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
let file;
|
||||
if (signature) {
|
||||
file = base64StringtoFile(signature, `${replaceSpace}_sign.png`);
|
||||
} else {
|
||||
if (!isUrl) {
|
||||
if (image && !isUrl) {
|
||||
file = base64StringtoFile(image, `${replaceSpace}__sign.png`);
|
||||
}
|
||||
}
|
||||
let imgUrl;
|
||||
if (!isUrl) {
|
||||
if (file && !isUrl) {
|
||||
imgUrl = await uploadFile(file);
|
||||
} else {
|
||||
imgUrl = image;
|
||||
}
|
||||
let initialsUrl = "";
|
||||
const isInitialsUrl = Initials.includes("https");
|
||||
if (!isInitialsUrl && Initials) {
|
||||
const initialsImg = base64StringtoFile(
|
||||
Initials,
|
||||
`${replaceSpace}_initials.png`
|
||||
);
|
||||
initialsUrl = await uploadFile(initialsImg);
|
||||
const isInitialsUrl =
|
||||
imgInitials?.includes("https") || imgInitials?.includes("http");
|
||||
|
||||
let initialFile;
|
||||
if (Initials) {
|
||||
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign.png`);
|
||||
} else {
|
||||
if (imgInitials && !isInitialsUrl) {
|
||||
initialFile = base64StringtoFile(
|
||||
imgInitials,
|
||||
`${replaceSpace}__sign.png`
|
||||
);
|
||||
}
|
||||
}
|
||||
let initialsUrl;
|
||||
if (initialFile && !isInitialsUrl) {
|
||||
initialsUrl = await uploadFile(initialFile);
|
||||
} else {
|
||||
initialsUrl = imgInitials;
|
||||
}
|
||||
if (imgUrl) {
|
||||
await saveEntry({
|
||||
@@ -146,7 +178,7 @@ const ManageSign = () => {
|
||||
}
|
||||
};
|
||||
function base64StringtoFile(base64String, filename) {
|
||||
var arr = base64String.split(","),
|
||||
let arr = base64String.split(","),
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]),
|
||||
n = bstr.length,
|
||||
@@ -175,31 +207,24 @@ const ManageSign = () => {
|
||||
|
||||
const saveEntry = async (obj) => {
|
||||
const signCls = "contracts_Signature";
|
||||
const User = Parse.User.current().id;
|
||||
const User = Parse?.User?.current()?.id;
|
||||
const userId = { __type: "Pointer", className: "_User", objectId: User };
|
||||
if (id) {
|
||||
try {
|
||||
const updateSign = new Parse.Object(signCls);
|
||||
updateSign.id = id;
|
||||
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : "");
|
||||
updateSign.set("ImageURL", obj.url);
|
||||
updateSign.set("ImageURL", obj.url ? obj.url : "");
|
||||
updateSign.set("SignatureName", obj.name);
|
||||
updateSign.set("UserId", userId);
|
||||
const res = await updateSign.save();
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: t("signature-saved-alert")
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setIsAlert({ type: "danger", message: `${err.message}` });
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: `${err.message}`
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
}
|
||||
} else {
|
||||
@@ -210,20 +235,13 @@ const ManageSign = () => {
|
||||
updateSign.set("SignatureName", obj.name);
|
||||
updateSign.set("UserId", userId);
|
||||
const res = await updateSign.save();
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: t("signature-saved-alert")
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setIsAlert({ type: "success", message: `${err.message}` });
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: `${err.message}`
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
}
|
||||
}
|
||||
@@ -234,7 +252,29 @@ const ManageSign = () => {
|
||||
};
|
||||
const handleInitialsChange = () => {
|
||||
setInitials(initailsRef.current.toDataURL());
|
||||
setIsValue(true);
|
||||
if (image || signature) {
|
||||
setIsValue(true);
|
||||
}
|
||||
};
|
||||
const handleUploadInitials = () => {
|
||||
imgInitialsRef.current.click();
|
||||
};
|
||||
const onImgInitialsChange = async (event) => {
|
||||
if (initailsRef.current) {
|
||||
initailsRef.current.clear();
|
||||
}
|
||||
setInitials("");
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
const file = event.target.files[0];
|
||||
const base64Img = await toDataUrl(file);
|
||||
setImgInitials(base64Img);
|
||||
if (image || signature) {
|
||||
setIsValue(true);
|
||||
}
|
||||
} else {
|
||||
setImgInitials("");
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
|
||||
@@ -281,7 +321,7 @@ const ManageSign = () => {
|
||||
height: "180px",
|
||||
className: "signatureCanvas rounded-box"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
@@ -332,7 +372,7 @@ const ManageSign = () => {
|
||||
className="op-link"
|
||||
onClick={() => handleUploadBtn()}
|
||||
>
|
||||
{t("upload-image")}
|
||||
{t("upload")}
|
||||
</div>
|
||||
<div
|
||||
type="button"
|
||||
@@ -356,17 +396,21 @@ const ManageSign = () => {
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
{t("initials")}
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImgInitialsChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
ref={imgInitialsRef}
|
||||
hidden
|
||||
/>
|
||||
<div>
|
||||
{isInitials ? (
|
||||
{imgInitials ? (
|
||||
<div className="intialSignature relative border-[1px] border-[#888] rounded-box overflow-hidden">
|
||||
<img
|
||||
alt="inititals"
|
||||
src={Initials}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
src={imgInitials}
|
||||
className="w-[100%] h-[100%] object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -376,10 +420,8 @@ const ManageSign = () => {
|
||||
canvasProps={{
|
||||
className: "intialSignature rounded-box"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleInitialsChange(initailsRef.current.toDataURL())
|
||||
}
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() => handleInitialsChange()}
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
@@ -402,6 +444,7 @@ const ManageSign = () => {
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
className="fa-light fa-pen-nib"
|
||||
onClick={() => {
|
||||
if (key === 0) {
|
||||
setInitialPen("blue");
|
||||
@@ -412,7 +455,6 @@ const ManageSign = () => {
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa-light fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
@@ -421,13 +463,22 @@ const ManageSign = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex flex-row gap-1">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
onClick={() => handleClearInitials()}
|
||||
onClick={() => handleUploadInitials()}
|
||||
>
|
||||
{t("clear")}
|
||||
{t("upload")}
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
onClick={() => handleClearInitials()}
|
||||
>
|
||||
{t("clear")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -499,22 +499,13 @@ function Opensigndrive() {
|
||||
//function to use check tour status of open sign drive
|
||||
async function checkTourStatus() {
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails");
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
if (cloudRes) {
|
||||
const extUser = JSON.parse(JSON.stringify(cloudRes));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||
const tourStatus = extUser?.TourStatus || [];
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter((obj) => obj["driveTour"]);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const driveTour = filteredtourStatus[0]["driveTour"];
|
||||
// console.log("loginTour", loginTour);
|
||||
if (driveTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
const driveTour = tourStatus.find((obj) => obj.driveTour)?.driveTour;
|
||||
setIsTour(!driveTour);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,6 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||
import Tour from "reactour";
|
||||
import { useLocation, useParams } from "react-router-dom";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import { RWebShare } from "react-web-share";
|
||||
@@ -45,23 +43,25 @@ import {
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import Title from "../components/Title";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { EmailBody } from "../components/pdf/EmailBody";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useSelector } from "react-redux";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import QuotaCard from "../primitives/QuotaCard";
|
||||
import LottieWithLoader from "../primitives/DotLottieReact";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
|
||||
function PlaceHolderSign() {
|
||||
const { t } = useTranslation();
|
||||
@@ -901,7 +901,7 @@ function PlaceHolderSign() {
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (signerPos?.length > 0) {
|
||||
if (signerPos?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
@@ -1047,7 +1047,7 @@ function PlaceHolderSign() {
|
||||
type="button"
|
||||
className="flex flex-row items-center op-link op-link-primary"
|
||||
>
|
||||
<i className="fa-light fa-link" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-copy" />
|
||||
<span className=" hidden md:block ml-1 ">{t("copy-link")}</span>
|
||||
</button>
|
||||
<RWebShare data={{ url: data.url, title: t("sign-url") }}>
|
||||
@@ -1121,7 +1121,7 @@ function PlaceHolderSign() {
|
||||
receiver_phone: signerMail[i]?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
};
|
||||
replaceVar = replaceMailVaribles(
|
||||
requestSubject,
|
||||
@@ -1155,7 +1155,7 @@ function PlaceHolderSign() {
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
senderEmail +
|
||||
@@ -1250,7 +1250,7 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="addWidgets"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-3")}
|
||||
message={t("tour-mssg.placeholder-sign-4")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
@@ -1261,19 +1261,18 @@ function PlaceHolderSign() {
|
||||
selector: '[data-tut="pdfArea"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-4")}
|
||||
message={t("tour-mssg.placeholder-sign-5")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
|
||||
{
|
||||
selector: '[data-tut="headerArea"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.placeholder-sign-5")}
|
||||
message={t("tour-mssg.placeholder-sign-6")}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
@@ -2059,6 +2058,7 @@ function PlaceHolderSign() {
|
||||
setSignerPos={setSignerPos}
|
||||
containerWH={containerWH}
|
||||
setIsResize={setIsResize}
|
||||
isResize={isResize}
|
||||
setZIndex={setZIndex}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
signersdata={signersdata}
|
||||
|
||||
@@ -121,9 +121,9 @@ const PlanSubscriptions = () => {
|
||||
};
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
Parse?.User?.logOut();
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
|
||||
@@ -4,7 +4,6 @@ import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import Confetti from "react-confetti";
|
||||
import axios from "axios";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
@@ -42,21 +41,23 @@ import {
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
import Signedby from "../components/pdf/Signedby";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import { useSelector } from "react-redux";
|
||||
import TextFontSetting from "../components/pdf/TextFontSetting";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
const { t } = useTranslation();
|
||||
@@ -130,6 +131,8 @@ function SignYourSelf() {
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const [isResize, setIsResize] = useState(false);
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -556,7 +559,7 @@ function SignYourSelf() {
|
||||
};
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (xyPostion?.length > 0) {
|
||||
if (xyPostion?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
@@ -615,12 +618,8 @@ function SignYourSelf() {
|
||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||
let checkDefaultSigned =
|
||||
requiredWidgets[i]?.options?.defaultValue;
|
||||
if (!checkSignUrl) {
|
||||
if (!checkDefaultSigned) {
|
||||
if (!showAlert) {
|
||||
showAlert = true;
|
||||
}
|
||||
}
|
||||
if (!checkSignUrl && !checkDefaultSigned && !showAlert) {
|
||||
showAlert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -647,10 +646,9 @@ function SignYourSelf() {
|
||||
return;
|
||||
} else {
|
||||
setIsUiLoading(true);
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
try {
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
|
||||
const isSignYourSelfFlow = true;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
@@ -669,8 +667,15 @@ function SignYourSelf() {
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
if (pdfBytes) {
|
||||
if (!pdfBytes?.error) {
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
setIsAlert({
|
||||
header: t("error"),
|
||||
isShow: true,
|
||||
alertMessage: t("pdf-uncompatible")
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setIsUiLoading(false);
|
||||
@@ -781,7 +786,7 @@ function SignYourSelf() {
|
||||
const handleStop = (event, dragElement) => {
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
if (isDragging && dragElement) {
|
||||
if (!isResize && isDragging && dragElement) {
|
||||
event.preventDefault();
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
@@ -1190,7 +1195,12 @@ function SignYourSelf() {
|
||||
)}
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
|
||||
@@ -1327,6 +1337,7 @@ function SignYourSelf() {
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
activeMailAdapter={activeMailAdapter}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
@@ -1346,6 +1357,7 @@ function SignYourSelf() {
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
widgetsDetails={xyPostion}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
<div ref={divRef} data-tut="reactourSecond" className="h-full">
|
||||
{containerWH?.width && containerWH?.height && (
|
||||
@@ -1391,6 +1403,8 @@ function SignYourSelf() {
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1450,6 +1464,12 @@ function SignYourSelf() {
|
||||
setIsRotate={setIsRotate}
|
||||
handleRemoveWidgets={handleRemoveWidgets}
|
||||
/>
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal}
|
||||
pdfDetails={pdfDetails}
|
||||
isDocId={true}
|
||||
/>
|
||||
<TextFontSetting
|
||||
isTextSetting={isTextSetting}
|
||||
setIsTextSetting={setIsTextSetting}
|
||||
|
||||
@@ -9,8 +9,6 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||
import Tour from "reactour";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
@@ -36,18 +34,21 @@ import {
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import EditTemplate from "../components/pdf/EditTemplate";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import AddRoleModal from "../components/pdf/AddRoleModal";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import Parse from "parse";
|
||||
import { useSelector } from "react-redux";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
|
||||
const TemplatePlaceholder = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -507,7 +508,7 @@ const TemplatePlaceholder = () => {
|
||||
const tourAddRole = [
|
||||
{
|
||||
selector: '[data-tut="reactourAddbtn"]',
|
||||
content: "You need to add a role before you can add fields for it. ",
|
||||
content: t("add-user-template"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
@@ -779,6 +780,8 @@ const TemplatePlaceholder = () => {
|
||||
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
|
||||
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||
NextReminderDate: pdfDetails[0]?.NextReminderDate,
|
||||
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
|
||||
IsTourEnabled: pdfDetails[0]?.IsTourEnabled === true ? true : false,
|
||||
URL: pdfUrl
|
||||
};
|
||||
const updateTemplate = new Parse.Object("contracts_Template");
|
||||
@@ -1597,6 +1600,7 @@ const TemplatePlaceholder = () => {
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ const UpdateExistUserAdmin = () => {
|
||||
const [isAlert, setIsAlert] = useState({ type: "danger", msg: "" });
|
||||
useEffect(() => {
|
||||
checkIsAdminExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const checkIsAdminExist = async () => {
|
||||
@@ -22,7 +23,7 @@ const UpdateExistUserAdmin = () => {
|
||||
const isAdminExist = await Parse.Cloud.run("checkadminexist");
|
||||
if (isAdminExist !== "not_exist") {
|
||||
// console.log("isAdminExist ", isAdminExist);
|
||||
setErrMsg("Admin already exists.");
|
||||
setErrMsg(t("admin-exists"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in checkadminexist", err);
|
||||
@@ -52,7 +53,7 @@ const UpdateExistUserAdmin = () => {
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-already-exist") }));
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
|
||||
} else {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import Tooltip from "../primitives/Tooltip";
|
||||
import AddUser from "../components/AddUser";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import { fetchSubscriptionInfo } from "../constant/Utils";
|
||||
import Title from "../components/Title";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -19,7 +19,11 @@ const UserList = () => {
|
||||
const { t } = useTranslation();
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isModal, setIsModal] = useState({
|
||||
form: false,
|
||||
addseats: false,
|
||||
options: false
|
||||
});
|
||||
const location = useLocation();
|
||||
const isDashboard =
|
||||
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
|
||||
@@ -27,9 +31,16 @@ const UserList = () => {
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
|
||||
const [isSubscribe, setIsSubscribe] = useState({
|
||||
plan: "",
|
||||
isValid: false,
|
||||
priceperUser: 0
|
||||
});
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [formHeader, setFormHeader] = useState(t("add-user"));
|
||||
const [usersCount, setUserCounts] = useState({ allowed: 0, totalAllowed: 0 });
|
||||
const [amount, setAmount] = useState({ quantity: 1, price: 0 });
|
||||
const [isBuyLoader, setIsBuyLoader] = useState(false);
|
||||
const recordperPage = 10;
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
|
||||
@@ -81,7 +92,6 @@ const UserList = () => {
|
||||
pages.push(lastPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
const pageNumbers = getPaginationRange();
|
||||
@@ -96,13 +106,43 @@ const UserList = () => {
|
||||
async function fetchUserList() {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (isEnableSubscription) {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
const subscribe = await fetchSubscriptionInfo();
|
||||
if (subscribe?.plan_code?.includes("team")) {
|
||||
const isSupAdmin =
|
||||
subscribe?.adminId && extUser?.objectId === subscribe?.adminId;
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: true,
|
||||
adminId: subscribe.adminId,
|
||||
priceperUser: subscribe.price,
|
||||
isSuperAdmin: isSupAdmin
|
||||
});
|
||||
setAmount((prev) => ({ ...prev, price: subscribe.price }));
|
||||
try {
|
||||
const res = await Parse.Cloud.run("allowedusers");
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(res),
|
||||
totalAllowed: parseInt(subscribe?.totalAllowedUser) || 0
|
||||
}));
|
||||
} catch (err) {
|
||||
console.log("err while get users", err);
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: false,
|
||||
adminId: subscribe.adminId
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe({ plan: "teams-yearly", isValid: true });
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
|
||||
if (extUser) {
|
||||
const admin =
|
||||
extUser?.UserRole &&
|
||||
@@ -121,14 +161,12 @@ const UserList = () => {
|
||||
console.log("Err in fetch userlist", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
const handleModal = (modalName) => {
|
||||
setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] }));
|
||||
};
|
||||
|
||||
// Change page
|
||||
@@ -146,7 +184,13 @@ const UserList = () => {
|
||||
};
|
||||
|
||||
const handleUserData = (userData) => {
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
if (userData) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: obj?.allowed > 0 ? obj.allowed - 1 : 1
|
||||
}));
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
}
|
||||
};
|
||||
// `formatRow` is used to show data in poper manner like
|
||||
// if data is of array type then it will join array items with ","
|
||||
@@ -162,9 +206,8 @@ const UserList = () => {
|
||||
return "-";
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsActiveModal({});
|
||||
};
|
||||
const handleClose = () => setIsActiveModal({});
|
||||
|
||||
const handleToggleSubmit = async (user) => {
|
||||
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
|
||||
if (index !== -1) {
|
||||
@@ -196,6 +239,49 @@ const UserList = () => {
|
||||
const handleToggleBtn = (user) => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
|
||||
const handleAddOnSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsBuyLoader(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buyaddonusers", {
|
||||
users: parseInt(amount.quantity)
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(amount.quantity),
|
||||
totalAllowed: parseInt(_resAddon.addon)
|
||||
}));
|
||||
setAmount((obj) => ({ ...obj, quantity: 1 }));
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
setIsBuyLoader(false);
|
||||
handleModal("addseats");
|
||||
}
|
||||
};
|
||||
const handlePricePerUser = (e) => {
|
||||
const quantity = e.target.value;
|
||||
const price = e.target?.value > 0 ? isSubscribe.priceperUser * quantity : 0;
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleBuyUsers = (allowed, totalAllowed) => {
|
||||
if (allowed && totalAllowed) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(allowed),
|
||||
totalAllowed: parseInt(totalAllowed)
|
||||
}));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={isAdmin ? "Users" : "Page not found"} />
|
||||
@@ -205,7 +291,7 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
{Object.keys(isActLoader)?.length > 0 && (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
@@ -216,19 +302,46 @@ const UserList = () => {
|
||||
{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">
|
||||
{t("report-name.Users")}
|
||||
{t("report-name.Users")}{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={t("users-from-teams")} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleFormModal()}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="hidden md:flex op-btn op-btn-sm h-[35px] op-btn-primary op-btn-outline text-xs mb-0.5"
|
||||
onClick={() => handleModal("addseats")}
|
||||
>
|
||||
{t("buy-users")}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModal("form")}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="cursor-pointer relative md:hidden mb-0.5"
|
||||
onClick={() => handleModal("options")}
|
||||
>
|
||||
<i className="fa-light fa-ellipsis-vertical fa-xl"></i>
|
||||
{isModal?.options && (
|
||||
<ul className="absolute -right-3 top-auto z-[70] w-max op-menu op-menu-xs shadow bg-base-100 text-base-content rounded-box border">
|
||||
<li onClick={() => handleModal("addseats")}>
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t("buy-users")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={` overflow-x-auto w-full`}>
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
@@ -313,34 +426,42 @@ const UserList = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
<div className="flex flex-row justify-between items-center text-xs font-medium">
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div>
|
||||
{t("available-seats")}: {usersCount.allowed}/
|
||||
{usersCount.totalAllowed}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
@@ -362,17 +483,60 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
isOpen={isModal.form}
|
||||
title={formHeader}
|
||||
handleClose={handleFormModal}
|
||||
handleClose={() => handleModal("form")}
|
||||
>
|
||||
<AddUser
|
||||
setIsAlert={setIsAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={handleFormModal}
|
||||
handleBuyUsers={handleBuyUsers}
|
||||
closePopup={() => handleModal("form")}
|
||||
setFormHeader={setFormHeader}
|
||||
/>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isModal.addseats}
|
||||
title="Add Seats"
|
||||
handleClose={() => handleModal("addseats")}
|
||||
>
|
||||
{isBuyLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleAddOnSubmit} className=" px-3 pb-6 pt-6">
|
||||
<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 * {isSubscribe.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>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
|
||||
@@ -596,7 +596,7 @@ function UserProfile() {
|
||||
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>{" "}
|
||||
<i className="fa-light fa-copy" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -611,7 +611,7 @@ function UserProfile() {
|
||||
<div className="flex flex-row md:items-center gap-1">
|
||||
{editmode || !extendUser?.[0]?.Tagline ? (
|
||||
<input
|
||||
maxLength={40}
|
||||
maxLength={100}
|
||||
onChange={handleOnchangeTagLine}
|
||||
value={tagLine}
|
||||
disabled={!editmode}
|
||||
|
||||
@@ -25,8 +25,8 @@ const Alert = ({ children, type, className }) => {
|
||||
: "z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm"
|
||||
} `}
|
||||
>
|
||||
<div className={`op-alert ${textcolor}`}>
|
||||
<span className="ml-3">{children}</span>
|
||||
<div className={`op-alert ${textcolor} flex justify-center`}>
|
||||
<span className="px-1">{children}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "./ModalUi";
|
||||
import {
|
||||
getSignedUrl,
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
sanitizeFileName
|
||||
} from "../constant/Utils";
|
||||
import Loader from "./Loader";
|
||||
import JSZip from "jszip";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
function DownloadPdfZip(props) {
|
||||
const { t } = useTranslation();
|
||||
const [selectType, setSelectType] = useState(1);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const downloadType = [
|
||||
{ id: 1, label: t("download-pdf") },
|
||||
{ id: 2, label: t("pdf-certificate") }
|
||||
];
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (selectType === 1) {
|
||||
await handleDownloadPdf(props.pdfDetails, setIsDownloading);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
} else if (selectType === 2) {
|
||||
setIsDownloading("pdf");
|
||||
const zip = new JSZip();
|
||||
const pdfDetails = props.pdfDetails;
|
||||
const pdfName = pdfDetails?.[0]?.Name || "Document";
|
||||
const pdfUrl = pdfDetails?.[0]?.SignedUrl || "";
|
||||
|
||||
try {
|
||||
// Fetch the first PDF (Signed Document)
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const fileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
console.log("pdfDetails?.[0] ", pdfDetails?.[0]);
|
||||
const signedUrl = await getSignedUrl(pdfUrl, docId, fileAdapterId);
|
||||
console.log("signedUrl ", signedUrl);
|
||||
const pdf1Response = await fetch(signedUrl);
|
||||
if (!pdf1Response.ok) {
|
||||
throw new Error(`Failed to fetch PDF: ${signedUrl}`);
|
||||
}
|
||||
const pdf1Blob = await pdf1Response.blob();
|
||||
const isZip = true;
|
||||
// Fetch the Certificate (or generate its URL dynamically)
|
||||
const certificateUrl = await handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsDownloading,
|
||||
isZip
|
||||
);
|
||||
const pdf2Response = await fetch(certificateUrl);
|
||||
if (!pdf2Response.ok) {
|
||||
throw new Error(`Failed to fetch certificate PDF: ${certificateUrl}`);
|
||||
}
|
||||
const pdf2Blob = await pdf2Response.blob();
|
||||
// Add files to ZIP
|
||||
zip.file(
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`,
|
||||
pdf1Blob
|
||||
);
|
||||
zip.file("Certificate_signed_by_OpenSign™.pdf", pdf2Blob);
|
||||
|
||||
// Generate the ZIP and trigger download
|
||||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||
saveAs(
|
||||
zipBlob,
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.zip`
|
||||
);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
setIsDownloading("");
|
||||
} catch (error) {
|
||||
console.error("Error creating ZIP file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isDownloadModal}
|
||||
title={t("download-files")}
|
||||
handleClose={() => props.setIsDownloadModal(false)}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
{downloadType.map((data, ind) => {
|
||||
return (
|
||||
<label
|
||||
key={ind}
|
||||
className="flex items-center gap-1 mb-2 cursor-pointer"
|
||||
>
|
||||
<input
|
||||
className="mr-[8px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
value={data.id}
|
||||
onChange={() => setSelectType(data.id)}
|
||||
checked={selectType === data.id}
|
||||
/>
|
||||
{data.label}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
onClick={() => handleDownload()}
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("download")}
|
||||
</button>
|
||||
</div>
|
||||
{isDownloading === "pdf" && (
|
||||
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isDownloading === "certificate"}
|
||||
title={
|
||||
isDownloading === "certificate"
|
||||
? 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>{t("generate-certificate-alert")}</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DownloadPdfZip;
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
checkIsSubscribed,
|
||||
copytoData,
|
||||
fetchUrl,
|
||||
getSignedUrl,
|
||||
replaceMailVaribles
|
||||
} from "../constant/Utils";
|
||||
import Confetti from "react-confetti";
|
||||
@@ -30,6 +31,8 @@ import SubscribeCard from "./SubscribeCard";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { serverUrl_fn } from "../constant/appinfo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DownloadPdfZip from "./DownloadPdfZip";
|
||||
import EmbedTab from "../components/pdf/EmbedTab";
|
||||
|
||||
const ReportTable = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -69,7 +72,11 @@ const ReportTable = (props) => {
|
||||
const [publicUserName, setIsPublicUserName] = useState("");
|
||||
const [isViewShare, setIsViewShare] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||
const [isModal, setIsModal] = useState({});
|
||||
const [reason, setReason] = useState("");
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const [isEmbed, setIsEmbed] = useState(false);
|
||||
const [isPublicTour, setIsPublicTour] = useState();
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
const startIndex = (currentPage - 1) * props.docPerPage;
|
||||
@@ -266,7 +273,9 @@ const ReportTable = (props) => {
|
||||
Signers: signers,
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5
|
||||
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false,
|
||||
FileAdapterId: Doc?.FileAdapterId || ""
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(
|
||||
@@ -364,6 +373,21 @@ const ReportTable = (props) => {
|
||||
setIsShareWith({ [item.objectId]: true });
|
||||
} else if (act.action === "Embed") {
|
||||
handleEmbedFunction(item);
|
||||
} else if (act.action === "CopyTemplateId") {
|
||||
copyTemplateId(item.objectId);
|
||||
} else if (act.action === "CopyPublicURL") {
|
||||
const isPublic = item?.IsPublic;
|
||||
if (isPublic) {
|
||||
let publicUrl = "";
|
||||
if (isStaging) {
|
||||
publicUrl = `https://staging.opensign.me/publicsign?templateid=${item.objectId}`;
|
||||
} else {
|
||||
publicUrl = `https://opensign.me/publicsign?templateid=${item.objectId}`;
|
||||
}
|
||||
copyTemplateId(publicUrl);
|
||||
} else {
|
||||
setIsPublicTour({ [item.objectId]: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
// Get current list
|
||||
@@ -515,7 +539,6 @@ const ReportTable = (props) => {
|
||||
.then(async (result) => {
|
||||
const res = result.data;
|
||||
if (res) {
|
||||
setReason("");
|
||||
setActLoader({});
|
||||
setIsAlert(true);
|
||||
setAlertMsg({
|
||||
@@ -527,7 +550,43 @@ const ReportTable = (props) => {
|
||||
(x) => x.objectId !== item.objectId
|
||||
);
|
||||
props.setList(upldatedList);
|
||||
const params = {
|
||||
event: "declined",
|
||||
body: {
|
||||
objectId: item.objectId,
|
||||
file: item?.SignedUrl || item?.URL,
|
||||
name: item?.Name,
|
||||
note: item?.Note || "",
|
||||
description: item?.Description || "",
|
||||
signers: item?.Signers?.map((x) => ({
|
||||
name: x?.Name,
|
||||
email: x?.Email,
|
||||
phone: x?.Phone
|
||||
})),
|
||||
declinedBy: jsonSender?.email,
|
||||
declinedReason: reason,
|
||||
declinedAt: new Date(),
|
||||
createdAt: item?.createdAt
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/callwebhook`,
|
||||
params,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
}
|
||||
}
|
||||
setReason("");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
@@ -544,25 +603,15 @@ const ReportTable = (props) => {
|
||||
|
||||
async function checkTourStatus() {
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails");
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
// console.log("res ", res.data.TourStatus);
|
||||
if (cloudRes) {
|
||||
const extUser = JSON.parse(JSON.stringify(cloudRes));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||
const tourStatus = extUser?.TourStatus || [];
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter(
|
||||
(obj) => obj["templateTour"]
|
||||
);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const templateTour = filteredtourStatus[0]["templateTour"];
|
||||
|
||||
if (templateTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
const templateTour = tourStatus.find(
|
||||
(obj) => obj.templateTour
|
||||
)?.templateTour;
|
||||
setIsTour(!templateTour);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
@@ -612,10 +661,24 @@ const ReportTable = (props) => {
|
||||
setActLoader({ [`${item.objectId}`]: true });
|
||||
const url = item?.SignedUrl || item?.URL || "";
|
||||
const pdfName = item?.Name || "exported_file";
|
||||
const isCompleted = item?.IsCompleted || false;
|
||||
const templateId = props?.ReportName === "Templates" && item.objectId;
|
||||
const docId = props?.ReportName !== "Templates" && item.objectId;
|
||||
const fileAdapterId = item?.FileAdapterId ? item?.FileAdapterId : "";
|
||||
|
||||
if (url) {
|
||||
try {
|
||||
const signedUrl = await Parse.Cloud.run("getsignedurl", { url: url });
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal({ [item.objectId]: true });
|
||||
} else {
|
||||
const signedUrl = await getSignedUrl(
|
||||
url,
|
||||
docId,
|
||||
fileAdapterId,
|
||||
templateId
|
||||
);
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
}
|
||||
setActLoader({});
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
@@ -648,7 +711,7 @@ const ReportTable = (props) => {
|
||||
receiver_phone: userDetails?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: doc.ExtUserPtr.Company,
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
};
|
||||
const res = replaceMailVaribles(subject, "", variables);
|
||||
|
||||
@@ -677,7 +740,7 @@ const ReportTable = (props) => {
|
||||
receiver_phone: userDetails?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: doc.ExtUserPtr.Company,
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
};
|
||||
const res = replaceMailVaribles("", body, variables);
|
||||
|
||||
@@ -718,7 +781,7 @@ const ReportTable = (props) => {
|
||||
receiver_phone: user?.signerPtr?.Phone || "",
|
||||
expiry_date: localExpireDate,
|
||||
company_name: doc?.ExtUserPtr?.Company || "",
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
};
|
||||
|
||||
const subject =
|
||||
@@ -741,7 +804,7 @@ const ReportTable = (props) => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
let params = {
|
||||
mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
|
||||
mailProvider: doc?.ExtUserPtr?.active_mail_adapter || "",
|
||||
extUserId: doc?.ExtUserPtr?.objectId,
|
||||
recipient: userDetails?.Email,
|
||||
subject: mail.subject,
|
||||
@@ -1076,12 +1139,25 @@ const ReportTable = (props) => {
|
||||
}
|
||||
};
|
||||
const handleEmbedFunction = async (item) => {
|
||||
setIsEmbed(true);
|
||||
setIsPublicProfile({
|
||||
[item.objectId]: props.isPublic[item.objectId]
|
||||
[item.objectId]: true
|
||||
});
|
||||
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
setIsPublicUserName(extendUser[0]?.UserName || "");
|
||||
};
|
||||
|
||||
const publicTourConfig = [
|
||||
{
|
||||
selector: '[data-tut="IsPublic"]',
|
||||
content: t("public-tour-message"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const closePublicTour = () => {
|
||||
setIsPublicTour();
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
{Object.keys(actLoader)?.length > 0 && (
|
||||
@@ -1089,21 +1165,40 @@ const ReportTable = (props) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="p-2 w-full overflow-auto bg-base-100 text-base-content op-card shadow-lg">
|
||||
<div className="p-2 w-full overflow-hidden bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>}
|
||||
{props.tourData && props.ReportName === "Templates" && (
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={props.tourData}
|
||||
isOpen={isTour}
|
||||
// rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
<>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={props.tourData}
|
||||
isOpen={isTour}
|
||||
// rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
{isPublicTour && (
|
||||
<Tour
|
||||
showNumber={false}
|
||||
showNavigation={false}
|
||||
showNavigationNumber={false}
|
||||
onRequestClose={closePublicTour}
|
||||
steps={publicTourConfig}
|
||||
isOpen={true}
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
@@ -1132,8 +1227,8 @@ const ReportTable = (props) => {
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isDashboard && props.List?.length > 0 ? "h-[317px]" : "h-full"
|
||||
} w-full`}
|
||||
isDashboard && props.List?.length > 0 ? "min-h-[317px]" : "h-full"
|
||||
} overflow-auto w-full`}
|
||||
>
|
||||
<table className="op-table border-collapse w-full ">
|
||||
<thead className="text-[14px]">
|
||||
@@ -1169,48 +1264,50 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
|
||||
<td className="px-4 py-2">{item?.Phone || "-"}</td>
|
||||
<td className="px-3 py-2 text-white grid grid-cols-2">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={t(`btnLabel.${act.hoverLabel}`)}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm`}
|
||||
<td className="px-3 py-2">
|
||||
<div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={t(`btnLabel.${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 Contact"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isDeleteModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Delete Contact"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("contact-delete-alert")}
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t("contact-delete-alert")}
|
||||
</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"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</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"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
@@ -1252,9 +1349,55 @@ const ReportTable = (props) => {
|
||||
{item?.URL ? t("download") : "-"}
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item?.ExtUserPtr)}
|
||||
</td>
|
||||
{props.ReportName === "In-progress documents" ? (
|
||||
<td className="px-4 py-2">
|
||||
<button
|
||||
onClick={() =>
|
||||
item?.AuditTrail?.length > 0 &&
|
||||
setIsModal({ [item?.objectId]: true })
|
||||
}
|
||||
className={`${
|
||||
item?.AuditTrail?.length
|
||||
? "border-green-400"
|
||||
: "cursor-default op-border-primary"
|
||||
} focus:outline-none w-[60px] border-[2px] text-[12px] rounded-full md:self-center`}
|
||||
>
|
||||
{item?.AuditTrail?.length ? "VIEWED" : "SENT"}
|
||||
</button>
|
||||
{isModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("document-logs")}
|
||||
handleClose={() => setIsModal({})}
|
||||
>
|
||||
{item?.AuditTrail?.map((x, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="pl-3 first:mt-2 text-sm font-medium flex flex-col md:flex-row items-start md:gap-4 border-t-[1px] border-gray-600"
|
||||
>
|
||||
<div className="py-2 break-all font-bold md:text-[12px] md:col-span-2 w-full md:w-[210px]">
|
||||
{x?.UserPtr?.Email || "-"}
|
||||
</div>
|
||||
<button className="px-2 cursor-default border-[2px] text-[12px] border-green-400 rounded-full md:self-center">
|
||||
{x?.Activity?.toUpperCase() || "-"}
|
||||
</button>
|
||||
<div className=" text-[12px] py-2">
|
||||
{x?.Activity === "Signed"
|
||||
? new Date(x?.SignedOn)?.toUTCString()
|
||||
: new Date(
|
||||
x?.ViewedOn
|
||||
)?.toUTCString() || "-"}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</ModalUi>
|
||||
)}
|
||||
</td>
|
||||
) : (
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item?.ExtUserPtr)}
|
||||
</td>
|
||||
)}
|
||||
<td className="px-4 py-2">
|
||||
{!item?.IsSignyourself && item?.Placeholders ? (
|
||||
<button
|
||||
@@ -1271,17 +1414,17 @@ const ReportTable = (props) => {
|
||||
isEnableSubscription && (
|
||||
<td className=" pl-[20px] py-2">
|
||||
{props.ReportName === "Templates" && (
|
||||
<div className="flex flex-row">
|
||||
<label
|
||||
className={
|
||||
"cursor-pointer relative inline-flex items-center mb-0"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="flex flex-row "
|
||||
data-tut="IsPublic"
|
||||
>
|
||||
<label className="cursor-pointer relative inline-flex items-center mb-0">
|
||||
<input
|
||||
checked={props.isPublic?.[item.objectId]}
|
||||
onChange={(e) =>
|
||||
handlePublicChange(e, item)
|
||||
}
|
||||
onChange={(e) => {
|
||||
setIsPublicTour();
|
||||
handlePublicChange(e, item);
|
||||
}}
|
||||
type="checkbox"
|
||||
value=""
|
||||
className="sr-only peer"
|
||||
@@ -1352,25 +1495,30 @@ const ReportTable = (props) => {
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("public-url")}
|
||||
handleClose={() => setIsPublicProfile({})}
|
||||
handleClose={() => {
|
||||
setIsPublicProfile({});
|
||||
setIsEmbed(false);
|
||||
}}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
{publicUserName ? (
|
||||
{isEmbed && !item?.IsPublic ? (
|
||||
<>
|
||||
<p>{t("public-template-mssg-6")}</p>
|
||||
<EmbedTab templateId={item.objectId} />
|
||||
</>
|
||||
) : publicUserName ? (
|
||||
<div className="font-normal text-black">
|
||||
<p>{t("public-url-copy-mssg")}</p>
|
||||
<div className=" flex items-center justify-between gap-3 mt-2 p-[2px] ">
|
||||
<span>{t("public-url-copy")}</span>
|
||||
<div className=" flex items-center justify-between gap-3 p-[2px] ">
|
||||
<div className="w-[280px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<span className="font-bold">
|
||||
{t("public-url")} :
|
||||
</span>
|
||||
<span
|
||||
onClick={() =>
|
||||
copytoProfileLink()
|
||||
}
|
||||
className="ml-[2px] underline underline-offset-2 cursor-pointer"
|
||||
<a
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
href={publicUrl()}
|
||||
className="ml-[2px] underline underline-offset-2 cursor-pointer text-blue-800"
|
||||
>
|
||||
{publicUrl()}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RWebShare
|
||||
@@ -1387,12 +1535,12 @@ const ReportTable = (props) => {
|
||||
</button>
|
||||
</RWebShare>
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
|
||||
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm md:w-[100px]"
|
||||
onClick={() =>
|
||||
copytoProfileLink()
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-link"></i>{" "}
|
||||
<i className="fa-light fa-copy" />
|
||||
<span className="hidden md:inline-block">
|
||||
{copied["publicprofile"]
|
||||
? t("copied")
|
||||
@@ -1401,36 +1549,10 @@ const ReportTable = (props) => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className=" flex items-center justify-between gap-3 mt-2 p-[2px] ">
|
||||
<div>
|
||||
<span className="font-bold">
|
||||
{t("templateid")} :
|
||||
</span>
|
||||
<span
|
||||
onClick={() =>
|
||||
copyTemplateId(item.objectId)
|
||||
}
|
||||
className="underline ml-[2px] underline-offset-2 w-[150px] md:w-[350px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer"
|
||||
>
|
||||
{item.objectId}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
|
||||
onClick={() =>
|
||||
copyTemplateId(item.objectId)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-link"></i>{" "}
|
||||
<span className="hidden md:inline-block">
|
||||
{copied["templateid"]
|
||||
? t("copied")
|
||||
: t("copy")}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[13px] mt-[5px]">
|
||||
{t("public-url-copy-mssg")}
|
||||
</p>
|
||||
<EmbedTab templateId={item.objectId} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="font-normal text-black">
|
||||
@@ -1439,7 +1561,7 @@ const ReportTable = (props) => {
|
||||
className="mt-3 op-btn op-btn-primary"
|
||||
onClick={() => navigate("/profile")}
|
||||
>
|
||||
{t("add")}
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -1449,7 +1571,7 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
)}
|
||||
<td className="px-2 py-2">
|
||||
<div className="text-base-content flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
<div className="text-base-content min-w-max flex flex-row gap-x-2 gap-y-1 justify-start items-center">
|
||||
{props.actions?.length > 0 &&
|
||||
props.actions.map((act, index) =>
|
||||
props.ReportName === "Templates" ? (
|
||||
@@ -1490,65 +1612,33 @@ const ReportTable = (props) => {
|
||||
{isOption[item.objectId] &&
|
||||
act.action === "option" && (
|
||||
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
|
||||
{act.subaction?.map((subact) =>
|
||||
props.isPublic[
|
||||
item.objectId
|
||||
] ? (
|
||||
<li
|
||||
key={subact.btnId}
|
||||
onClick={() =>
|
||||
handleActionBtn(
|
||||
subact,
|
||||
item
|
||||
)
|
||||
}
|
||||
title={t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className={`${subact.btnIcon} mr-1.5`}
|
||||
></i>
|
||||
{subact.btnLabel && (
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
) : (
|
||||
subact.action !== "Embed" && (
|
||||
<li
|
||||
key={subact.btnId}
|
||||
onClick={() =>
|
||||
handleActionBtn(
|
||||
subact,
|
||||
item
|
||||
)
|
||||
}
|
||||
title={t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className={`${subact.btnIcon} mr-1.5`}
|
||||
></i>
|
||||
{subact.btnLabel && (
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
</span>
|
||||
{act.subaction?.map((subact) => (
|
||||
<li
|
||||
key={subact.btnId}
|
||||
onClick={() =>
|
||||
handleActionBtn(
|
||||
subact,
|
||||
item
|
||||
)
|
||||
}
|
||||
title={t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
<i
|
||||
className={`${subact.btnIcon} mr-1.5`}
|
||||
></i>
|
||||
{subact.btnLabel && (
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t(
|
||||
`btnLabel.${subact.btnLabel}`
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
)}
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
@@ -1869,7 +1959,7 @@ const ReportTable = (props) => {
|
||||
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
|
||||
onClick={() => copytoclipboard(share)}
|
||||
>
|
||||
<i className="fa-light fa-link"></i>{" "}
|
||||
<i className="fa-light fa-copy" />
|
||||
{copied[share.email]
|
||||
? t("copied")
|
||||
: t("copy")}
|
||||
@@ -2023,6 +2113,14 @@ const ReportTable = (props) => {
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
{isDownloadModal[item.objectId] && (
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal[item.objectId]}
|
||||
pdfDetails={[item]}
|
||||
isDocId={false}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -41,8 +41,8 @@ function CustomModal(props) {
|
||||
className="op-btn op-btn-primary mr-2 px-6"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setReason("");
|
||||
props.declineDoc(reason);
|
||||
setReason("");
|
||||
}}
|
||||
>
|
||||
{t("yes")}
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function TourContentWithBtn({ message, isChecked, video }) {
|
||||
></iframe>
|
||||
</div>
|
||||
)}
|
||||
<label className="flex items-center justify-center mb-0">
|
||||
<label className="flex items-center justify-center mt-3 mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-xs mr-1"
|
||||
|
||||
@@ -11,28 +11,37 @@ import { isPublicStaging } from "../constant/const";
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.mjs`;
|
||||
|
||||
// Wrapper component to manage props
|
||||
const PublicScriptFileWrapper = ({ initialProps }) => {
|
||||
const [props, setProps] = useState(initialProps);
|
||||
const PublicScriptFileWrapper = () => {
|
||||
const [props, setProps] = useState("");
|
||||
|
||||
// Make the setProps function globally accessible
|
||||
useEffect(() => {
|
||||
// Assigns the function 'setProps' to the global 'window' object as 'updatePublicTemplateProps'.
|
||||
// This allows 'setProps' to be accessed and called globally across different scripts or components,
|
||||
// enabling dynamic updates to the public template's properties.
|
||||
window.updatePublicTemplateProps = setProps;
|
||||
|
||||
const publicScript = document.getElementById("opensign-script");
|
||||
// condition for using plan js load opensign-script then get templateId
|
||||
if (publicScript) {
|
||||
// Get the custom templateId attribute
|
||||
const templateId = publicScript.getAttribute("templateId");
|
||||
if (templateId) {
|
||||
setProps({ templateId: templateId });
|
||||
} else {
|
||||
const error = "error: TemplateId is missing";
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
window.updatePublicTemplateProps = undefined;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <PdfRequestFiles {...props} />;
|
||||
};
|
||||
|
||||
const appId = isPublicStaging ? "opensign" : "legadranaxn";
|
||||
//"opensign"
|
||||
|
||||
const serverUrl = isPublicStaging
|
||||
? "https://staging-app.opensignlabs.com/api/app"
|
||||
: "https://app.opensignlabs.com/api/app";
|
||||
// "https://staging-app.opensignlabs.com/api/app"
|
||||
|
||||
localStorage.setItem("baseUrl", `${serverUrl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
@@ -58,7 +67,7 @@ const root = ReactDOM.createRoot(document.getElementById("script-component"));
|
||||
root.render(
|
||||
<div data-theme="opensigncss">
|
||||
<Provider store={store}>
|
||||
<PublicScriptFileWrapper initialProps={{ text: "templateId" }} />
|
||||
<PublicScriptFileWrapper />
|
||||
</Provider>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import en from "./locales/en/translation.json";
|
||||
import es from "./locales/es/translation.json";
|
||||
import fr from "./locales/fr/translation.json";
|
||||
|
||||
i18n
|
||||
@@ -12,6 +13,9 @@ i18n
|
||||
en: {
|
||||
translation: en
|
||||
},
|
||||
es: {
|
||||
translation: es
|
||||
},
|
||||
fr: {
|
||||
translation: fr
|
||||
}
|
||||
@@ -27,7 +31,7 @@ i18n
|
||||
interpolation: {
|
||||
escapeValue: false // React already does escaping
|
||||
},
|
||||
whitelist: ["en", "fr"] // List of allowed languages
|
||||
whitelist: ["en", "es", "fr"] // List of allowed languages
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"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-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",
|
||||
@@ -131,7 +131,9 @@
|
||||
"Share": "Share",
|
||||
"View": "View",
|
||||
"option": "Option",
|
||||
"Embed": "Embed"
|
||||
"Embed": "Embed",
|
||||
"Copy TemplateId": "Copy TemplateId",
|
||||
"Copy Public URL": "Copy Public URL"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Sr.No",
|
||||
@@ -151,7 +153,8 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Created Date",
|
||||
"Type": "Type"
|
||||
"Type": "Type",
|
||||
"Logs": "Logs"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -262,9 +265,10 @@
|
||||
"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": " Here’s 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.",
|
||||
"public-url": "Public profile needed",
|
||||
"public-url-copy": "Here’s your public URL: ",
|
||||
"public-url-copy-mssg": "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 complete your OpenSign public profile setup. Once done, you’ll be able to make templates public. This process only needs to be completed once.",
|
||||
"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",
|
||||
@@ -572,9 +576,10 @@
|
||||
"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.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "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-6": "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.",
|
||||
@@ -614,25 +619,69 @@
|
||||
"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.",
|
||||
"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 you’ve 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.",
|
||||
"quota-mail-info-head":"Monthly request signatures email limit",
|
||||
"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 you’ve 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",
|
||||
"public-template-mssg-1": "To integrate OpenSign into your React or Next.js project, simply run the following command:",
|
||||
"public-template-mssg-2": "Ensure you have npm or yarn set up in your project. If you’re using Yarn, you can replace npm install with yarn add @opensign/react.",
|
||||
"public-template-mssg-3": "Need more details or examples?",
|
||||
"public-template-mssg-4": "Visit the",
|
||||
"public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.",
|
||||
"public-template-mssg-6": "Before you can use this code snippet, you must make this template public.",
|
||||
"copy-code": "COPY",
|
||||
"copied-code": "COPIED",
|
||||
"Installation": "Installation",
|
||||
"Usage": "Usage",
|
||||
"insufficient-credits": "Insufficient Signing Credits",
|
||||
"insufficient-credits-mssg": "The owner of this document currently lacks the necessary OpenSign credits for you to sign. Please reach out to the owner if you require further details.",
|
||||
"angular-npm-mssg-1": "To integrate OpenSign into your Angular project, simply run the following command:",
|
||||
"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"
|
||||
|
||||
"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 {{adminEmail}}.",
|
||||
"sent-this-month": "Sent this month",
|
||||
"available-seats": "Available seats",
|
||||
"buy-users": "Buy more users",
|
||||
"isenable-otp": "Enable OTP verification",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Would you like to enable the verification process using a one-time password (OTP)?",
|
||||
"p2": "Selecting this option will enable OTP verification. Users will receive a verification code via email, which they must enter to sign the document.",
|
||||
"p3": "Selecting this option will disable OTP verification, allowing users to sign the document directly without additional steps.",
|
||||
"p4": "Please choose the option that best suits your document signing requirements."
|
||||
},
|
||||
"advanced-options": "Advanced options",
|
||||
"hide-advanced-options": "Hide Advanced options",
|
||||
"download-files": "Download files",
|
||||
"download-pdf": "Download Pdf",
|
||||
"pdf-certificate": "Download Pdf + Certificate",
|
||||
"document-logs": "Document logs",
|
||||
"server-down": "Unable to connect to the OpenSign server. If you are self-hosting OpenSign, please ensure that all the steps in the documentation have been followed correctly. If you're running OpenSign locally, you might be accessing it through an incorrect port number.",
|
||||
"admin-exists": "Admin already exists. Please login to the application using admin credentials in order to manage users.",
|
||||
"enable-tour": "Enable Tour",
|
||||
"istourenabled-help": {
|
||||
"p1": "This will enable a guided tour for signers, providing instructions during the signing process.",
|
||||
"p2": "This will disable the guided tour, ensuring a faster, uninterrupted signing experience.",
|
||||
"p3": "Tip: If your signers are familiar with OpenSign and frequently use it, you may want to disable the guided tour for a more seamless experience. Choose the option that best fits your document signing needs."
|
||||
},
|
||||
"form-title-1": "Document Flow Setting",
|
||||
"form-title-2": "Security Setting",
|
||||
"public-tour-message": "The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it.",
|
||||
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>"
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,686 @@
|
||||
{
|
||||
"header-news": "Nueva funcionalidad: ¡Los perfiles públicos ahora están disponibles! Crea y comparte plantillas públicas fácilmente",
|
||||
"header-news-btn": "Configurar ahora",
|
||||
"create-account": "Crear cuenta",
|
||||
"login": "Iniciar sesión",
|
||||
"language": "Idioma",
|
||||
"name": "Nombre",
|
||||
"phone": "Teléfono",
|
||||
"phone-optional": "opcional",
|
||||
"email": "Correo",
|
||||
"company": "Compañía",
|
||||
"job-title": "Título del puesto",
|
||||
"profile": "Perfil",
|
||||
"log-out": "Cerrar sesión",
|
||||
"is-email-verified": "El correo está verificado",
|
||||
"public-profile": "Perfil público",
|
||||
"tagline": "Eslogan ",
|
||||
"has-requested-you-to-sign": "ha solicitado que firmes",
|
||||
"has-requested-you-to-review-and-sign": " ha solicitado que revises y firmes",
|
||||
"input-required": "Por favor, rellena este campo.",
|
||||
"sort-order": {
|
||||
"Ascending": "Ascendente",
|
||||
"Descending": "Descendente",
|
||||
"Name": "Nombre",
|
||||
"Date": "Fecha"
|
||||
},
|
||||
"context-menu": {
|
||||
"Download": "Descargar",
|
||||
"Rename": "Renombrar",
|
||||
"Move": "Mover",
|
||||
"Delete": "Eliminar"
|
||||
},
|
||||
"action": "Acción",
|
||||
"add": "Agregar",
|
||||
"add-new-folder": "Agregar nueva carpeta",
|
||||
"disable-documentId": "Desactivar ID de documento",
|
||||
"edit": "Editar",
|
||||
"change-password": "Cambiar contraseña",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"upgrade-now": "Mejorar ahora",
|
||||
"upgrade-to": "Mejorar a",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
||||
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
||||
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
||||
"tour-content": "No mostrar esto otra vez",
|
||||
"pro": "PRO",
|
||||
"team": "EQUIPO",
|
||||
"user": "Usuario",
|
||||
"docs": "Documentación",
|
||||
"session-expired": "Tu sesión ha expirado.",
|
||||
"public-profile-help": "Esta es tu URL pública. Cópiala o compártela con el firmante, y podrás ver todas tus plantillas públicas.",
|
||||
"tagline-help": "Este eslogan aparecerá en tu perfil público de OpenSign, por ejem. https://opensign.me/alex",
|
||||
"welcome": "¡Bienvenido de nuevo!",
|
||||
"Login-to-your-account": "Ingresa a tu cuenta",
|
||||
"password": "Contraseña",
|
||||
"forgot-password": "¿Olvidaste la contraseña?",
|
||||
"loading": "Cargando...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Ingresar con SSO",
|
||||
"login-page": "Página de inicio de sesión",
|
||||
"or": "O",
|
||||
"signup-page": "Página de registro",
|
||||
"password-length": "La contraseña debe tener 8 caracteres",
|
||||
"password-case": "La contraseña debe incluir mayúsculas, minúsculas y números",
|
||||
"password-special-char": " La contraseña debe incluir caracteres especiales",
|
||||
"agreee": "Estoy de acuerdo con los",
|
||||
"term": "Términos de Servicio",
|
||||
"subscribe-to-opensign": "Suscribirse al boletín de OpenSign",
|
||||
"register": "Registrarse",
|
||||
"sidebar": {
|
||||
"Dashboard": "Panel",
|
||||
"Sign yourself": "Firma tu mismo",
|
||||
"Request signatures": "Solicitar firmas",
|
||||
"Templates": "Plantillas",
|
||||
"Templates-Children": {
|
||||
"Create template": "Crear plantilla",
|
||||
"Manage templates": "Administrar plantillas"
|
||||
},
|
||||
"OpenSign™ Drive": "OpenSign™ Drive",
|
||||
"Reports": "Reportes",
|
||||
"Reports-Children": {
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In Progress": "En progreso",
|
||||
"Completed": "Completado",
|
||||
"Drafts": "Borradores",
|
||||
"Declined": "Rechazados",
|
||||
"Expired": "Expirados",
|
||||
"Contactbook": "Agenda de contactos"
|
||||
},
|
||||
"Settings": "Ajustes",
|
||||
"Settings-Children": {
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
}
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necesitan tu firma",
|
||||
"Out for signatures": "En espera de ser firmados"
|
||||
},
|
||||
"report-name": {
|
||||
"Draft Documents": "Documentos borradores",
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In-progress documents": "Documentos en progreso",
|
||||
"Completed Documents": "Documentos completados",
|
||||
"Declined Documents": "Documentos rechazados",
|
||||
"Expired Documents": "Documentos expirados",
|
||||
"Recently sent for signatures": "Recientemente enviados para ser firmados",
|
||||
"Recent signature requests": "Solicitudes de firma recientes",
|
||||
"Drafts": "Borradores",
|
||||
"Contactbook": "Agenda de contactos",
|
||||
"Templates": "Plantillas",
|
||||
"Teams": "Equipos",
|
||||
"Users": "Usuarios"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "firmar",
|
||||
"Sign": "FIRMAR",
|
||||
"Resend": "Reenviar",
|
||||
"Revoke": "Revocar",
|
||||
"Delete": "Eliminar",
|
||||
"Use": "Usar",
|
||||
"Quick send": "Envío rápido",
|
||||
"Edit": "Editar",
|
||||
"Share with team": "Compartir con el equipo",
|
||||
"Share": "Compartir",
|
||||
"View": "Ver",
|
||||
"option": "Opción",
|
||||
"Embed": "Incrustar",
|
||||
"Copy TemplateId": "Copiar ID de plantilla",
|
||||
"Copy Public URL": "Copiar URL pública"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
"Title": "Título",
|
||||
"Email": "Correo",
|
||||
"Phone": "Teléfono",
|
||||
"File": "Archivo",
|
||||
"Owner": "Propietario",
|
||||
"Signers": "Firmantes",
|
||||
"Note": "Nota",
|
||||
"Folder": "Carpeta",
|
||||
"Reason": "Razón",
|
||||
"Parent Team": "Equipo padre",
|
||||
"Active": "Activo",
|
||||
"Role": "Rol",
|
||||
"Team": "Equipo",
|
||||
"Name": "Nombre",
|
||||
"Status": "Estado",
|
||||
"created-date": "Fecha de creación",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Registros"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
|
||||
"Need your sign": "Esta es una lista de documentos que esperan tu firma.",
|
||||
"In-progress documents": "Esta es una lista de documentos que has enviado a otras partes para su firma.",
|
||||
"Completed Documents": "Esta es una lista de documentos en los que todas las partes han firmado.",
|
||||
"Declined Documents": "Esta es una lista de documentos que han sido rechazados por uno de los firmantes.",
|
||||
"Expired Documents": "Esta es una lista de documentos que han alcanzado su fecha de caducidad.",
|
||||
"Contactbook": "Esta es una lista de contactos/firmantes añadidos por ti. Aparecerán como sugerencias cuando intentes añadir firmantes a un nuevo documento.",
|
||||
"Templates": "Esta es una lista de plantillas que están a tu disposición para crear documentos. Puedes hacer clic en el botón «usar» para crear un nuevo documento utilizando una plantilla, modifica el documento y añade firmantes en el siguiente paso."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla"
|
||||
},
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "archivo seleccionado",
|
||||
"template-title": "Título de la plantilla",
|
||||
"document-title": "Título del documento",
|
||||
"description": "Descripción",
|
||||
"time-to-complete": "Plazo de finalización (días)",
|
||||
"send-in-order": "Enviar en orden",
|
||||
"send-in-order-help": {
|
||||
"p1": "Elige cómo quieres que se envíen las solicitudes de firma a los firmantes del documento:",
|
||||
"p2": "Seleccionando esta opción se enviará inicialmente la solicitud de firma al primer firmante. Una vez que el primer firmante complete su parte, el siguiente firmante en la secuencia recibirá la solicitud. Este proceso continúa hasta que todos los firmantes hayan firmado el documento. Este método garantiza que el documento se firme en un orden específico.",
|
||||
"p3": "Seleccionando esta opción se enviarán los enlaces para la firma a todos los firmantes simultáneamente. Cada firmante puede firmar el documento cuando le convenga, independientemente de si otros firmantes han completado sus firmas. Este método es más rápido, pero no impone ningún orden de firma entre los participantes.",
|
||||
"p4": "Selecciona la opción que mejor se adapte a las necesidades de tu tramitación de documentos."
|
||||
},
|
||||
"no": "No",
|
||||
"auto-reminder": "Recordatorio automático",
|
||||
"remind-once": " Recordar una vez cada (Días)",
|
||||
"next": "Siguiente",
|
||||
"select-folder": "Seleccionar carpeta",
|
||||
"OpenSign-drive": "OpenSign™ Drive",
|
||||
"drive-document-status": {
|
||||
"Completed": "Completado",
|
||||
"Declined": "Rechazado",
|
||||
"Draft": "Borrador",
|
||||
"Expired": "Expirado",
|
||||
"In Progress": "En progreso"
|
||||
},
|
||||
"select-folder-help": "Si no seleccionas una carpeta, tu documento firmado se guardará en la carpeta principal de OpenSign Drive.",
|
||||
"no-data": "No se encontraron datos",
|
||||
"save-here": "Guardar aquí",
|
||||
"back": "Atrás",
|
||||
"add-folder": "Añadir carpeta",
|
||||
"create-folder": "Crear carpeta",
|
||||
"parent-folder": "Carpeta superior",
|
||||
"create": "Crear",
|
||||
"signers": "Firmantes",
|
||||
"signers-help": "Empieza a escribir el nombre de un contacto para ver los firmantes sugeridos entre tus contactos guardados o añade otros nuevos. Organiza el orden de firma añadiendo firmantes en la secuencia deseada. Utiliza el botón «+» para añadir nuevos firmantes y la «X» para quitar todos. Cada firmante recibirá una solicitud por correo electrónico para que firme el documento según el orden indicado.",
|
||||
"add-signer": "Añadir firmante",
|
||||
"contact-not-found": "Contacto no encontrado",
|
||||
"add-yourself": "Añadirte a ti mismo",
|
||||
"submit": "Enviar",
|
||||
"reset": "Volver a empezar",
|
||||
"my-signature": "Mi firma",
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen",
|
||||
"initials": "Iniciales",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
"regenerate-token": "Regenerar token activo",
|
||||
"generate-token": "Generar token activo",
|
||||
"view-docs": "Ver documentación",
|
||||
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
||||
"yes": "Sí",
|
||||
"copied": "Copiado",
|
||||
"something-went-wrong-mssg": "Algo salió mal, por favor, intenta de nuevo más tarde.",
|
||||
"token-generated": "Token generado exitosamente.",
|
||||
"webhook": "Webhook",
|
||||
"update-webhook": "Actualizar webhook",
|
||||
"add-webhook": "Agregar webhook",
|
||||
"webhook-added": "Webhook agregado exitosamente.",
|
||||
"team-disabled": "Equipo deshabilitado",
|
||||
"Team enabled": "Equipo habilitado",
|
||||
"are-you-sure": "¿En definitiva quieres ",
|
||||
"disable": "deshabilitar",
|
||||
"enable": "habilitar",
|
||||
"this-team": " este equipo?",
|
||||
"edit-team": "Editar equipo",
|
||||
"name-of-team": "Nombre del equipo",
|
||||
"prev": "Anterior",
|
||||
"no-data-avaliable": "No hay datos disponibles",
|
||||
"add-team": "Agregar equipo",
|
||||
"page-not-found": "Página no encontrada",
|
||||
"users-from-teams": "usuarios de equipos",
|
||||
"user-status": "Estado del usuario",
|
||||
"activate": "activar",
|
||||
"deactivate": "desactivar",
|
||||
"this-user": " este usuario",
|
||||
"delete-user": "Eliminar usuario",
|
||||
"delete": "eliminar",
|
||||
"add-user": "Agregar usuario",
|
||||
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
|
||||
"Team status": "Estado del equipo",
|
||||
"user-deactivated": "Usuario desactivado.",
|
||||
"user-activated": "Usuario activado.",
|
||||
"public": "Público",
|
||||
"download": "Descargar",
|
||||
"view": "Ver",
|
||||
"make-template-public": "Convertir la plantilla en pública",
|
||||
"make-template-private": "Convertir la plantilla en privada",
|
||||
"make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?",
|
||||
"make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.",
|
||||
"public-role": "Rol público",
|
||||
"public-url": "Se requiere perfil público",
|
||||
"public-url-copy": "Aquí está tu URL pública: ",
|
||||
"public-url-copy-mssg": "Cópiala o compártela con el firmante y podrás ver todas tus plantillas públicas.",
|
||||
"add-public-url-alert": "Por favor, completa la configuración de tu perfil público OpenSign. Una vez terminado, podrás crear plantillas públicas. Este proceso solo necesita ser completado una vez.",
|
||||
"share-with-alert": "No puedes compartir una plantilla si alguno de los roles ya tiene contactos asignados. Elimina todas las asignaciones de contactos de los roles antes de compartir la plantilla.",
|
||||
"share-with": "Compartir con",
|
||||
"team-not-found": "Equipo no encontrado",
|
||||
"roles": "Roles",
|
||||
"delete-document": "Eliminar documento",
|
||||
"delete-document-alert": "¿En definitiva quieres eliminar este documento?",
|
||||
"quick-send": "Envío rápido",
|
||||
"add-new": "Agregar nuevo",
|
||||
"send": "Enviar",
|
||||
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
||||
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
||||
"quick-send-alert-3": "Por favor, añade al menos un rol a esta plantilla para poder hacer un «envío rápido» de copias a varios firmantes.",
|
||||
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
||||
"copy-link": "Copiar enlace",
|
||||
"copy": "Copiar",
|
||||
"revoke-document": "Revocar documento",
|
||||
"revoke-document-alert": "¿En definitiva quieres revocar este documento?",
|
||||
"resend-mail": "Reenviar correo",
|
||||
"resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe!",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
"document-sent-alert": "¡Documento enviado exitosamente!",
|
||||
"template-share-alert": "Plantilla compartida exitosamente.",
|
||||
"template-public-alert-1": "Has convertido exitosamente la plantilla en pública.",
|
||||
"template-public-alert-2": "Has convertido exitosamente la plantilla en privada.",
|
||||
"template-public-alert-3": "Tienes que seleccionar un rol para los firmantes públicos.",
|
||||
"template-public-alert-4": "Por favor, asegúrate de que has añadido al menos un widget de firma para cada firmante.",
|
||||
"template-public-alert-5": "Dado que esta plantilla tiene «enviar en orden» habilitado, el rol público debe estar posicionado arriba al inicio.",
|
||||
"template-public-alert-6": "Por favor, enlaza firmantes a todos los roles excepto al usuario de rol público.",
|
||||
"template-public-alert-7": "Por favor, asigna al menos un rol público para convertir está plantilla en pública.",
|
||||
"loader": " Esto podría llevar algún tiempo",
|
||||
"pages": "Páginas",
|
||||
"document-signed": "Documento firmado",
|
||||
"close": "Cerrar",
|
||||
"validation-alert": "Alerta de validación",
|
||||
"validate-alert-mssg": " Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"otp-verification": "Verificación OTP",
|
||||
"enter-otp": "Ingresar OTP",
|
||||
"verify": "Verificar",
|
||||
"verify-email-1": "Verificar correo",
|
||||
"resend": "Reenviar",
|
||||
"contact-details": "Detalles del contacto",
|
||||
"verify-email": "¡Por favor, verifica tu correo!",
|
||||
"send-otp": " Enviar OTP",
|
||||
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
||||
"loading-doc": "Cargando documento...",
|
||||
"widgets-name": {
|
||||
"signature": "firma",
|
||||
"stamp": "sello",
|
||||
"initials": "iniciales",
|
||||
"name": "nombre",
|
||||
"job title": "título del puesto",
|
||||
"company": "compañía",
|
||||
"date": "fecha",
|
||||
"text": "texto",
|
||||
"text input": "entrada de texto",
|
||||
"checkbox": "casilla",
|
||||
"dropdown": "desplegable",
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
"please-add": "Por favor, agrega un",
|
||||
"signed-by": "Firmado por",
|
||||
"more": "más",
|
||||
"sent": "enviado",
|
||||
"print": "Imprimir",
|
||||
"certificate": "Certificado",
|
||||
"decline": "Rechazar",
|
||||
"finish": "Finalizar",
|
||||
"mail": "Correo",
|
||||
"sign-now": "Firmar ahora",
|
||||
"successfully-signed": "¡Firmado exitosamente!",
|
||||
"email-mssg": " Los destinatarios agregados aquí recibirán una copia del documento firmado.",
|
||||
"email-error-1": " por favor, proporciona una dirección de correo correcta",
|
||||
"email-error-2": "Solo puedes enviar hasta a 10 destinatarios a la vez.",
|
||||
"Email-verified-alert-1": "El correo está verificado.",
|
||||
"Email-verified-alert-2": "El correo ya ha sido verificado.",
|
||||
"upload-stamp-image": "Subir imagen del sello",
|
||||
"draw": "Dibujar",
|
||||
"type": "Escribir",
|
||||
"color-type": {
|
||||
"red": "rojo",
|
||||
"blue": "azul",
|
||||
"black": "negro",
|
||||
"yellow": "amarillo"
|
||||
},
|
||||
"my-initials": "Mis iniciales",
|
||||
"upload": "Subir",
|
||||
"initial-teb": "Iniciales",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "Tu firma",
|
||||
"copy-title": "Copiar widget a",
|
||||
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?",
|
||||
"reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
|
||||
"reset-password-alert-2": "Por favor, configura un adaptador de correo ",
|
||||
"reset-password-alert-3": "Restablece tu contraseña",
|
||||
"faild-animation": "Error al cargar la animación",
|
||||
"apply": "Aplicar",
|
||||
"copy-type": {
|
||||
"All pages": "Todas las páginas",
|
||||
"All pages but last": "Todas las páginas menos la última",
|
||||
"All pages but first": "Todas las páginas menos la primera",
|
||||
"Next to current widget": "Junto al widget actual"
|
||||
},
|
||||
"options": "Opciones",
|
||||
"minimun-check": "Chequeo mínimo",
|
||||
"maximum-check": "Chequeo máximo",
|
||||
"default-value": "Valor por defecto",
|
||||
"select": "Seleccionar",
|
||||
"read-only": "Es de solo lectura",
|
||||
"hide-labels": "Esconder etiquetas",
|
||||
"checkbox": "Casilla",
|
||||
"alert": "Alerta",
|
||||
"zoom-in": "Acercar",
|
||||
"zoom-out": "Alejar",
|
||||
"document-signed-alert": "¡Felicitaciones! 🎉 ¡Este documento ha sido firmado por ti exitosamente!",
|
||||
"otp-sent-alert": "OTP enviado a tu correo",
|
||||
"fields-required": "Campos requeridos",
|
||||
"signature-widget-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma agregado",
|
||||
"signature-widget-alert-2": "Por favor, asegúrate de que todos los campos están correctamente rellenados y de que cumplen todos los requisitos.",
|
||||
"encrypted-pdf-alert": "Actualmente los archivos PDF encriptados no están soportados.",
|
||||
"encrypted-pdf-alert-1": "La contraseña del archivo PFX es inválida.",
|
||||
"user-not-exist": "El usuario no existe",
|
||||
"template-signature-field": "Por favor, agrega al menos un campo de firma para todos los roles.",
|
||||
"template-role-alert": "Por favor, primero agrega roles",
|
||||
"create-document": "Crear documento",
|
||||
"template-created-alert": "¿Quieres crear un documento usando la plantilla que acabas de crear?",
|
||||
"signers-alert": "alerta de firmantes",
|
||||
"template-creation-alert-1": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"ok": "OK",
|
||||
"radio-group": "Grupo de radio",
|
||||
"dropdown-options": "Opciones del desplegable",
|
||||
"add-role": "Agregar rol",
|
||||
"role-ex": " por ejem.: Cliente, RRHH, Director, Administrador, Estudiante, etc.",
|
||||
"add/choose-signer": "Agregar/Elegir firmante",
|
||||
"choose-from-contacts": "Elegir desde los contactos",
|
||||
"select-signer": " Por favor, selecciona el firmante",
|
||||
"edit-template": "Editar plantilla",
|
||||
"widget-info": "Información del widget",
|
||||
"validation": "Validación",
|
||||
"invalid-default-value": "valor por defecto inválido",
|
||||
"hint": "Pista",
|
||||
"color": "color",
|
||||
"role-help": {
|
||||
"p1": "¿Qué son los roles de una plantilla?",
|
||||
"p2": "Empieza por especificar cada uno de los roles necesarios para completar el documento. Piensa en las partes implicadas en el proceso de firma y cuáles son sus responsabilidades. Los roles más habituales son RRHH para los documentos internos, Cliente para los contratos o Proveedor para los convenios comerciales.",
|
||||
"p3": "¿Por qué preasignar usuarios a algunos roles?",
|
||||
"p4": "Para los roles que consistentemente involucran al mismo individuo (por ejem., la firma del Gerente en las ofertas de empleo), puedes preasignar un usuario a un rol dentro de una plantilla. Este paso es opcional pero recomendado para la eficiencia y consistencia entre documentos",
|
||||
"p5": "¿Cuando especifico el usuario asignado a cada rol?",
|
||||
"p6": "Cuando crees un documento usando tu plantilla, se te pedirá que asignes usuarios a cada rol definido. Si un rol ya tiene un usuario asignado, este será rellenado con anticipación, pero puedes modificarlo según lo necesites antes de enviar el documento."
|
||||
},
|
||||
"add-recipients": "Agregar destinatarios",
|
||||
"loading-mssg": "Esto podría llevar algún tiempo",
|
||||
"send-mail": "Enviar correo",
|
||||
"placeholder-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma para cada destinatario.",
|
||||
"placeholder-alert-2": "Por favor, confirma que hayas rellenado el campo de texto.",
|
||||
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
|
||||
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
|
||||
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"email-subject": "asunto del correo",
|
||||
"email-body": "cuerpo del correo",
|
||||
"email-placeholder": "agregar cuerpo del correo",
|
||||
"reset-to-default": "Restablecer a por defecto",
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"Mails Sent": "Correos enviados",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
"alert-message": "Mensaje de alerta",
|
||||
"document-decline": "Rechazar documento",
|
||||
"decline-alert-1": "¿En definitiva quieres rechazar este documento?",
|
||||
"decline-alert-2": "¡Has rechazado este documento!",
|
||||
"decline-alert-3": "No puedes firmar este documento ya que ha sido rechazado/revocado.",
|
||||
"sign here": "Firma aquí",
|
||||
"guest-email-alert": "el código de verificación ha sido enviado a tu correo",
|
||||
"get-otp-alert": "recibirás un código de verificación por correo",
|
||||
"user-already-exist": "El usuario ya existe.",
|
||||
"server-error": "¡Error interno del servidor!",
|
||||
"fill-required-details!": "¡Por favor, rellena los detalles requeridos!",
|
||||
"sign-up-form": "Formulario de registro",
|
||||
"sign-up": "Registrarse",
|
||||
"folder-already-exist!": "¡la carpeta ya existe!",
|
||||
"please-select-position!": "¡por favor, selecciona una posición!",
|
||||
"default-sign-alert": "¿En definitiva quieres firmar automáticamente en todos los lugares solicitados?",
|
||||
"Your-Signature": "Tu firma",
|
||||
"auto-sign-all": "Firmar todo automáticamente",
|
||||
"generating-certificate": "Generando certificado",
|
||||
"pdf-download": "Descargar PDF",
|
||||
"generate-certificate-alert": " Tu certificado de finalización se está generando. Por favor, espera un momento. Si la descarga no inicia pronto, presiona el botón otra vez.",
|
||||
"folder-already-exist": "¡La carpeta ya existe!",
|
||||
"folder-created-successfully": "¡Carpeta creada exitosamente!",
|
||||
"fill-folder-name": "Por favor, rellena el nombre de la carpeta",
|
||||
"already-exists-this-username": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"verification-code-sent": "¡El correo de verificación ha sido enviado a tu correo!",
|
||||
"registered-user-successfully": "Usuario registrado exitosamente.",
|
||||
"role-not-found": "Rol no encontrado.",
|
||||
"do-not-access": "No tienes acceso, por favor, contacta al administrador.",
|
||||
"add-admin": "Agregar administrador",
|
||||
"opensign-setup": "Instalación de OpenSign",
|
||||
"join-discord": "Únete a nuestro servidor de Discord",
|
||||
"admin-already-exist": "El administrador ya existe.",
|
||||
"password-update-alert-1": "Contraseña actualizada exitosamente.",
|
||||
"password-update-alert-2": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-3": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-4": "Tu contraseña y su confirmación no son iguales.",
|
||||
"current-password": "Contraseña actual",
|
||||
"new-password": "Contraseña nueva",
|
||||
"confirm-password": "Confirmar contraseña",
|
||||
"file-alert-1": "El tamaño del archivo seleccionado es muy grande. Por favor, selecciona un archivo con un tamaño menor a",
|
||||
"file-alert-2": "Por favor, selecciona un archivo.",
|
||||
"file-alert-3": "Por favor, espera mientras se sube el documento.",
|
||||
"enter-pdf-password": "Ingresa la contraseña del PDF",
|
||||
"correct-password": "Por favor, proporciona la contraseña correcta",
|
||||
"decrypting-pdf": " Desencriptando PDF, por favor, espera...",
|
||||
"invalid-otp": "OTP inválido",
|
||||
"user-not-found": "¡Usuario no encontrado!",
|
||||
"enter-otp-alert": "¡Por favor, ingresa el OTP!",
|
||||
"get-verification-code": "Obtener código de verificación",
|
||||
"get-verification-code-2": "Obtendrás un código de verificación por correo",
|
||||
"enter-verification-code": "Ingresar código de verificación",
|
||||
"provide-your-details": "Proporciona tus detalles",
|
||||
"provide-email": "Por favor, proporciona un correo.",
|
||||
"additional-info": "Información adicional",
|
||||
"signature-saved-alert": "Firma guardada exitosamente.",
|
||||
"fill-field": "Por favor, rellena este campo",
|
||||
"error-template": "Error: ¡Plantilla no encontrada!",
|
||||
"error": "Error",
|
||||
"invalid-document": "Error: ¡Documento inválido!",
|
||||
"document-not-found": "¡Documento no encontrado!",
|
||||
"document-signed-alert-1": "Has firmado el documento exitosamente. Puedes descargar o imprimir una copia del documento parcialmente firmado. Una copia del documento firmado digitalmente será enviada al propietario mediante correo electrónico una vez haya sido firmado por todos los firmantes.",
|
||||
"encrypted-pdf-not-support": "Los archivos PDF encriptados no están soportados actualmente.",
|
||||
"document-signed-alert-4": " ¡Felicitaciones! 🎉 ¡Este documento ha sido firmado exitosamente por todos los participantes!",
|
||||
"document-signed-alert-5": "Este documento ha sido firmado por todos los firmantes.",
|
||||
"document-signed-alert-6": "Este documento ha sido rechazado por uno o más destinatarios.",
|
||||
"document-signed-alert-7": "Este documento ya no está disponible.",
|
||||
"document-signed-alert-8": "Este documento ya ha sido enviado para ser firmado.",
|
||||
"yet-to-sign": "Todavía por firmar",
|
||||
"validation-alert-1": "Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"account-already-exists": "¡La cuenta ya existe!",
|
||||
"choose-password": "Elegir contraseña",
|
||||
"enter-password": "Ingresar contraseña",
|
||||
"sure": "claro",
|
||||
"subscribed-alert": "¡Ya te has suscrito al plan!",
|
||||
"monthly": "Mensual",
|
||||
"yearly-upto": "Anual (hasta 66% de descuento)",
|
||||
"billed-yearly": "Facturado anualmente",
|
||||
"billed-monthly": "Facturado mensualmente",
|
||||
"plansubscription-1": "Alójalo por tu cuenta gratuitamente",
|
||||
"visit-github": "Visitar GitHub",
|
||||
"user-already-exist-name": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"do-not-access-contact-admin": "No tienes acceso, por favor, contacta a un administrador.",
|
||||
"filed-required-correctly": "Por favor, rellena correctamente los detalles requeridos.",
|
||||
"admin-created": "Administrador creado",
|
||||
"invalid-masterkey": "Llave maestra inválida",
|
||||
"opensign-Setup": "Instalación de OpenSign",
|
||||
"master-key": " Llave maestra",
|
||||
"profile-update-alert": "Perfil actualizado exitosamente.",
|
||||
"date": "Fecha",
|
||||
"report-not-found": "Reporte no encontrado",
|
||||
"please-wait": "por favor, espera",
|
||||
"select-foler": "Seleccionar carpeta",
|
||||
"generated-on": "Generado el",
|
||||
"text-field": "Campo de texto",
|
||||
"font-size": "Tamaño de la fuente",
|
||||
"widget-status": {
|
||||
"Required": "Requerido",
|
||||
"Optional": "Opcional"
|
||||
},
|
||||
"all-right": "Todos los derechos reservados",
|
||||
"version": "versión",
|
||||
"social-media": {
|
||||
"github": "GitHub",
|
||||
"linked-in": "LinkedIn",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord"
|
||||
},
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.",
|
||||
"home-layout-2": "Para subir documentos para firmar tú mismo o para solicitar la firma de otros, simplemente selecciona los botones correspondientes.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar OpenSign! Si necesitas soporte siéntete libre de contactarnos.",
|
||||
"generate-token": "Mejora ahora para generar un token API de producción.",
|
||||
"opensign-drive-1": "Haz clic en los enlaces para navegar fácilmente por la estructura jerárquica de carpetas y ver los documentos de cada carpeta.",
|
||||
"opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
|
||||
"opensign-drive-3": "Ordena tus documentos por «Fecha» o «Nombre» usando este menú.",
|
||||
"opensign-drive-4": "Haz clic en este menú para mostrar los documentos en vista de lista.",
|
||||
"opensign-drive-5": "La lista de documentos es mostrada de acuerdo a la opción de ordenamiento seleccionada. Los íconos situados junto a cada documento indican su estado actual.",
|
||||
"opensign-drive-6": "Haz clic con el botón derecho en un documento para ver opciones como «Descargar», «Renombrar», «Mover» y «Eliminar». Haz clic en el documento para abrirlo.",
|
||||
"opensign-drive-7": "Haz clic con el botón derecho en cualquier carpeta para ver las opciones. Elige «Renombrar» para cambiar el nombre de la carpeta o haz clic en ella para navegar por su contenido.",
|
||||
"pdf-request-file-1": "Lista de firmantes que todavía tienen que firmar este documento.",
|
||||
"pdf-request-file-2": "Haz clic en cualquiera de los marcadores de posición que aparecen en el documento para firmar. A continuación, verás opciones para dibujar tu firma, escribirla o subir una imagen .",
|
||||
"pdf-request-file-3": "Haz clic en los botones «Atrás», «Rechazar» o «Finalizar» para navegar por el documento. Utiliza el menú de tres puntos para ver otras opciones, como el botón «Descargar».",
|
||||
"pdf-request-file-4": "Lista de firmantes que ya han firmado el documento.",
|
||||
"pdf-request-file-5": "Puedes hacer clic en «Firmar todo automáticamente» para firmar de manera automática en todos los lugares que deban ser firmados por ti. Asegúrate de revisar correctamente el documento antes de hacer clic en ese botón.",
|
||||
"placeholder-sign-1": "Selecciona un destinatario de esta lista para agregar un marcador de posición dónde este destinatario tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del destinatario una vez lo sueltes en el documento.",
|
||||
"placeholder-sign-2": "Hacer clic en el botón «Agregar destinatarios» te permitirá añadir más firmantes.",
|
||||
"placeholder-sign-3": "Haz clic en este botón para agregar más destinatarios/firmantes al documento.",
|
||||
"placeholder-sign-4": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los marcadores de posición existentes en la plantilla. Para tu comodidad, estos marcadores de posición coincidirán con el color del nombre del destinatario, haciéndolos fácilmente identificables.",
|
||||
"placeholder-sign-6": "Hacer clic en «Enviar» guardará el documento. En el siguiente paso podrás personalizar los correos que serán enviados a los destinatarios o copiar los enlaces para firmar y compartirlos con los destinatarios por tu cuenta.",
|
||||
"report-1": "Haz clic en el botón «Agregar» para crear una nueva plantilla. Las plantillas son documentos reusables diseñados para generar rápidamente nuevos documentos con la misma estructura pero diferentes firmantes. Por ejemplo, una plantilla de RR.HH. para el proceso de incorporación podría tener roles predefinidos como «Director de RR.HH.» y «Nuevo empleado». Cada vez que utilices la plantilla, puedes asignar el rol de «Nuevo empleado» a los distintos miembros del personal que se incorporen, mientras que el rol de « Director de RR.HH.» permanece constante, lo que facilita un proceso de incorporación fluido para cada persona contratada.",
|
||||
"redirect": "Haz clic en el botón «Usar» para crear un nuevo documento desde una plantilla existente.",
|
||||
"bulksend": "Para crear y enviar rápidamente múltiples documentos usando una plantilla existente, haz clic en el botón «Envío rápido».",
|
||||
"option": "Este menú revela más opciones como «Editar» y «Eliminar». Usa el botón «Editar» para agregar roles, modificar campos y actualizar tu plantilla. Los cambios aplicarán para todos los documentos futuros que sean creados usando esta plantilla pero no afectarán a los documentos ya existentes. Con el botón «Eliminar» puedes eliminar la plantilla.",
|
||||
"signyour-self-1": "Para personalizar el documento antes de que sea firmado, selecciona y arrastra al documento los widgets que prefieras. Escoge el espacio perfecto para cada modificación para adaptar el documento a tus necesidades.",
|
||||
"signyour-self-2": "Arrastra y suelta en cualquier lugar de esta área. Después podrás cambiar el tamaño y moverlo.",
|
||||
"template-placeholder-1": "Hacer clic en el botón «Agregar rol» te permitirá agregar varios roles de firmantes. Puedes asociar usuarios a cada rol en los siguientes pasos.",
|
||||
"template-placeholder-2": "Una vez que los roles hayan sido agregados, selecciona un rol de la lista para agregar un marcador de posición donde éste tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del rol una vez lo sueltes en el documento.",
|
||||
"template-placeholder-3": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"template-placeholder-4": "Arrastra el marcador de posición para un rol a cualquier lugar del documento. Recuerda, aparecerá con el mismo color que el nombre del rol para poder referenciarlo fácilmente.",
|
||||
"template-placeholder-5": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"webhook-1": "Mejora ahora para configurar un webhook",
|
||||
"Need your Signature": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan por tu revisión.",
|
||||
"Out for signatures": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan tu firma.",
|
||||
"Recent signature requests": "Esta es una lista de documentos que esperan por tu firma.",
|
||||
"Recently sent for signatures": "Esta es una lista de documentos que has enviado a otras partes para que sea firmado.",
|
||||
"Drafts": "Estos son documentos que has empezado pero que todavía no finalizas para su envío.",
|
||||
"public-template": "Este video muestra cómo puedes configurar tu perfil público personalizado, como «https://opensign.me/tu-nombre-de-usuario». También aprenderás cómo personalizar tu eslogan y cómo hacer tus plantillas disponibles para su firmado público."
|
||||
},
|
||||
"enter-email-plaholder": "Agrega una dirección de correo y presiona «Enter»",
|
||||
"success-email-alert": "¡Correo enviado exitosamente!",
|
||||
"expired-doc-title": "Documento expirado",
|
||||
"expired-on-mssg": "Este documento expiro el {{expiredDate}} y ya no está disponible para ser firmado.",
|
||||
"signature-validate-alert": "Por favor, confirma que has seleccionado al menos {{minRequiredCount}} casillas.",
|
||||
"signature-validate-alert-2": "Asegúrate de que este campo esté correctamente rellenado y que cumpla con todos los requisitos.",
|
||||
"remaing-users": "Usuarios restantes",
|
||||
"Role": "Rol",
|
||||
"additional-users": "Por favor, compra el complemento Usuarios.",
|
||||
"Quantity-of-users": "Cantidad de usuarios",
|
||||
"Price": "Precio",
|
||||
"Total-price-for-next-time": "Precio total para la próxima vez",
|
||||
"Proceed": "Proceder",
|
||||
"Select": "Seleccionar",
|
||||
"Add-seats": "Agregar plazas",
|
||||
"user-name-exist": "el nombre de usuario ya existe",
|
||||
"format": "formato",
|
||||
"select-date-format": "Selecciona un formato de fecha",
|
||||
"quantity-of-credits": "Cantidad de créditos premium",
|
||||
"remaining-credits": "Créditos premium disponibles:",
|
||||
"remaining-credits-help": "Usa créditos premium para la API para la firma de documentos, envío en masa, o para incorporar la integración de OpenSign en tu página web. Te quedan {{allowedcredits}} créditos incluidos y {{addoncredits}} créditos comprados adicionalmente.",
|
||||
"additional-credits": "Por favor, compra créditos premium",
|
||||
"quota-err-quicksend": "Cuota alcanzada, no tienes créditos suficientes.",
|
||||
"buy-credits": "Comprar créditos premium",
|
||||
"rotate-right": "Rotar a la derecha",
|
||||
"rotate-left": "Rotar a la izquierda",
|
||||
"rotate-alert-mssg": "Todos los widgets de esta página se perderán. ¿En definitiva quieres proceder?",
|
||||
"templateid": "ID de plantilla",
|
||||
"bulk-send-subcription-alert": "Por favor, mejora al plan a Professional o Team para usar envío en masa.",
|
||||
"generate-test-token": "Generar token de prueba",
|
||||
"regenerate-test-token": "Regenerar token de prueba",
|
||||
"help-test-token": "Este token puede ser usado para probar las API en el punto de acceso «https://sandbox.opensignlabs.com/api/v1», permitiéndote realizar firmas de documentos ilimitadas. Por favor, ten en cuenta que el API de pruebas firmará tus documentos con certificados autofirmados, que podrían no ser reconocidos por Adobe como válidos. Una vez hayas completado tus pruebas, puedes mejorar a uno de nuestros planes de pago para generar un token de producción.",
|
||||
"help-api-token": "Este token puede ser usado para acceder a las API de producción en el punto de acceso «{{origin}}/api/v1». Solo puede ser generado con uno de nuestros planes de pago.",
|
||||
"reason": "Razón",
|
||||
"decline-by": "Rechazado/revocado por",
|
||||
"document-declined": "Documento rechazado",
|
||||
"public-template-mssg-1": "Para integrar OpenSign a tu proyecto React o Next.js, simplemente ejecuta los siguientes comandos:",
|
||||
"public-template-mssg-2": "Asegúrate de tener «npm» o «yarn» configurado en tu proyecto. Si estás usando «yarn», puedes reemplazar «npm install» con «yarn add @opensign/react».",
|
||||
"public-template-mssg-3": "¿Necesitas más detalles o ejemplos?",
|
||||
"public-template-mssg-4": "Visita la",
|
||||
"public-template-mssg-5": " «npm» para las últimas actualizaciones, documentación detallada e historial de versiones.",
|
||||
"public-template-mssg-6": "Antes de que puedas usar este fragmento de código, debes convertir esta plantilla en pública.",
|
||||
"copy-code": "COPIAR",
|
||||
"copied-code": "COPIADO",
|
||||
"Installation": "Instalación",
|
||||
"Usage": "Uso",
|
||||
"insufficient-credits": "Créditos de firma insuficientes",
|
||||
"insufficient-credits-mssg": "El propietario de este documento actualmente carece de los créditos OpenSign necesarios para que puedas firmarlo. Por favor, contacta al propietario si necesitas más detalles.",
|
||||
"angular-npm-mssg-1": "Para integrar OpenSign a tu proyecto Angular, simplemente ejecuta los siguientes comandos:",
|
||||
"quota-mail-info-head": "Límite mensual de solicitudes de firma por correo",
|
||||
"quota-mail-info": "Puedes enviar hasta 15 solicitudes de firma por correo cada mes. Mejora ahora para enviar solicitudes de firma sin límite.",
|
||||
"quota-mail": "Has alcanzado tu límite de 15 solicitudes de firma por correo para este mes. Mejora ahora para continuar enviando correos.",
|
||||
"quota-mail-tip": "Consejo: Todavía puedes solciitar la firma de <1>documentos ilimitados</1> compartiendo manualmente los enlaces de solicitud de firma.",
|
||||
"quota-mail-head": "Cuota alcanzada",
|
||||
"unauthorized-modal": "No tienes permisos para realizar esta acción, por favor, contacta a {{adminEmail}}.",
|
||||
"sent-this-month": "Enviados este mes",
|
||||
"available-seats": "Plazas disponibles",
|
||||
"buy-users": "Comprar más usuarios",
|
||||
"isenable-otp": "Habilitar verificación con OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "¿Te gustaría habilitar el proceso de verificación usando una contraseña de un solo uso (OTP)?",
|
||||
"p2": "Seleccionar esta opción habilitará la verificación con OTP. Los usuarios recibirán un código de verificación por correo, la cual tendrán que ingresar para poder firmar el documento.",
|
||||
"p3": "Seleccionar esta opción deshabilitará la verificación con OTP, lo cual permitirá a los usuarios firmar el documento directamente sin pasos adicionales.",
|
||||
"p4": "Por favor, selecciona la opción que mejor se adapte a tus requisitos para la firma de documentos."
|
||||
},
|
||||
"advanced-options": "Opciones avanzadas",
|
||||
"hide-advanced-options": "Ocultar las opciones avanzadas",
|
||||
"download-files": "Descargar archivos",
|
||||
"download-pdf": "Descargar PDF",
|
||||
"pdf-certificate": "Descargar PDF + Certificado",
|
||||
"document-logs": "Registros de documentos",
|
||||
"server-down": "No se pudo conectar al servidor de OpenSign. Si estás alojando OpenSign por tu cuenta, por favor, asegúrate de que todos los pasos de la documentación han sido seguidos correctamente. Si estás ejecutando OpenSign localmente, puedes estar accediendo a OpenSign por un número de puerto incorrecto.",
|
||||
"admin-exists": "El administrador ya existe. Por favor, inicia sesión en la aplicación usando las credenciales de administrador para poder administrar los usuarios.",
|
||||
"public-tour-message": "Por favor, convierte la plantilla en pública para copiar la URL pública",
|
||||
"add-user-template": "Es necesario agregar un rol antes de poder agregarle campos.",
|
||||
"pdf-uncompatible": "Este pdf no es compatible con opensign, comuníquese con <support@opensignlabs.com>",
|
||||
"enable-tour": "Habilitar recorrido",
|
||||
"istourenabled-help":{
|
||||
"p1": "Esto permitirá realizar una visita guiada para los firmantes, brindando instrucciones durante el proceso de firma.",
|
||||
"p2": "Esto desactivará la visita guiada, lo que garantizará una experiencia de firma más rápida e ininterrumpida.",
|
||||
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
|
||||
},
|
||||
"form-title-1": "Configuración del flujo de documentos",
|
||||
"form-title-2": "Configuración de seguridad"
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
"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-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",
|
||||
@@ -136,7 +136,8 @@
|
||||
"Name": "Nom",
|
||||
"Status": "Statut",
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir"
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
@@ -150,7 +151,9 @@
|
||||
"Share": "Partager",
|
||||
"View": "Voir",
|
||||
"option": "Option",
|
||||
"Embed": "Intégrer"
|
||||
"Embed": "Intégrer",
|
||||
"Copy TemplateId": "Copier l'ID du modèle",
|
||||
"Copy Public URL": "Copier l'URL publique"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
@@ -261,9 +264,10 @@
|
||||
"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.",
|
||||
"public-url": "Profil public requis",
|
||||
"public-url-copy": "Voici votre URL publique : ",
|
||||
"public-url-copy-mssg": "copiez-la ou partagez-le avec le signataire et vous pouvoir voir tous vos paramètres publics modèles.",
|
||||
"add-public-url-alert": "Veuillez terminer la configuration de votre profil public OpenSign. Une fois cela fait, vous pourrez rendre les modèles publics. Ce processus ne doit être effectué qu’une seule fois.",
|
||||
"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",
|
||||
@@ -571,9 +575,10 @@
|
||||
"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.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "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-6": "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'.",
|
||||
@@ -611,28 +616,70 @@
|
||||
"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.",
|
||||
"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.",
|
||||
"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 à l’un 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.",
|
||||
"quota-mail-info-head":"Limite mensuelle d'e-mails de signatures de demandes",
|
||||
"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 à l’un 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é",
|
||||
"public-template-mssg-1": "Pour intégrer OpenSign dans votre projet React ou Next.js, exécutez simplement la commande suivante :",
|
||||
"public-template-mssg-2": "Assurez-vous que npm ou Yarn est configuré dans votre projet. Si vous utilisez Yarn, vous pouvez remplacer npm install par Yarn Add @opensign/react.",
|
||||
"public-template-mssg-3": "Besoin de plus de détails ou d'exemples ?",
|
||||
"public-template-mssg-4": "Visitez le",
|
||||
"public-template-mssg-5": "npm pour les dernières mises à jour, une documentation détaillée et l'historique des versions.",
|
||||
"public-template-mssg-6": "Avant de pouvoir utiliser cet extrait de code, vous devez rendre ce modèle public.",
|
||||
"copy-code": "COPIE",
|
||||
"copied-code": "COPIÉ",
|
||||
"Installation": "Installation",
|
||||
"Usage": "Usage",
|
||||
"insufficient-credits": "Crédits de signature insuffisants",
|
||||
"insufficient-credits-mssg": "Le propriétaire de ce document ne dispose actuellement pas des crédits OpenSign nécessaires pour que vous puissiez le signer. Veuillez contacter le propriétaire si vous avez besoin de plus de détails.",
|
||||
"angular-npm-mssg-1": "Pour intégrer OpenSign dans votre projet Angular, exécutez simplement la commande suivante :",
|
||||
"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":"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"
|
||||
|
||||
|
||||
"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 {{adminEmail}}.",
|
||||
"sent-this-month": "envoyé ce mois-ci",
|
||||
"available-seats": "Disponible sièges",
|
||||
"buy-users": "Acheter plus d'utilisateurs",
|
||||
"isenable-otp": "Activer la vérification OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Souhaitez-vous activer le processus de vérification à l'aide d'un mot de passe à usage unique (OTP)?",
|
||||
"p2": "La sélection de cette option activera la vérification OTP. Les utilisateurs recevront un code de vérification par e-mail, qu'ils devront saisir pour signer le document.",
|
||||
"p3": "La sélection de cette option désactivera la vérification OTP, permettant aux utilisateurs de signer le document directement sans étapes supplémentaires.",
|
||||
"p4": "Veuillez choisir l'option qui correspond le mieux à vos exigences en matière de signature de documents."
|
||||
},
|
||||
"advanced-options": "Options avancées",
|
||||
"hide-advanced-options": "Masquer les options avancées",
|
||||
"download-files": "Télécharger des fichiers",
|
||||
"download-pdf": "Télécharger le PDF",
|
||||
"pdf-certificate": "Télécharger Pdf + Certificat",
|
||||
"document-logs": "Journaux de documents",
|
||||
"server-down": "Impossible de se connecter au serveur OpenSign. Si vous hébergez vous-même OpenSign, veuillez vous assurer que toutes les étapes de la documentation ont été correctement suivies. Si vous exécutez OpenSign localement, vous y accédez peut-être via un numéro de port incorrect.",
|
||||
"admin-exists": "L'administrateur existe déjà. Veuillez vous connecter à l'application en utilisant les informations d'identification de l'administrateur afin de gérer les utilisateurs.",
|
||||
"enable-tour": "Activer la visite",
|
||||
"istourenabled-otp-help":{
|
||||
"p1": "Cela permettra une visite guidée pour les signataires, fournissant des instructions pendant le processus de signature.",
|
||||
"p2": "Cela désactivera la visite guidée, garantissant ainsi une expérience de signature plus rapide et ininterrompue.",
|
||||
"p3": "Conseil : si vos signataires connaissent OpenSign et l'utilisent fréquemment, vous souhaiterez peut-être désactiver la visite guidée pour une expérience plus fluide. Choisissez l'option qui correspond le mieux à vos besoins en matière de signature de documents."
|
||||
},
|
||||
"form-title-1": "Paramètre du flux de documents",
|
||||
"form-title-2": "Paramètre de sécurité",
|
||||
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
|
||||
"add-user-template": "Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
|
||||
"pdf-uncompatible": "Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
animation: inAnimation 2s ease-in-out;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const cloudServerUrl = 'http://localhost:8080/app';
|
||||
export const appName = process.env.APP_NAME || 'OpenSign™';
|
||||
export function customAPIurl() {
|
||||
const url = new URL(cloudServerUrl);
|
||||
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
|
||||
@@ -42,36 +43,38 @@ export function replaceMailVaribles(subject, body, variables) {
|
||||
export const saveFileUsage = async (size, fileUrl, userId) => {
|
||||
//checking server url and save file's size
|
||||
try {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
if (userId) {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in fetch tenant Id', err);
|
||||
|
||||
@@ -53,7 +53,7 @@ async function sendMail(doc, signer) {
|
||||
receiver_phone: signer?.Phone || '',
|
||||
expiry_date: localExpireDate,
|
||||
company_name: doc?.ExtUserPtr?.Company || '',
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`,
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
|
||||
};
|
||||
const mail = replaceMailVaribles(subject, body, variables);
|
||||
|
||||
@@ -88,7 +88,7 @@ export default async function autoReminder(request, response) {
|
||||
docQuery.exists('Signers');
|
||||
docQuery.exists('SignedUrl');
|
||||
docQuery.descending('createdAt');
|
||||
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr');
|
||||
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr,ExtUserPtr.TenantId');
|
||||
docQuery.notEqualTo('IsCompleted', true);
|
||||
docQuery.notEqualTo('IsDeclined', true);
|
||||
docQuery.notEqualTo('IsArchive', true);
|
||||
|
||||
@@ -8,6 +8,7 @@ import saveInvoice from './saveInvoice.js';
|
||||
import savePayments from './savePayments.js';
|
||||
import gooogleauth from './googleauth.js';
|
||||
import autoReminder from './autoReminder.js';
|
||||
import validateSmtp from './validateSmtp.js';
|
||||
export const app = express();
|
||||
|
||||
dotenv.config();
|
||||
@@ -22,3 +23,4 @@ app.post('/saveinvoice', saveInvoice);
|
||||
app.post('/savepayment', savePayments);
|
||||
app.post('/googleauth', gooogleauth);
|
||||
app.post('/sendreminder', autoReminder);
|
||||
app.post('/validatesmtp', validateSmtp);
|
||||
|
||||
@@ -10,7 +10,6 @@ import getContact from './routes/getContact.js';
|
||||
import deleteContact from './routes/deleteContact.js';
|
||||
import getContactList from './routes/getContactList.js';
|
||||
import draftDocument from './routes/draftDocument.js';
|
||||
import createTemplate from './routes/createTemplate.js';
|
||||
import getTemplate from './routes/getTemplate.js';
|
||||
import deletedTemplate from './routes/deleteTemplate.js';
|
||||
import getTemplatetList from './routes/getTemplateList.js';
|
||||
@@ -81,9 +80,6 @@ app.get('/documentlist/:doctype', getDocumentList);
|
||||
// create Template with co-ordinate
|
||||
app.post('/createtemplate', createTemplatewithCoordinate);
|
||||
|
||||
// create Template
|
||||
app.post('/drafttemplate', createTemplate);
|
||||
|
||||
// create Template with binary
|
||||
app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplatewithCoordinate);
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
if (totalcredits > 0) {
|
||||
const templateQuery = new Parse.Query('contracts_Template');
|
||||
templateQuery.include('ExtUserPtr');
|
||||
templateQuery.include('ExtUserPtr.TenantId');
|
||||
const templateRes = await templateQuery.get(templateId, { useMasterKey: true });
|
||||
if (templateRes) {
|
||||
const template = JSON.parse(JSON.stringify(templateRes));
|
||||
@@ -196,6 +197,14 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
if (TimeToCompleteDays) {
|
||||
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||
}
|
||||
const enableOTP = request.body?.enableOTP;
|
||||
const IsEnableOTP =
|
||||
enableOTP !== undefined ? enableOTP : template?.IsEnableOTP || false;
|
||||
const enableTour = request.body?.enableTour;
|
||||
const isTourEnabled =
|
||||
enableTour !== undefined ? enableTour : template?.IsTourEnabled || false;
|
||||
object.set('IsTourEnabled', isTourEnabled);
|
||||
object.set('IsEnableOTP', IsEnableOTP);
|
||||
object.set('CreatedBy', template.CreatedBy);
|
||||
object.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
@@ -205,6 +214,9 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
if (folderId) {
|
||||
object.set('Folder', folderPtr);
|
||||
}
|
||||
if (template?.FileAdapterId) {
|
||||
object.set('FileAdapterId', template?.FileAdapterId);
|
||||
}
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
@@ -265,7 +277,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
@@ -312,6 +324,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: template.ExtUserPtr.objectId,
|
||||
mailProvider: template?.ExtUserPtr?.active_mail_adapter || '',
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
sanitizeFileName,
|
||||
cloudServerUrl,
|
||||
} from '../../../../Utils.js';
|
||||
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
|
||||
|
||||
// `sendDoctoWebhook` is used to send res data of document on webhook
|
||||
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
|
||||
@@ -63,6 +64,8 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
const email_body = request.body.email_body;
|
||||
const sendInOrder = request.body.sendInOrder || false;
|
||||
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
@@ -86,6 +89,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
};
|
||||
const extUsers = new Parse.Query('contracts_Users');
|
||||
extUsers.equalTo('UserId', userPtr);
|
||||
extUsers.include('TenantId');
|
||||
const extUser = await extUsers.first({ useMasterKey: true });
|
||||
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
@@ -113,6 +117,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
.status(400)
|
||||
.json({ error: 'Please add at least one signature widget for all signers' });
|
||||
}
|
||||
const parseExtUser = JSON.parse(JSON.stringify(extUser));
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
@@ -125,23 +130,29 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const filename = sanitizeFileName(`${name}.pdf`);
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
let adapter = {};
|
||||
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
|
||||
if (ActiveFileAdapter) {
|
||||
adapter =
|
||||
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
|
||||
}
|
||||
if (adapter?.id) {
|
||||
const filedata = Buffer.from(base64File, 'base64');
|
||||
// `uploadFileToS3` is used to save document in user's file storage
|
||||
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
|
||||
} else {
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
}
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const parseExtUser = JSON.parse(JSON.stringify(extUser));
|
||||
|
||||
const extUserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
};
|
||||
|
||||
const object = new Parse.Object('contracts_Document');
|
||||
object.set('Name', name);
|
||||
|
||||
@@ -162,7 +173,12 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
if (TimeToCompleteDays) {
|
||||
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||
}
|
||||
object.set('IsEnableOTP', IsEnableOTP);
|
||||
object.set('IsTourEnabled', isTourEnabled);
|
||||
object.set('IsSendMail', send_email);
|
||||
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
|
||||
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
|
||||
}
|
||||
let contact = [];
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
@@ -323,7 +339,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
@@ -369,6 +385,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: extUser.id,
|
||||
mailProvider: parseExtUser?.active_mail_adapter || '',
|
||||
};
|
||||
|
||||
await axios.post(url, params, { headers: headers });
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
import { saveFileUsage } from '../../../../Utils.js';
|
||||
export default async function createTemplate(request, response) {
|
||||
const name = request.body?.title;
|
||||
const note = request.body?.note;
|
||||
const description = request.body?.description;
|
||||
const signers = request.body?.signers;
|
||||
const folderId = request.body?.folderId;
|
||||
const base64File = request.body.file;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
|
||||
try {
|
||||
const reqToken = request.headers['x-api-token'];
|
||||
if (!reqToken) {
|
||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||
}
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('token', reqToken);
|
||||
tokenQuery.include('userId');
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// Valid Token then proceed request
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: parseUser.userId.objectId,
|
||||
};
|
||||
let fileUrl;
|
||||
if (base64File) {
|
||||
const file = new Parse.File(`${name}.pdf`, {
|
||||
base64: base64File,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const extUserPtr = { __type: 'Pointer', className: 'contracts_Users', objectId: extUser.id };
|
||||
|
||||
const folderPtr = { __type: 'Pointer', className: 'contracts_Template', objectId: folderId };
|
||||
|
||||
const object = new Parse.Object('contracts_Template');
|
||||
object.set('Name', name);
|
||||
if (note) {
|
||||
object.set('Note', note);
|
||||
}
|
||||
if (description) {
|
||||
object.set('Description', description);
|
||||
}
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
if (SendinOrder) {
|
||||
object.set('SendinOrder', SendinOrder);
|
||||
}
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
if (base64File) {
|
||||
parseSigners = signers;
|
||||
} else {
|
||||
parseSigners = JSON.parse(signers);
|
||||
}
|
||||
let createContactUrl = protocol + '/v1/createcontact';
|
||||
|
||||
let contact = [];
|
||||
for (const obj of parseSigners) {
|
||||
const body = {
|
||||
name: obj?.name || '',
|
||||
email: obj?.email || '',
|
||||
phone: obj?.phone || '',
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(createContactUrl, body, {
|
||||
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
|
||||
});
|
||||
// console.log('res ', res.data);
|
||||
contact.push({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: res.data?.objectId,
|
||||
});
|
||||
} catch (err) {
|
||||
// console.log('err ', err.response);
|
||||
if (err?.response?.data?.objectId) {
|
||||
contact.push({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: err.response.data?.objectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
object.set('Signers', contact);
|
||||
}
|
||||
if (folderId) {
|
||||
object.set('Folder', folderPtr);
|
||||
}
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
newACL.setReadAccess(userPtr.objectId, true);
|
||||
newACL.setWriteAccess(userPtr.objectId, true);
|
||||
object.setACL(newACL);
|
||||
const res = await object.save(null, { useMasterKey: true });
|
||||
if (request.posthog) {
|
||||
request.posthog?.capture({
|
||||
distinctId: parseUser.userId.email,
|
||||
event: 'api_draft_template',
|
||||
properties: { response_code: 200 },
|
||||
});
|
||||
}
|
||||
return response.json({
|
||||
objectId: res.id,
|
||||
url: baseUrl.origin + '/template/' + res.id,
|
||||
});
|
||||
} else {
|
||||
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
formatWidgetOptions,
|
||||
sanitizeFileName,
|
||||
} from '../../../../Utils.js';
|
||||
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
|
||||
|
||||
const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
export default async function createTemplatewithCoordinate(request, response) {
|
||||
@@ -17,6 +18,9 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
const base64File = request.body.file;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
|
||||
@@ -38,6 +42,11 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
objectId: parseUser.userId.objectId,
|
||||
};
|
||||
if (signers && signers.length > 0) {
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
contractsUser.include('TenantId');
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const parseExtUser = JSON.parse(JSON.stringify(extUser));
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
@@ -50,24 +59,31 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const filename = sanitizeFileName(`${name}.pdf`);
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
let adapter = {};
|
||||
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
|
||||
if (ActiveFileAdapter) {
|
||||
adapter =
|
||||
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
|
||||
}
|
||||
if (adapter?.id) {
|
||||
const filedata = Buffer.from(base64File, 'base64');
|
||||
// `uploadFileToS3` is used to save document in user's file storage
|
||||
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
|
||||
} else {
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
}
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const extUserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
};
|
||||
|
||||
const object = new Parse.Object('contracts_Template');
|
||||
object.set('Name', name);
|
||||
|
||||
if (note) {
|
||||
object.set('Note', note);
|
||||
}
|
||||
@@ -80,6 +96,8 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
object.set('IsTourEnabled', isTourEnabled);
|
||||
let contact = [];
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
@@ -183,6 +201,9 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
objectId: folderId,
|
||||
});
|
||||
}
|
||||
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
|
||||
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
|
||||
}
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
|
||||
@@ -20,6 +20,7 @@ export default async function deleteDocument(request, response) {
|
||||
const Document = new Parse.Query('contracts_Document');
|
||||
Document.equalTo('objectId', request.params.document_id);
|
||||
Document.equalTo('CreatedBy', userPtr);
|
||||
Document.include('ExtUserPtr.TenantId');
|
||||
const res = await Document.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const isArchive = res.get('IsArchive');
|
||||
|
||||
@@ -22,6 +22,7 @@ export default async function deleteFolder(request, response) {
|
||||
obj.equalTo('objectId', folderId);
|
||||
obj.equalTo('IsArchive', true);
|
||||
obj.equalTo('CreatedBy', userPtr);
|
||||
obj.include('ExtUserPtr.TenantId');
|
||||
const isFolderDeleted = await obj.first({ useMasterKey: true });
|
||||
if (!isFolderDeleted) {
|
||||
const folder = new Parse.Query('contracts_Document');
|
||||
@@ -34,6 +35,7 @@ export default async function deleteFolder(request, response) {
|
||||
folder.notEqualTo('IsArchive', true);
|
||||
folder.equalTo('CreatedBy', userPtr);
|
||||
folder.equalTo('Type', 'Folder');
|
||||
folder.include('ExtUserPtr.TenantId');
|
||||
const isSubItems = await folder.first({ useMasterKey: true });
|
||||
// console.log('isSubItems ', isSubItems);
|
||||
if (isSubItems) {
|
||||
|
||||
@@ -19,6 +19,7 @@ export default async function deletedTemplate(request, response) {
|
||||
const template = new Parse.Query('contracts_Template');
|
||||
template.equalTo('objectId', request.params.template_id);
|
||||
template.equalTo('CreatedBy', userPtr);
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const isArchive = res.get('IsArchive');
|
||||
|
||||
@@ -12,6 +12,9 @@ export default async function draftDocument(request, response) {
|
||||
const send_email = request.body.send_email || true;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
@@ -83,6 +86,8 @@ export default async function draftDocument(request, response) {
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsSendMail', send_email);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
object.set('IsTourEnabled', isTourEnabled);
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
if (base64File) {
|
||||
|
||||
@@ -24,6 +24,7 @@ export default async function getDocument(request, response) {
|
||||
Document.include('Folder');
|
||||
Document.include('ExtUserPtr');
|
||||
Document.include('Placeholders.signerPtr');
|
||||
Document.include('ExtUserPtr.TenantId');
|
||||
const res = await Document.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const document = JSON.parse(JSON.stringify(res));
|
||||
@@ -62,8 +63,10 @@ export default async function getDocument(request, response) {
|
||||
document?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
|
||||
[],
|
||||
sendInOrder: document?.SendinOrder || false,
|
||||
enableOTP: document?.IsEnableOTP || false,
|
||||
createdAt: document.createdAt,
|
||||
updatedAt: document.updatedAt,
|
||||
enableTour: document?.IsTourEnabled || false,
|
||||
});
|
||||
} else {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -62,7 +62,7 @@ export default async function getDocumentList(request, response) {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||
};
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys},Placeholders&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys},Placeholders&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
|
||||
try {
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
if (res.data && res.data.results.length > 0) {
|
||||
@@ -100,8 +100,10 @@ export default async function getDocumentList(request, response) {
|
||||
x?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
|
||||
[],
|
||||
sendInOrder: x?.SendinOrder || false,
|
||||
enableOTP: x?.IsEnableOTP || false,
|
||||
createdAt: x.createdAt,
|
||||
updatedAt: x.updatedAt,
|
||||
enableTour: x?.IsTourEnabled || false,
|
||||
}));
|
||||
return response.json({ result: updateRes });
|
||||
} else {
|
||||
|
||||
@@ -25,7 +25,7 @@ export default async function getFolder(request, response) {
|
||||
folderCls.notEqualTo('IsArchive', true);
|
||||
folderCls.descending('createdAt');
|
||||
folderCls.include('Folder');
|
||||
|
||||
folderCls.include('ExtUserPtr.TenantId');
|
||||
const res = await folderCls.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const parseRes = JSON.parse(JSON.stringify(res));
|
||||
|
||||
@@ -35,6 +35,7 @@ export default async function getFolderList(request, response) {
|
||||
folderCls.doesNotExist('Folder');
|
||||
}
|
||||
folderCls.descending('createdAt');
|
||||
folderCls.include('ExtUserPtr.TenantId');
|
||||
folderCls.limit(limit);
|
||||
folderCls.skip(skip);
|
||||
const res = await folderCls.find({ useMasterKey: true });
|
||||
|
||||
@@ -25,6 +25,7 @@ export default async function getTemplate(request, response) {
|
||||
Template.include('Folder');
|
||||
Template.include('ExtUserPtr');
|
||||
Template.include('Placeholders.signerPtr');
|
||||
Template.include('ExtUserPtr.TenantId');
|
||||
const res = await Template.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const template = JSON.parse(JSON.stringify(res));
|
||||
@@ -61,8 +62,10 @@ export default async function getTemplate(request, response) {
|
||||
),
|
||||
})) || [],
|
||||
sendInOrder: template?.SendinOrder || false,
|
||||
enableOTP: template?.IsEnableOTP || false,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
enableTour: template?.IsTourEnabled || false,
|
||||
});
|
||||
} else {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -53,7 +53,7 @@ export default async function getTemplatetList(request, response) {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||
};
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
if (res.data && res.data.results.length > 0) {
|
||||
if (request.posthog) {
|
||||
@@ -88,8 +88,10 @@ export default async function getTemplatetList(request, response) {
|
||||
),
|
||||
})) || [],
|
||||
sendInOrder: template?.SendinOrder || false,
|
||||
enableOTP: template?.IsEnableOTP || false,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
enableTour: template?.IsTourEnabled || false,
|
||||
}));
|
||||
|
||||
return response.json({ result: updateRes });
|
||||
|
||||
@@ -35,12 +35,13 @@ export default async function resendMail(request, response) {
|
||||
docQuery.notEqualTo('IsArchive', true);
|
||||
docQuery.greaterThanOrEqualTo('ExpiryDate', new Date());
|
||||
docQuery.exists('SignedUrl');
|
||||
|
||||
docQuery.include('ExtUserPtr.TenantId');
|
||||
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||
// console.log("resDoc ",resDoc)
|
||||
if (resDoc) {
|
||||
const _resDoc = resDoc.toJSON();
|
||||
const contact = _resDoc.Signers.find(x => x.Email === userMail);
|
||||
const activeMailAdapter = _resDoc?.ExtUserPtr?.active_mail_adapter || '';
|
||||
if (contact) {
|
||||
try {
|
||||
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
|
||||
@@ -85,7 +86,7 @@ export default async function resendMail(request, response) {
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
@@ -131,6 +132,7 @@ export default async function resendMail(request, response) {
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: _resDoc.ExtUserPtr.objectId,
|
||||
mailProvider: activeMailAdapter,
|
||||
};
|
||||
|
||||
const res = await axios.post(url, params, { headers: headers });
|
||||
|
||||
@@ -10,7 +10,7 @@ export default async function updateDocument(request, response) {
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// Valid Token then proceed request
|
||||
const allowedKeys = ['name', 'note', 'description', 'folderId'];
|
||||
const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP', 'enableTour'];
|
||||
const objectKeys = Object.keys(request.body);
|
||||
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
@@ -23,6 +23,7 @@ export default async function updateDocument(request, response) {
|
||||
const document = new Parse.Query('contracts_Document');
|
||||
document.equalTo('objectId', request.params.document_id);
|
||||
document.equalTo('CreatedBy', userPtr);
|
||||
document.include('ExtUserPtr.TenantId');
|
||||
const res = await document.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const isArchive = res.get('IsArchive');
|
||||
@@ -48,6 +49,12 @@ export default async function updateDocument(request, response) {
|
||||
objectId: request?.body?.folderId,
|
||||
});
|
||||
}
|
||||
if (request.body?.enableOTP !== undefined) {
|
||||
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
|
||||
}
|
||||
if (request.body?.enableTour !== undefined) {
|
||||
updateQuery.set('IsTourEnabled', request.body?.enableTour);
|
||||
}
|
||||
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
|
||||
if (updatedRes) {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -25,7 +25,7 @@ export default async function updateFolder(request, response) {
|
||||
folderCls.equalTo('objectId', folderId);
|
||||
folderCls.equalTo('Type', 'Folder');
|
||||
folderCls.notEqualTo('IsArchive', true);
|
||||
|
||||
folderCls.include('ExtUserPtr.TenantId');
|
||||
const res = await folderCls.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const updateFolder = new Parse.Object('contracts_Document');
|
||||
|
||||
@@ -10,7 +10,7 @@ export default async function updateTemplate(request, response) {
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// Valid Token then proceed request
|
||||
const allowedKeys = ['name', 'note', 'description', 'folderId'];
|
||||
const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP', 'enableTour'];
|
||||
const objectKeys = Object.keys(request.body);
|
||||
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
@@ -23,6 +23,7 @@ export default async function updateTemplate(request, response) {
|
||||
const template = new Parse.Query('contracts_Template');
|
||||
template.equalTo('objectId', request.params.template_id);
|
||||
template.equalTo('CreatedBy', userPtr);
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const isArchive = res.get('IsArchive');
|
||||
@@ -48,6 +49,12 @@ export default async function updateTemplate(request, response) {
|
||||
objectId: request?.body?.folderId,
|
||||
});
|
||||
}
|
||||
if (request.body?.enableOTP !== undefined) {
|
||||
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
|
||||
}
|
||||
if (request.body?.enableTour !== undefined) {
|
||||
updateQuery.set('IsTourEnabled', request.body?.enableTour);
|
||||
}
|
||||
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
|
||||
if (updatedRes) {
|
||||
if (request.posthog) {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { appName } from '../../Utils.js';
|
||||
// Online Javascript Editor for free
|
||||
// Write, Edit and Run your Javascript code using JS Online Compiler
|
||||
|
||||
function generateOtpWithSum10() {
|
||||
let otp = [];
|
||||
let sum = 0;
|
||||
|
||||
// First, generate 3 random digits (0-9) and add them to the sum
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let digit = Math.floor(Math.random() * 10);
|
||||
otp.push(digit);
|
||||
sum += digit;
|
||||
}
|
||||
|
||||
// Calculate the last digit to ensure the sum equals 10
|
||||
let lastDigit = 10 - sum;
|
||||
|
||||
// If the last digit is not valid (e.g., greater than 9), regenerate
|
||||
if (lastDigit >= 0 && lastDigit <= 9) {
|
||||
otp.push(lastDigit);
|
||||
} else {
|
||||
return generateOtpWithSum10(); // Recursively generate again
|
||||
}
|
||||
|
||||
return otp.join('');
|
||||
}
|
||||
|
||||
export default async function validateSmtp(request, response) {
|
||||
const host = request.body.host;
|
||||
const port = request.body?.port?.toString();
|
||||
const username = request.body.username;
|
||||
const password = request.body.password;
|
||||
const secure = request.body?.secure;
|
||||
const email = request.body?.email;
|
||||
const otp = generateOtpWithSum10();
|
||||
// console.log('Generated OTP:', otp);
|
||||
|
||||
try {
|
||||
const smtpsecure = secure || port !== '465' ? false : true;
|
||||
const transporterSMTP = createTransport({
|
||||
host: host,
|
||||
port: port,
|
||||
secure: smtpsecure,
|
||||
auth: { user: username, pass: password },
|
||||
});
|
||||
const from = appName;
|
||||
const mailsender = username;
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: email,
|
||||
subject: `Your ${appName} SMTP credentials verification code`,
|
||||
text: 'mail',
|
||||
html:
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>SMTP Verification Code</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your verification code is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
|
||||
otp +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
};
|
||||
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('custom smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
response.status(200).json({ message: 'success' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
const code = err?.responseCode || 400;
|
||||
const message = err?.response || 'failed!';
|
||||
response.status(code).json({ error: message });
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,14 @@ import ExtUserAftersave from './parsefunction/ExtUserAftersave.js';
|
||||
import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js';
|
||||
import AllowedCredits from './parsefunction/AllowedCredits.js';
|
||||
import BuyCredits from './parsefunction/BuyCredits.js';
|
||||
import getContact from './parsefunction/getContact.js';
|
||||
import updateContactTour from './parsefunction/updateContactTour.js';
|
||||
import declinedocument from './parsefunction/declinedocument.js';
|
||||
import addcustomsmtp from './parsefunction/addcustomsmtp.js';
|
||||
import deactivateMailAdapter from './parsefunction/deactivateMailAdapter.js';
|
||||
import addFileAdapter from './parsefunction/addFileAdapter.js';
|
||||
import saveToFileAdapter from './parsefunction/saveToFileAdapter.js';
|
||||
import getFileAdapter from './parsefunction/getFileAdapter.js';
|
||||
|
||||
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||
@@ -142,3 +150,11 @@ Parse.Cloud.define('allowedusers', AllowedUsers);
|
||||
Parse.Cloud.define('buyaddonusers', BuyAddonUsers);
|
||||
Parse.Cloud.define('allowedcredits', AllowedCredits);
|
||||
Parse.Cloud.define('buycredits', BuyCredits);
|
||||
Parse.Cloud.define('getcontact', getContact);
|
||||
Parse.Cloud.define('updatecontacttour', updateContactTour);
|
||||
Parse.Cloud.define('declinedoc', declinedocument);
|
||||
Parse.Cloud.define('addsmtp', addcustomsmtp);
|
||||
Parse.Cloud.define('deactivatemailadapter', deactivateMailAdapter);
|
||||
Parse.Cloud.define('savetofileadapter', saveToFileAdapter);
|
||||
Parse.Cloud.define('addfileadapter', addFileAdapter);
|
||||
Parse.Cloud.define('getfileadapter', getFileAdapter);
|
||||
|
||||
@@ -2,21 +2,30 @@ import { useLocal } from '../../Utils.js';
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function DocumentAfterFind(request) {
|
||||
if (useLocal !== 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const FileAdapterId = obj?.get('FileAdapterId') || '';
|
||||
if (FileAdapterId || useLocal !== 'true') {
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
let fileAdapter = {};
|
||||
if (FileAdapterId) {
|
||||
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
|
||||
if (tenantId) {
|
||||
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
|
||||
fileAdapter = _tenantId?.FileAdapters?.find(x => x.id === FileAdapterId) || {};
|
||||
}
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
obj.set('URL', getPresignedUrl(Url, fileAdapter));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ async function DocumentAftersave(request) {
|
||||
const ExpiryDate = new Date(createdAt);
|
||||
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
documentQuery.include('ExtUserPtr.TenantId');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
if (!originIp) {
|
||||
@@ -30,6 +31,7 @@ async function DocumentAftersave(request) {
|
||||
const ExpiryDate = new Date(createdAt);
|
||||
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
documentQuery.include('ExtUserPtr.TenantId');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
if (!originIp) {
|
||||
@@ -76,6 +78,7 @@ async function DocumentAftersave(request) {
|
||||
// console.log(objId)
|
||||
const Query = new Parse.Query('contracts_Document');
|
||||
Query.include('Signers');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
Query.include('CreatedBy');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
@@ -116,6 +119,7 @@ async function DocumentAftersave(request) {
|
||||
// console.log(objId)
|
||||
const Query = new Parse.Query('contracts_Document');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
|
||||
@@ -20,6 +20,7 @@ async function GetPublicTemplate(request) {
|
||||
templatQuery.descending('updatedAt');
|
||||
templatQuery.equalTo('IsPublic', true);
|
||||
templatQuery.notEqualTo('IsArchive', true);
|
||||
templatQuery.include('ExtUserPtr.TenantId');
|
||||
const getTemplate = await templatQuery.find({ useMasterKey: true });
|
||||
const extcls = new Parse.Query('contracts_Users');
|
||||
extcls.equalTo('Email', user.get('email'));
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', userRes.data.email);
|
||||
extUserQuery.include('TeamIds');
|
||||
@@ -48,12 +48,14 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
}
|
||||
}
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
// console.log("res ",res)
|
||||
return res;
|
||||
const templateRes = JSON.parse(JSON.stringify(res));
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapters;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
@@ -69,10 +71,13 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
// console.log("res ", res)
|
||||
if (res) {
|
||||
return res;
|
||||
const templateRes = JSON.parse(JSON.stringify(res));
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapters;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
|
||||
@@ -31,7 +31,13 @@ const saveRoleContact = async contact => {
|
||||
contactQuery.set('CreatedBy', contact.CreatedBy);
|
||||
contactQuery.set('UserId', contact.UserId);
|
||||
contactQuery.set('UserRole', 'contracts_Guest');
|
||||
contactQuery.set('TenantId', contact.TenantId);
|
||||
if (contact?.TenantId) {
|
||||
contactQuery.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: contact.TenantId,
|
||||
});
|
||||
}
|
||||
contactQuery.set('IsDeleted', false);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setReadAccess(contact.CreatedBy.objectId, true);
|
||||
@@ -55,13 +61,16 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
|
||||
object.set('Description', template?.Description);
|
||||
object.set('Note', template?.Note);
|
||||
object.set('TimeToCompleteDays', template?.TimeToCompleteDays || 15);
|
||||
object.set('SendinOrder', template?.SendinOrder);
|
||||
object.set('SendinOrder', template?.SendinOrder || false);
|
||||
object.set('AutomaticReminders', template?.AutomaticReminders || false);
|
||||
object.set('RemindOnceInEvery', template?.RemindOnceInEvery || 5);
|
||||
object.set('URL', template?.URL);
|
||||
object.set('CreatedBy', template?.CreatedBy);
|
||||
object.set('ExtUserPtr', template?.ExtUserPtr);
|
||||
object.set('OriginIp', template?.OriginIp || '');
|
||||
object.set('IsEnableOTP', template?.IsEnableOTP || false);
|
||||
object.set('IsTourEnabled', template?.IsTourEnabled || false);
|
||||
object.set('FileAdapterId', template?.FileAdapterId || '');
|
||||
let signers = template?.Signers || [];
|
||||
const signerobj = {
|
||||
__type: 'Pointer',
|
||||
@@ -96,7 +105,7 @@ const sendMailToAllSigners = async docId => {
|
||||
try {
|
||||
//get document details that recenlty created from public template
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.include('ExtUserPtr');
|
||||
docQuery.include('ExtUserPtr,ExtUserPtr.TenantId');
|
||||
docQuery.include('Signers');
|
||||
const docRes = await docQuery.get(docId, { useMasterKey: true });
|
||||
const Doc = JSON.parse(JSON.stringify(docRes));
|
||||
@@ -167,7 +176,7 @@ const sendMailToAllSigners = async docId => {
|
||||
receiver_phone: signerMail[i]?.Phone || '',
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`,
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
|
||||
};
|
||||
replaceVar = replaceMailVaribles(requestSubject, htmlReqBody, variables);
|
||||
}
|
||||
@@ -221,6 +230,25 @@ const sendMailToAllSigners = async docId => {
|
||||
}
|
||||
};
|
||||
|
||||
const deductcount = async _resSub => {
|
||||
try {
|
||||
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
|
||||
subscriptionCls.id = _resSub.objectId;
|
||||
const allowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const addonCredits = _resSub?.AddonCredits || 0;
|
||||
if (allowedCredits > 0) {
|
||||
const updateAllowedcredits = allowedCredits - 1 || 0;
|
||||
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
|
||||
} else {
|
||||
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
|
||||
subscriptionCls.set('AddonCredits', updateAddonCredits);
|
||||
}
|
||||
await subscriptionCls.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('Err in deductcount in PublicUserLinkContacttoDoc cloud function', err);
|
||||
}
|
||||
};
|
||||
|
||||
// `PublicUserLinkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
|
||||
// create new document from template and save contact pointer in placeholder, signers and ACL of Document
|
||||
export default async function PublicUserLinkContactToDoc(req) {
|
||||
@@ -234,132 +262,170 @@ export default async function PublicUserLinkContactToDoc(req) {
|
||||
// Execute the query to get the template with the specified 'templateid'
|
||||
const docQuery = new Parse.Query('contracts_Template');
|
||||
docQuery.include('ExtUserPtr');
|
||||
docQuery.include('ExtUserPtr.TenantId');
|
||||
const tempRes = await docQuery.get(templateid, { useMasterKey: true });
|
||||
// Check if the template was found; if not, throw an error indicating the template was not found
|
||||
if (!tempRes) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
|
||||
}
|
||||
const _tempRes = JSON.parse(JSON.stringify(tempRes));
|
||||
const Placeholders = _tempRes?.Placeholders || [];
|
||||
let index;
|
||||
if (role) {
|
||||
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
|
||||
}
|
||||
if (index !== -1) {
|
||||
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
|
||||
const contactCls = new Parse.Query('contracts_Contactbook');
|
||||
contactCls.equalTo('Email', email);
|
||||
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
|
||||
contactCls.notEqualTo('IsDeleted', true);
|
||||
const existContact = await contactCls.first({ useMasterKey: true });
|
||||
if (existContact) {
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
|
||||
if (docRes) {
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: existContact.id, docId: docRes.id };
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
subscription.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: _tempRes?.ExtUserPtr?.TenantId?.objectId,
|
||||
});
|
||||
subscription.include('ExtUserPtr');
|
||||
const date = new Date();
|
||||
subscription.greaterThanOrEqualTo('Next_billing_date', date);
|
||||
const resSub = await subscription.first({ useMasterKey: true });
|
||||
if (resSub) {
|
||||
const _resSub = JSON.parse(JSON.stringify(resSub));
|
||||
const allowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const addonCredits = _resSub?.AddonCredits || 0;
|
||||
const totalcredits = allowedCredits + addonCredits;
|
||||
if (totalcredits > 0) {
|
||||
const Placeholders = _tempRes?.Placeholders || [];
|
||||
let index;
|
||||
if (role) {
|
||||
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
|
||||
}
|
||||
} else {
|
||||
// Execute the query to check if a user already exists in the 'contracts_Users' class
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', email);
|
||||
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const contact = {
|
||||
UserId: _extUser.UserId,
|
||||
Name: _extUser.Name,
|
||||
Email: email,
|
||||
Phone: _extUser?.Phone ? _extUser.Phone : '',
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr.TenantId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
// if user present on platform create contact on the basis of extended user details
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
|
||||
if (docRes) {
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
if (index !== -1) {
|
||||
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
|
||||
const contactCls = new Parse.Query('contracts_Contactbook');
|
||||
contactCls.equalTo('Email', email);
|
||||
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
|
||||
contactCls.notEqualTo('IsDeleted', true);
|
||||
const existContact = await contactCls.first({ useMasterKey: true });
|
||||
if (existContact) {
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: existContact.id, docId: docRes.id };
|
||||
}
|
||||
return { contactId: contactRes.id, docId: docRes.id };
|
||||
}
|
||||
} else if (name) {
|
||||
try {
|
||||
// Execute the query to check if a user already exists in the '_User' class
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
userQuery.equalTo('email', email);
|
||||
const userRes = await userQuery.first({ useMasterKey: true });
|
||||
if (userRes) {
|
||||
} else {
|
||||
// Execute the query to check if a user already exists in the 'contracts_Users' class
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', email);
|
||||
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const contact = {
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
|
||||
Name: name,
|
||||
UserId: _extUser.UserId,
|
||||
Name: _extUser.Name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
Phone: _extUser?.Phone ? _extUser.Phone : '',
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr.TenantId,
|
||||
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
// if user present on platform create contact on the basis of extended user details
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: contactRes.id, docId: docRes.id };
|
||||
}
|
||||
} else if (name) {
|
||||
try {
|
||||
// Execute the query to check if a user already exists in the '_User' class
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
userQuery.equalTo('email', email);
|
||||
const userRes = await userQuery.first({ useMasterKey: true });
|
||||
if (userRes) {
|
||||
const contact = {
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(
|
||||
template_json,
|
||||
contactRes,
|
||||
index
|
||||
);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: contactRes.id, docId: docRes.id };
|
||||
}
|
||||
} else {
|
||||
// create new user in _User class on the basis of details provide by user
|
||||
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 newUserRes = await _user.save();
|
||||
const contact = {
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(
|
||||
template_json,
|
||||
contactRes,
|
||||
index
|
||||
);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: contactRes.id, docId: docRes.id };
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err', err);
|
||||
}
|
||||
} else {
|
||||
// create new user in _User class on the basis of details provide by user
|
||||
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 newUserRes = await _user.save();
|
||||
const contact = {
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
|
||||
Name: name,
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr.TenantId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
|
||||
if (docRes) {
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
if (!template_json?.SendinOrder) {
|
||||
await sendMailToAllSigners(docRes.id);
|
||||
}
|
||||
return { contactId: contactRes.id, docId: docRes.id };
|
||||
}
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err', err);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
throw new Parse.Error(
|
||||
Parse.Error.OBJECT_NOT_FOUND,
|
||||
'Please provide required parameters!'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.REQUEST_LIMIT_EXCEEDED, 'Insufficient Credit');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Plan expired');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { smtpenable, updateMailCount } from '../../Utils.js';
|
||||
import { appName, smtpenable, updateMailCount } from '../../Utils.js';
|
||||
async function getDocument(docId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
@@ -7,6 +7,7 @@ async function getDocument(docId) {
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
query.include('Placeholders');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
@@ -28,12 +29,12 @@ async function sendMailOTPv1(request) {
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
try {
|
||||
await Parse.Cloud.sendEmail({
|
||||
from: 'Opensign™' + ' <' + mailsender + '>',
|
||||
from: appName + ' <' + mailsender + '>',
|
||||
recipient: recipient,
|
||||
subject: 'Your OpenSign™ OTP',
|
||||
subject: `Your ${appName} OTP`,
|
||||
text: 'This email is a test.',
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" +
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for ${appName} verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
|
||||
code +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
});
|
||||
|
||||
@@ -2,23 +2,31 @@ import { useLocal } from '../../Utils.js';
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function TemplateAfterFind(request) {
|
||||
if (useLocal !== 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const FileAdapterId = obj?.get('FileAdapterId') || '';
|
||||
if (FileAdapterId || useLocal !== 'true') {
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
let fileAdapter = {};
|
||||
if (FileAdapterId) {
|
||||
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
|
||||
if (tenantId) {
|
||||
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
|
||||
fileAdapter = _tenantId?.FileAdapters?.find(x => x.id === FileAdapterId) || {};
|
||||
}
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
obj.set('URL', getPresignedUrl(Url, fileAdapter));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export default async function TemplateAfterSave(request) {
|
||||
const Query = new Parse.Query('contracts_Template');
|
||||
Query.include('Signers');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
@@ -85,6 +86,7 @@ export default async function TemplateAfterSave(request) {
|
||||
|
||||
const Query = new Parse.Query('contracts_Template');
|
||||
Query.include('CreatedBy');
|
||||
Query.include('ExtUserPtr.TenantId');
|
||||
const updateACL = await Query.get(objId, { useMasterKey: true });
|
||||
const res = JSON.parse(JSON.stringify(updateACL));
|
||||
// console.log("res");
|
||||
|
||||
@@ -51,7 +51,10 @@ export default async function UpdateExistUserAsAdmin(request) {
|
||||
extClsQuery.notEqualTo('IsDisabled', true);
|
||||
const extAdminRes = await extClsQuery.find({ useMasterKey: true });
|
||||
if (extAdminRes && extAdminRes.length === 1 && extAdminRes?.[0]?.get('OrganizationId')) {
|
||||
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Admin already exist.');
|
||||
throw new Parse.Error(
|
||||
Parse.Error.DUPLICATE_VALUE,
|
||||
'Admin already exists. Please login to the application using admin credentials in order to manage users.'
|
||||
);
|
||||
} else {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('Email', email);
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// `generateId` is used to unique Id for fileAdapter
|
||||
function generateId(length) {
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
let result = '';
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export default async function addFileAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const fileAdapterName = request.params.fileAdapterName;
|
||||
const bucketName = request.params.bucketName;
|
||||
const region = request.params.region;
|
||||
const endpoint = request.params.endpoint;
|
||||
const baseUrl = request.params.baseUrl;
|
||||
const accessKeyId = request.params.accessKeyId;
|
||||
const secretAccessKey = request.params.secretAccessKey;
|
||||
const adapter = request.params.fileAdapter;
|
||||
|
||||
if (fileAdapterName && accessKeyId && secretAccessKey && adapter) {
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
extUserCls.include('TenantId');
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const tenantCls = new Parse.Object('partners_Tenant');
|
||||
tenantCls.id = extUser?.get('TenantId')?.id;
|
||||
const existsFileAdaptes = _extUser?.TenantId?.FileAdapters
|
||||
? _extUser?.TenantId?.FileAdapters
|
||||
: [];
|
||||
|
||||
// assign existing file adapters or empty array
|
||||
let updatedFileAdapters = existsFileAdaptes;
|
||||
const uniqueId = generateId(10);
|
||||
let id = extUser?.get('TenantId')?.id + '_' + uniqueId;
|
||||
const index = updatedFileAdapters?.findIndex(x => x.fileAdapterName === fileAdapterName);
|
||||
|
||||
if (index !== -1) {
|
||||
// If an object with the same fileAdapterName exists, update it
|
||||
if (bucketName || region || endpoint || baseUrl) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
'Cannot update bucketName, region, endpoint, baseUrl.'
|
||||
);
|
||||
} else {
|
||||
const adapterConfig = { accessKeyId: accessKeyId, secretAccessKey: secretAccessKey };
|
||||
updatedFileAdapters[index] = { ...updatedFileAdapters[index], ...adapterConfig };
|
||||
id = updatedFileAdapters[index].id;
|
||||
}
|
||||
} else {
|
||||
if (bucketName && region && endpoint && baseUrl) {
|
||||
// If the object with the given fileAdapterName doesn't exist, add a new one
|
||||
updatedFileAdapters.push({
|
||||
id: id,
|
||||
fileAdapterName: fileAdapterName,
|
||||
fileAdapter: adapter,
|
||||
bucketName: bucketName,
|
||||
region: region,
|
||||
endpoint: endpoint,
|
||||
baseUrl: baseUrl,
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey,
|
||||
});
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
|
||||
}
|
||||
}
|
||||
tenantCls.set('FileAdapters', updatedFileAdapters);
|
||||
if (adapter) {
|
||||
tenantCls.set('ActiveFileAdapter', id);
|
||||
} else {
|
||||
tenantCls.unset('ActiveFileAdapter');
|
||||
}
|
||||
const updateTenant = await tenantCls.save(null, { useMasterKey: true });
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
tenantSchema.setCLP({
|
||||
get: { '*': true },
|
||||
find: { '*': true },
|
||||
count: { '*': true },
|
||||
create: { '*': true },
|
||||
update: { '*': true },
|
||||
delete: { '*': true },
|
||||
addField: { '*': true },
|
||||
protectedFields: { '*': ['FileAdapters'] },
|
||||
});
|
||||
await tenantSchema.update();
|
||||
const ActiveFileAdapter = adapter === 'opensign' ? 'opensign' : id;
|
||||
return { ActiveFileAdapter: ActiveFileAdapter, updateAt: updateTenant.updatedAt };
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in add custom file adapter', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else if (adapter === 'opensign') {
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
extUserCls.include('TenantId');
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const tenantCls = new Parse.Object('partners_Tenant');
|
||||
tenantCls.id = extUser?.get('TenantId')?.id;
|
||||
tenantCls.unset('ActiveFileAdapter');
|
||||
const updateTenant = await tenantCls.save(null, { useMasterKey: true });
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
tenantSchema.setCLP({
|
||||
get: { '*': true },
|
||||
find: { '*': true },
|
||||
count: { '*': true },
|
||||
create: { '*': true },
|
||||
update: { '*': true },
|
||||
delete: { '*': true },
|
||||
addField: { '*': true },
|
||||
protectedFields: { '*': ['FileAdapters'] },
|
||||
});
|
||||
await tenantSchema.update();
|
||||
return { ActiveFileAdapter: 'opensign', updateAt: updateTenant.updatedAt };
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in add custom file adapter', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export default async function addcustomsmtp(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const host = request.params.host;
|
||||
const port = request.params.port;
|
||||
const username = request.params.username;
|
||||
const password = request.params.password;
|
||||
if (host && port && username && password) {
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const extUserCls = new Parse.Object('contracts_Users');
|
||||
extUserCls.id = extUser.id;
|
||||
extUserCls.set('SmtpConfig', { host, port, username, password });
|
||||
extUserCls.set('active_mail_adapter', 'smtp');
|
||||
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
|
||||
// console.log('updateExtUser ', updateExtUser);
|
||||
return updateExtUser.updatedAt;
|
||||
}
|
||||
return extUser;
|
||||
} catch (err) {
|
||||
console.log('Err in add custom smtp', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
|
||||
}
|
||||
}
|
||||
@@ -7,99 +7,102 @@ export default async function callWebhook(request) {
|
||||
const contactId = request.params.contactId;
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
|
||||
const userId = userRes.data && userRes.data.objectId;
|
||||
if (userId) {
|
||||
if (event === 'viewed' && contactId) {
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
const res = await docQuery.get(docId, { useMasterKey: true });
|
||||
if (res) {
|
||||
const _res = res.toJSON();
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const obj = {
|
||||
UserPtr: userPtr,
|
||||
SignedUrl: _res.SignedUrl,
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ViewedOn: date,
|
||||
};
|
||||
const isUserExist = _res?.AuditTrail?.some(
|
||||
x => x.UserPtr.objectId === contactId && x?.ViewedOn
|
||||
);
|
||||
if (!isUserExist) {
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = res.id;
|
||||
if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) {
|
||||
updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]);
|
||||
} else {
|
||||
updateDoc.set('AuditTrail', [obj]);
|
||||
try {
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.include('ExtUserPtr.TenantId');
|
||||
const docRes = await docQuery.get(docId, { useMasterKey: true });
|
||||
const isEnableOTP = docRes?.get('IsEnableOTP') || false;
|
||||
let userId;
|
||||
if (isEnableOTP) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
userId = userRes.data && userRes.data.objectId;
|
||||
}
|
||||
if (!isEnableOTP || userId) {
|
||||
if (event === 'viewed' && contactId) {
|
||||
if (docRes) {
|
||||
const _docRes = docRes.toJSON();
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const obj = {
|
||||
UserPtr: userPtr,
|
||||
SignedUrl: _docRes.SignedUrl,
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ViewedOn: date,
|
||||
};
|
||||
const isUserExist = _docRes?.AuditTrail?.some(
|
||||
x => x.UserPtr.objectId === contactId && x?.ViewedOn
|
||||
);
|
||||
if (!isUserExist) {
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = docRes.id;
|
||||
if (_docRes?.AuditTrail && _docRes?.AuditTrail?.length > 0) {
|
||||
updateDoc.set('AuditTrail', [..._docRes?.AuditTrail, obj]);
|
||||
} else {
|
||||
updateDoc.set('AuditTrail', [obj]);
|
||||
}
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
const resDoc = await docQuery.get(docId, { useMasterKey: true });
|
||||
const extendcls = new Parse.Query('contracts_Users');
|
||||
extendcls.equalTo('objectId', resDoc.get('ExtUserPtr')?.id);
|
||||
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const res = await extendcls.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const extUser = JSON.parse(JSON.stringify(res));
|
||||
if (extUser?.Webhook) {
|
||||
const params = {
|
||||
event: event,
|
||||
...body,
|
||||
};
|
||||
await axios
|
||||
.post(extUser?.Webhook, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
// console.log('res ', res);
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
const extendcls = new Parse.Query('contracts_Users');
|
||||
extendcls.equalTo('objectId', docRes.get('ExtUserPtr')?.id);
|
||||
// extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const resExt = await extendcls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const extUser = JSON.parse(JSON.stringify(resExt));
|
||||
if (extUser?.Webhook) {
|
||||
const params = { event: event, ...body };
|
||||
await axios
|
||||
.post(extUser?.Webhook, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
return { message: 'webhook called!' };
|
||||
}
|
||||
return { message: 'webhook called!' };
|
||||
} else {
|
||||
return { message: 'User not found!' };
|
||||
}
|
||||
} else {
|
||||
return { message: 'User not found!' };
|
||||
} catch (err) {
|
||||
console.log('Err in callwebhook', err);
|
||||
return { message: 'Something went wrong!' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ async function sendMail(document, sessionToken) {
|
||||
const orgName = document.ExtUserPtr.Company ? document.ExtUserPtr.Company : '';
|
||||
const themeBGcolor = '#47a3ad';
|
||||
let params = {
|
||||
mailProvider: document?.ExtUserPtr?.active_mail_adapter || '',
|
||||
extUserId: document.ExtUserPtr.objectId,
|
||||
recipient: objectId ? existSigner?.Email : signerMail[i].email,
|
||||
subject: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`,
|
||||
@@ -94,7 +95,7 @@ async function sendMail(document, sessionToken) {
|
||||
orgName +
|
||||
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
localExpireDate +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
|
||||
signPdf +
|
||||
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender +
|
||||
@@ -199,6 +200,9 @@ export default async function createBatchDocs(request) {
|
||||
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
IsTourEnabled: x?.IsTourEnabled || false,
|
||||
FileAdapterId: x?.FileAdapterId || '',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export default async function deactivateMailAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const extUserCls = new Parse.Object('contracts_Users');
|
||||
extUserCls.id = extUser.id;
|
||||
extUserCls.unset('active_mail_adapter');
|
||||
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
|
||||
// console.log('updateExtUser ', updateExtUser);
|
||||
return updateExtUser.updatedAt;
|
||||
}
|
||||
return extUser;
|
||||
} catch (err) {
|
||||
console.log('Err in add custom smtp', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export default async function declinedocument(request) {
|
||||
const docId = request.params.docId;
|
||||
const reason = request.params?.reason || '';
|
||||
const declineBy = {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: request.params?.userId,
|
||||
};
|
||||
|
||||
try {
|
||||
const docCls = new Parse.Query('contracts_Document');
|
||||
docCls.include('ExtUserPtr.TenantId');
|
||||
const updateDoc = await docCls.get(docId, { useMasterKey: true });
|
||||
if (updateDoc) {
|
||||
const isEnableOTP = updateDoc?.get('IsEnableOTP') || false;
|
||||
if (!isEnableOTP) {
|
||||
updateDoc.set('IsDeclined', true);
|
||||
updateDoc.set('DeclineReason', reason);
|
||||
updateDoc.set('DeclineBy', declineBy);
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
return 'document declined';
|
||||
} else {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
updateDoc.set('IsDeclined', true);
|
||||
updateDoc.set('DeclineReason', reason);
|
||||
updateDoc.set('DeclineBy', declineBy);
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
return 'document declined';
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err while decling doc', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export default async function getContact(request) {
|
||||
const contactId = request.params.contactId;
|
||||
try {
|
||||
const contactCls = new Parse.Query('contracts_Contactbook');
|
||||
const contactRes = await contactCls.get(contactId, { useMasterKey: true });
|
||||
return contactRes;
|
||||
} catch (err) {
|
||||
console.log('Err in contracts_Contactbook class ', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,12 @@ export default async function getDocument(request) {
|
||||
const docId = request.params.docId;
|
||||
|
||||
try {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
const userId = userRes.data && userRes.data.objectId;
|
||||
if (docId && userId) {
|
||||
if (docId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
query.include('ExtUserPtr');
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
@@ -26,11 +20,34 @@ export default async function getDocument(request) {
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const acl = res.getACL();
|
||||
if (acl && acl.getReadAccess(userId)) {
|
||||
return res;
|
||||
const IsEnableOTP = res?.get('IsEnableOTP') || false;
|
||||
const document = JSON.parse(JSON.stringify(res));
|
||||
delete document.ExtUserPtr.TenantId.FileAdapters;
|
||||
if (!IsEnableOTP) {
|
||||
return document;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
if (request?.headers?.['sessiontoken']) {
|
||||
try {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
const userId = userRes.data && userRes.data?.objectId;
|
||||
const acl = res.getACL();
|
||||
if (userId && acl && acl.getReadAccess(userId)) {
|
||||
return document;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err user in not authenticated', err);
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
|
||||
@@ -18,9 +18,9 @@ export default async function getDrive(request) {
|
||||
if (userId) {
|
||||
let url;
|
||||
if (docId) {
|
||||
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers,Folder&skip=${skip}&limit=${limit}`;
|
||||
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,ExtUserPtr.TenantId,Signers,Folder&order=-updatedAt&skip=${skip}&limit=${limit}`;
|
||||
} else {
|
||||
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers&skip=${skip}&limit=${limit}`;
|
||||
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,ExtUserPtr.TenantId,Signers&order=-updatedAt&skip=${skip}&limit=${limit}`;
|
||||
}
|
||||
try {
|
||||
const res = await axios.get(url, {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export default async function getFileAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
extUserCls.include('TenantId');
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const FileAdapters =
|
||||
_extUser?.TenantId?.FileAdapters?.length > 0 ? _extUser?.TenantId?.FileAdapters : [];
|
||||
const ActiveFileAdapter = _extUser?.TenantId?.ActiveFileAdapter || 'opensign';
|
||||
return { ActiveFileAdapter: ActiveFileAdapter, FileAdapters: FileAdapters };
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in add custom file adapter', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
}
|
||||
@@ -52,11 +52,7 @@ export default async function getReport(request) {
|
||||
} else {
|
||||
strParams = JSON.stringify({
|
||||
...params,
|
||||
CreatedBy: {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
},
|
||||
CreatedBy: { __type: 'Pointer', className: '_User', objectId: userId },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -66,7 +62,7 @@ export default async function getReport(request) {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': masterKey,
|
||||
};
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
|
||||
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
|
||||
const res = await axios.get(url, { headers: headers });
|
||||
if (res.data && res.data.results) {
|
||||
return res.data.results;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import { useLocal } from '../../Utils.js';
|
||||
export default function getPresignedUrl(url) {
|
||||
export default function getPresignedUrl(url, adapter) {
|
||||
const credentials = {
|
||||
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
||||
accessKeyId: adapter?.accessKeyId || process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: adapter?.secretAccessKey || process.env.DO_SECRET_ACCESS_KEY,
|
||||
};
|
||||
AWS.config.update({ credentials: credentials, region: process.env.DO_REGION });
|
||||
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
AWS.config.update({ credentials: credentials, region: adapter?.region || process.env.DO_REGION });
|
||||
const spacesEndpoint = adapter?.endpoint || new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint });
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function getPresignedUrl(url) {
|
||||
|
||||
// presignedGETURL return presignedUrl with expires time
|
||||
const presignedGETURL = s3.getSignedUrl('getObject', {
|
||||
Bucket: process.env.DO_SPACE,
|
||||
Bucket: adapter?.bucketName || process.env.DO_SPACE,
|
||||
Key: filename, //filename
|
||||
Expires: 160, //time to expire in seconds
|
||||
});
|
||||
@@ -28,15 +28,67 @@ export default function getPresignedUrl(url) {
|
||||
|
||||
export async function getSignedUrl(request) {
|
||||
try {
|
||||
const docId = request.params.docId || '';
|
||||
const templateId = request.params.templateId || '';
|
||||
const url = request.params.url;
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
const fileAdapterId = request.params.fileAdapterId || '';
|
||||
if (docId || templateId) {
|
||||
try {
|
||||
if (fileAdapterId || useLocal !== 'true') {
|
||||
const query = new Parse.Query(docId ? 'contracts_Document' : 'contracts_Template');
|
||||
query.equalTo('objectId', docId ? docId : templateId);
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const _resDoc = JSON.parse(JSON.stringify(res));
|
||||
if (_resDoc?.IsEnableOTP) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_SESSION_TOKEN,
|
||||
'User is not authenticated.'
|
||||
);
|
||||
} else {
|
||||
let adapterConfig = {};
|
||||
if (fileAdapterId) {
|
||||
// `adapterConfig` is used to get file in user's fileAdapter
|
||||
adapterConfig =
|
||||
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(
|
||||
x => x.id === fileAdapterId
|
||||
) || {};
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
return presignedUrl;
|
||||
}
|
||||
} else {
|
||||
let adapterConfig = {};
|
||||
if (fileAdapterId) {
|
||||
// `adapterConfig` is used to get file in user's fileAdapter
|
||||
adapterConfig =
|
||||
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === fileAdapterId) ||
|
||||
{};
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
return presignedUrl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in presigned url', err);
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
return url;
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user