mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-03 08:18:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
affbfc5061 | ||
|
|
886ede2007 | ||
|
|
a3936c66aa | ||
|
|
d98453ffc2 | ||
|
|
7424912455 | ||
|
|
e095495081 | ||
|
|
eb4ce5544f | ||
|
|
8eef17834d | ||
|
|
40ed5e2353 | ||
|
|
2aa4c8c361 | ||
|
|
e5b120fa36 | ||
|
|
1e026f8ebd | ||
|
|
e5c7da0847 | ||
|
|
2e67002768 | ||
|
|
9c03649d48 | ||
|
|
df436b3b20 | ||
|
|
34acf4170e | ||
|
|
f9a13c880a | ||
|
|
df7cd45be6 | ||
|
|
b22d70c833 | ||
|
|
1ef8e8aa6a | ||
|
|
2aa58b7eb3 | ||
|
|
9fb61509c4 | ||
|
|
b99dc875ed | ||
|
|
7da31f00c3 | ||
|
|
894ccd1244 | ||
|
|
8d90e6e808 | ||
|
|
79771f725e | ||
|
|
786f56941a | ||
|
|
8fae910d46 | ||
|
|
8b5920bba3 | ||
|
|
03f1451e64 | ||
|
|
1d2a975b5f | ||
|
|
3db32979dc | ||
|
|
110431dae2 | ||
|
|
ade228f756 | ||
|
|
9b18a66f22 | ||
|
|
c88de90a24 | ||
|
|
737d66d97d | ||
|
|
b77b57d3fd | ||
|
|
240c7766c1 | ||
|
|
508b2e43d3 | ||
|
|
d6129e21e9 | ||
|
|
a11a2444f1 | ||
|
|
db1fb1c0e1 | ||
|
|
7f326b19aa | ||
|
|
2a1d7d2502 | ||
|
|
01edfce6e7 | ||
|
|
9176b8f99c | ||
|
|
0ff2e57424 | ||
|
|
567966866c | ||
|
|
e03f07a8ef | ||
|
|
9915a700bb | ||
|
|
f0a406a397 | ||
|
|
851ea24be4 | ||
|
|
50c4eff790 | ||
|
|
e69e6998c9 | ||
|
|
c87873cfb6 | ||
|
|
b09becf3eb | ||
|
|
0673242f41 | ||
|
|
1776931df5 | ||
|
|
a2c0921345 | ||
|
|
ad0a974c14 | ||
|
|
2d4a7d336c | ||
|
|
8d2df87de3 | ||
|
|
873e7e2068 | ||
|
|
3fcb1d0138 | ||
|
|
ce41f9a0ce | ||
|
|
34d8ed19bf | ||
|
|
ea9f34f0b3 | ||
|
|
4e9cbd23f2 | ||
|
|
bbd65c01e9 | ||
|
|
5dc30b7702 | ||
|
|
eb3b31bd59 | ||
|
|
57597dfef0 | ||
|
|
8cb7a610c7 | ||
|
|
2c464978b4 | ||
|
|
8d0bf6abe8 | ||
|
|
82c724a768 | ||
|
|
6146fe5f86 | ||
|
|
24ce6eae3b | ||
|
|
8db934a2c2 | ||
|
|
dc90d412ca | ||
|
|
b8415c8995 | ||
|
|
0d86f39ba3 | ||
|
|
0bab033673 | ||
|
|
72a0ce3f51 | ||
|
|
c3a14b82b3 | ||
|
|
b3252e276b | ||
|
|
10c63a66f6 | ||
|
|
f44205b90d | ||
|
|
cd087d410f | ||
|
|
c5812ba938 | ||
|
|
2e4c7f7690 | ||
|
|
a7389efb00 | ||
|
|
532062687c | ||
|
|
d3a603d115 | ||
|
|
26fece78c5 | ||
|
|
b8fb626591 | ||
|
|
732786067f | ||
|
|
45c0cd48c7 | ||
|
|
50d7982dd5 | ||
|
|
ff085b1284 | ||
|
|
c3564b1f8f | ||
|
|
32eba21e7a | ||
|
|
9952afcc38 | ||
|
|
b3feccab0d | ||
|
|
86ebd4903b | ||
|
|
0533bb9c30 | ||
|
|
185e90494c | ||
|
|
3f77b497ce | ||
|
|
87a8333cdf | ||
|
|
3d12261aa7 | ||
|
|
19c6cbbcd3 | ||
|
|
39c191db7c | ||
|
|
58c36266e0 | ||
|
|
0ebdfffdeb | ||
|
|
ed840f3048 | ||
|
|
1e13b509df | ||
|
|
d76a9a4628 | ||
|
|
f70e20498e | ||
|
|
1715b39629 | ||
|
|
3c63db8c9c | ||
|
|
7af78c95dd | ||
|
|
1c4a377641 | ||
|
|
bf56937585 | ||
|
|
e97442b0c8 | ||
|
|
d029ce5380 | ||
|
|
20049921ad | ||
|
|
05a0db75db | ||
|
|
a7b8100a18 | ||
|
|
112369b283 | ||
|
|
f9b915b728 | ||
|
|
8acdbc928e | ||
|
|
9665e14e6d | ||
|
|
b45e05cc9e | ||
|
|
116f4d94ea | ||
|
|
f18b5b49c5 | ||
|
|
09a4db7c03 | ||
|
|
2616a5776b | ||
|
|
36cfc4a867 | ||
|
|
631f118afb | ||
|
|
b43e3a61e6 | ||
|
|
04100adece | ||
|
|
a103f93925 | ||
|
|
8467340fb5 | ||
|
|
3a8e41de27 | ||
|
|
97c2f183ca | ||
|
|
f1ab35e71e | ||
|
|
240d892b94 | ||
|
|
364d96e7f6 | ||
|
|
03dfd58ac5 | ||
|
|
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 | ||
|
|
a5f8e4fc58 | ||
|
|
0c472ba22c | ||
|
|
634744c22d | ||
|
|
85ad7ca187 | ||
|
|
66a56757d1 | ||
|
|
088ec5580a |
Generated
+722
-764
File diff suppressed because it is too large
Load Diff
+25
-22
@@ -4,16 +4,16 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@lottiefiles/dotlottie-react": "^0.9.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.9.3",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.1.3",
|
||||
"@radix-ui/themes": "^3.1.4",
|
||||
"@react-pdf/renderer": "^4.0.0",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"@reduxjs/toolkit": "^2.3.0",
|
||||
"axios": "^1.7.7",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^23.15.1",
|
||||
"i18next": "^23.16.5",
|
||||
"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",
|
||||
@@ -25,8 +25,8 @@
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.5",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-datepicker": "^7.4.0",
|
||||
"react-cookie": "^7.2.2",
|
||||
"react-datepicker": "^7.5.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
@@ -34,25 +34,25 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^15.0.2",
|
||||
"react-i18next": "^15.0.3",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-pdf": "^9.1.1",
|
||||
"react-quill-new": "^3.3.2",
|
||||
"react-quill-new": "^3.3.3",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-rnd": "^10.4.13",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"react-router-dom": "^6.27.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.8.1",
|
||||
"react-select": "^5.8.3",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
"serve": "^14.2.3",
|
||||
"serve": "^14.2.4",
|
||||
"styled-components": "^5.3.0",
|
||||
"web-vitals": "^4.2.3",
|
||||
"ws": "^8.18.0"
|
||||
@@ -95,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.6",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/preset-react": "^7.25.9",
|
||||
"@babel/runtime-corejs2": "^7.26.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-loader": "^9.2.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.0.1",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"daisyui": "^4.12.14",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint": "^9.13.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.37.0",
|
||||
"eslint-plugin-react": "^7.37.1",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss": "^8.4.48",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"overrides": {
|
||||
"nth-check": "$nth-check",
|
||||
"ws": "$ws"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "My Signature",
|
||||
"API Token": "API Token",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferences",
|
||||
"Teams": "Teams",
|
||||
"Team-update-successfully": "Team Updated successfully.",
|
||||
"Users": "Users"
|
||||
@@ -291,7 +292,7 @@
|
||||
"subject": "Subject",
|
||||
"body": "Body",
|
||||
"add-contact": "Add Contact",
|
||||
"add-signer-alert": "Contact already exist!",
|
||||
"add-signer-alert": "Contact already exist! Please select it from ‘Signers’ dropdown",
|
||||
"record-delete-alert": "Record deleted successfully!",
|
||||
"record-revoke-alert": "Record revoked successfully!",
|
||||
"mail-sent-alert": "Mail sent successfully.",
|
||||
@@ -445,7 +446,8 @@
|
||||
"cutomize-email": "Cutomize Email",
|
||||
"upgrade-to-customize-email": "Upgrade to customize Email",
|
||||
"sign-url": "Sign url",
|
||||
"Mails Sent": "Mails Sent",
|
||||
"mails-sent": "Mails sent",
|
||||
"mail-not-delivered": "Mail not delivered",
|
||||
"document-alert": "Document Alert",
|
||||
"owner-subscription-expired": "Owner's subscription has expired.",
|
||||
"subscription-expired": "Subscription Expired",
|
||||
@@ -672,7 +674,28 @@
|
||||
"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 Settings",
|
||||
"public-tour-message":"The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it."
|
||||
|
||||
"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>",
|
||||
"text-field-tour":"Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
|
||||
"attach-signer-tour":"You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
|
||||
"allowed-signature-types": "Allowed signature types",
|
||||
"at-least-one-signature-type": "At least one signature type should be enabled.",
|
||||
"expect-default-one-more-signature-type": "Please enable one additional signature type apart from the default.",
|
||||
"add-signer-note": "Note - This contact will be added to your contact list",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "This signature preference controls which signing options are available to your signers. Only the signature types you select will appear in the signature widget when a document is signed. For example, if you choose only the 'Draw' option in preferences, your signer will see only the 'Draw' option in the signature widget, while the other three options will not be available.",
|
||||
"l1": "Create a handwritten signature directly on the screen using your finger or a stylus.",
|
||||
"l2": "Enter your name and choose from various font styles to create a signature.",
|
||||
"l3": "Upload an existing digital version of your signature.",
|
||||
"l4": "Use a pre-set signature style saved under 'My Signature' in your settings."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferencias",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
@@ -291,7 +292,7 @@
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe!",
|
||||
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
@@ -445,7 +446,8 @@
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"Mails Sent": "Correos enviados",
|
||||
"mails-sent": "Correos enviados",
|
||||
"mail-not-delivered": "correo no entregado",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
@@ -673,5 +675,27 @@
|
||||
"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."
|
||||
"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",
|
||||
"text-field-tour":"Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
|
||||
"attach-signer-tour" :"Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
|
||||
"allowed-signature-types": "Tipos de firma permitidos",
|
||||
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
|
||||
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
|
||||
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
|
||||
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
|
||||
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
|
||||
"l3": "Cargue una versión digital existente de su firma.",
|
||||
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "Ma signature",
|
||||
"API Token": "Jeton API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Préférences",
|
||||
"Teams": "Équipe",
|
||||
"Team-update-successfully": "Équipe mise à jour avec succès.",
|
||||
"Users": "Utilisateurs"
|
||||
@@ -290,7 +291,7 @@
|
||||
"subject": "Sujet",
|
||||
"body": "Corps",
|
||||
"add-contact": "Ajouter le contact",
|
||||
"add-signer-alert": "Le contact existe déjà !",
|
||||
"add-signer-alert": "Le contact existe déjà ! Veuillez le sélectionner dans la liste déroulante 'Signataires'",
|
||||
"record-delete-alert": "Enregistrement supprimé avec succès !",
|
||||
"record-revoke-alert": " Enregistrement révoqué avec succès ! ",
|
||||
"mail-sent-alert": "Courriel envoyé avec succès.",
|
||||
@@ -444,7 +445,8 @@
|
||||
"cutomize-email": "Personnaliser l'e-mail",
|
||||
"upgrade-to-customize-email": "Mise à niveau pour personnaliser l'e-mail",
|
||||
"sign-url": "Signer l'URL",
|
||||
"Mails Sent": "Mails envoyés avec succès !",
|
||||
"mails-sent": "Mails envoyés avec succès !",
|
||||
"mail-not-delivered": "Courrier non distribué",
|
||||
"document-alert": "Alerte document",
|
||||
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
|
||||
"subscription-expired": "Abonnement expiré",
|
||||
@@ -671,8 +673,28 @@
|
||||
"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. "
|
||||
|
||||
|
||||
"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>",
|
||||
"text-field-tour" :"Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
|
||||
"attach-signer-tour" :"Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
|
||||
"allowed-signature-types": "Types de signature autorisés",
|
||||
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
|
||||
"expect-default-one-signature-type": "Veuillez activer un type de signature supplémentaire en plus de celui par défaut.",
|
||||
"add-signer-note": "Remarque - Ce contact sera ajouté à votre liste de contacts",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Cette préférence de signature contrôle les options de signature disponibles pour vos signataires. Seuls les types de signature que vous sélectionnez apparaîtront dans le widget de signature lorsqu'un document est signé. Par exemple, si vous choisissez uniquement l'option 'Draw' dans les préférences, votre signataire ne verra que l'option 'Draw' dans le widget de signature, tandis que les trois autres options ne seront pas disponibles.",
|
||||
"l1": "Créez une signature manuscrite directement sur l'écran à l'aide de votre doigt ou d'un stylet.",
|
||||
"l2": "Entrez votre nom et choisissez parmi différents styles de police pour créer une signature.",
|
||||
"l3": "Téléchargez une version numérique existante de votre signature.",
|
||||
"l4": "Utilisez un style de signature prédéfini enregistré sous 'Ma signature' dans vos paramètres."
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -35,6 +35,8 @@ const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||
const DraftTemplate = lazy(() => import("./pages/DraftTemplate"));
|
||||
const Preferences = lazy(() => import("./pages/Preferences"));
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||
const AppLoader = () => {
|
||||
@@ -198,8 +200,16 @@ function App() {
|
||||
</>
|
||||
)}
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route
|
||||
path="/preferences"
|
||||
element={<LazyPage Page={Preferences} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/sso" element={<SSOVerify />} />
|
||||
<Route
|
||||
path="/drafttemplate/:jwttoken"
|
||||
element={<DraftTemplate />}
|
||||
/>
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddSigner = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -47,139 +48,143 @@ const AddSigner = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
}
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
contactQuery.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -206,6 +211,7 @@ const AddSigner = (props) => {
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto p-2 text-base-content">
|
||||
<p className="mb-3">{t("add-signer-note")}</p>
|
||||
{isUserExist && (
|
||||
<div className="mb-3 flex items-center">
|
||||
<input
|
||||
|
||||
@@ -3,7 +3,7 @@ import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { copytoData, fetchSubscriptionInfo } from "../constant/Utils";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
@@ -115,149 +115,172 @@ const AddUser = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsFormLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
|
||||
setIsFormLoader(false);
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
try {
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsFormLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
|
||||
setIsFormLoader(false);
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
|
||||
} else {
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsFormLoader(false);
|
||||
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
||||
}
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("something-went-wrong-mssg")
|
||||
});
|
||||
} finally {
|
||||
setTimeout(
|
||||
() => props.setIsAlert({ type: "success", msg: "" }),
|
||||
1500
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("something-went-wrong-mssg")
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
const BulkSendUi = (props) => {
|
||||
@@ -69,7 +69,21 @@ const BulkSendUi = (props) => {
|
||||
if (subscription?.plan === "freeplan") {
|
||||
setIsFreePlan(true);
|
||||
}
|
||||
const resCredits = await Parse.Cloud.run("allowedcredits");
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const axiosres = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/allowedcredits`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
}
|
||||
}
|
||||
);
|
||||
const resCredits = axiosres.data && axiosres.data.result;
|
||||
if (resCredits) {
|
||||
const allowedcredits = resCredits?.allowedcredits || 0;
|
||||
const addoncredits = resCredits?.addoncredits || 0;
|
||||
@@ -81,7 +95,7 @@ const BulkSendUi = (props) => {
|
||||
}
|
||||
setAmount((obj) => ({ ...obj, totalcredits: totalcredits }));
|
||||
}
|
||||
const getPlaceholder = props.item?.Placeholders;
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
@@ -97,7 +111,7 @@ const BulkSendUi = (props) => {
|
||||
} else {
|
||||
setIsBulkAvailable(true);
|
||||
setAdmin((obj) => ({ ...obj, isAdmin: true }));
|
||||
const getPlaceholder = props.item?.Placeholders;
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
@@ -188,75 +202,99 @@ const BulkSendUi = (props) => {
|
||||
const updatedForms = forms.filter((_, i) => i !== index);
|
||||
setForms(updatedForms);
|
||||
};
|
||||
|
||||
function validateEmails(data) {
|
||||
for (const item of data) {
|
||||
for (const field of item.fields) {
|
||||
if (!emailRegex.test(field.email)) {
|
||||
alert(`Invalid email found: ${field.email}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmit(true);
|
||||
|
||||
// Create a copy of Placeholders array from props.item
|
||||
let Placeholders = [...props.item.Placeholders];
|
||||
// Initialize an empty array to store updated documents
|
||||
let Documents = [];
|
||||
// Loop through each form
|
||||
forms.forEach((form) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
});
|
||||
// Map through the copied Placeholders array to update email values
|
||||
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||
// Find the field in the current form that matches the placeholder Id
|
||||
const field = form.fields.find(
|
||||
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||
);
|
||||
// If a matching field is found, update the email value in the placeholder
|
||||
const signer = field?.signer?.objectId ? field.signer : "";
|
||||
if (field) {
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...placeholder,
|
||||
email: field.email,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
if (validateEmails(forms)) {
|
||||
// Create a copy of Placeholders array from props.item
|
||||
let Placeholders = [...props.Placeholders];
|
||||
// Initialize an empty array to store updated documents
|
||||
let Documents = [];
|
||||
// Loop through each form
|
||||
forms.forEach((form) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
}
|
||||
// If no matching field is found, keep the placeholder as is
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
// Push a new document object with updated Placeholders into the Documents array
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
|
||||
}
|
||||
});
|
||||
await batchQuery(Documents);
|
||||
// Map through the copied Placeholders array to update email values
|
||||
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||
// Find the field in the current form that matches the placeholder Id
|
||||
const field = form.fields.find(
|
||||
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||
);
|
||||
// If a matching field is found, update the email value in the placeholder
|
||||
const signer = field?.signer?.objectId ? field.signer : "";
|
||||
if (field) {
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...placeholder,
|
||||
email: field.email,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
}
|
||||
}
|
||||
// If no matching field is found, keep the placeholder as is
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
// Push a new document object with updated Placeholders into the Documents array
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
SignatureType: props.signatureType
|
||||
});
|
||||
}
|
||||
});
|
||||
await batchQuery(Documents);
|
||||
} else {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const batchQuery = async (Documents) => {
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const functionsUrl = `${serverUrl}functions/batchdocuments`;
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const functionsUrl = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}functions/batchdocuments`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
...token
|
||||
};
|
||||
const params = { Documents: JSON.stringify(Documents) };
|
||||
try {
|
||||
@@ -362,6 +400,7 @@ const BulkSendUi = (props) => {
|
||||
fieldIndex
|
||||
)
|
||||
}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import QuotaCard from "../primitives/QuotaCard";
|
||||
|
||||
const Header = ({ showSidebar }) => {
|
||||
const Header = ({ showSidebar, setIsMenu }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
@@ -40,7 +40,7 @@ const Header = ({ showSidebar }) => {
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
showSidebar();
|
||||
setIsMenu(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
function DefaultSignature(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const confirmToaddDefaultSign = () => {
|
||||
if (xyPostion.length > 0) {
|
||||
setDefaultSignAlert({
|
||||
if (props?.xyPostion.length > 0) {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("default-sign-alert")
|
||||
});
|
||||
} else {
|
||||
setDefaultSignAlert({
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("please-select-position!")
|
||||
});
|
||||
@@ -22,22 +22,30 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
{t("signature")}
|
||||
</div>
|
||||
<div className="flex flex-col items-center mt-[10px] font-semibold">
|
||||
<div className="flex flex-col items-center mt-[10px] font-semibold relative">
|
||||
<p className="text-base-content">{t("Your-Signature")}</p>
|
||||
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
|
||||
<img
|
||||
alt="signature"
|
||||
className="w-full h-full object-contain"
|
||||
src={defaultSignImg}
|
||||
src={props?.defaultSignImg}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
|
||||
onClick={() => confirmToaddDefaultSign()}
|
||||
disabled={!props?.isDefault}
|
||||
>
|
||||
{t("auto-sign-all")}
|
||||
</button>
|
||||
{!props.isDefault && (
|
||||
<div className="absolute bg-black/70 text-white w-full h-full flex items-center justify-center text-[11px] cursor-default">
|
||||
<span className="-rotate-45">
|
||||
Document creator has disabled this option
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -339,9 +339,9 @@ function DropdownWidgetOption(props) {
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
"12"
|
||||
12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -5,11 +5,8 @@ import { isEnableSubscription } from "../../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
const EditTemplate = ({ template, onSuccess, jwttoken }) => {
|
||||
const { t } = useTranslation();
|
||||
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
Name: template?.Name || "",
|
||||
Note: template?.Note || "",
|
||||
@@ -17,7 +14,10 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||
AutomaticReminders: template?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false"
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
||||
IsTourEnabled: template?.IsTourEnabled
|
||||
? `${template?.IsTourEnabled}`
|
||||
: "false"
|
||||
});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -26,23 +26,20 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
}, []);
|
||||
const fetchSubscription = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
const subscribe = await checkIsSubscribed(jwttoken);
|
||||
setIsSubscribe(subscribe.isValid);
|
||||
}
|
||||
};
|
||||
const handleStrInput = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
// const handleFolder = (data) => {
|
||||
// console.log("handleFolder ", data)
|
||||
// setFolder(data);
|
||||
// };
|
||||
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
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 = {};
|
||||
@@ -56,6 +53,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
...formData,
|
||||
SendinOrder: isChecked,
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
IsTourEnabled: isTourEnabled,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
@@ -80,7 +78,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
</div>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("name")}
|
||||
{t("Title")}
|
||||
<span className="text-[13px] text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -153,7 +151,6 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
}
|
||||
>
|
||||
{t("auto-reminder")}
|
||||
{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
@@ -256,6 +253,59 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
</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")}
|
||||
|
||||
@@ -30,16 +30,15 @@ function EmailComponent({
|
||||
const pdfName = pdfDetails[0]?.Name;
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
|
||||
const docId = !pdfDetails?.[0]?.IsEnableOTP
|
||||
? pdfDetails?.[0]?.objectId
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const FileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
let presignedUrl = pdfUrl;
|
||||
try {
|
||||
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl, docId: docId },
|
||||
{ url: pdfUrl, docId: docId, fileAdapterId: FileAdapterId },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
|
||||
@@ -40,7 +40,8 @@ function Header({
|
||||
handleRotationFun,
|
||||
isDisableRotate,
|
||||
templateId,
|
||||
setIsDownloadModal
|
||||
setIsDownloadModal,
|
||||
disabledBackBtn
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
@@ -48,6 +49,7 @@ function Header({
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
const enabledBackBtn = disabledBackBtn === true ? false : true;
|
||||
|
||||
//function for show decline alert
|
||||
const handleDeclinePdfAlert = async () => {
|
||||
@@ -332,13 +334,15 @@ function Header({
|
||||
<i className="fa-light fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
{enabledBackBtn && (
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
disabled={isMailSend && true}
|
||||
data-tut="headerArea"
|
||||
|
||||
@@ -331,10 +331,9 @@ function Placeholder(props) {
|
||||
//condition for only placeholder and template flow
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
const checkIndex = props.xyPostion
|
||||
.filter((data) => data.Role !== "prefill")
|
||||
.findIndex((data) => data.Id === props.data.Id);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
//condition to handle in placeholder and template flow for text widget signerId for text widgets i have to set uniqueId in tempSignerId because
|
||||
@@ -342,10 +341,6 @@ function Placeholder(props) {
|
||||
else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setWidgetType(props.pos.type);
|
||||
@@ -361,18 +356,13 @@ function Placeholder(props) {
|
||||
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
const checkIndex = props.xyPostion
|
||||
.filter((data) => data.Role !== "prefill")
|
||||
.findIndex((data) => data.Id === props.data.Id);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
} else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
|
||||
//checking widget's type and open widget copy modal for required widgets
|
||||
@@ -422,7 +412,7 @@ function Placeholder(props) {
|
||||
data?.format,
|
||||
null,
|
||||
null,
|
||||
props.fontSize || props.pos?.options?.fontSize || "12",
|
||||
props.fontSize || props.pos?.options?.fontSize || 12,
|
||||
props.fontColor || props.pos?.options?.fontColor || "black"
|
||||
);
|
||||
setSelectDate({ date: date, format: data?.format });
|
||||
@@ -455,17 +445,13 @@ function Placeholder(props) {
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: "29px",
|
||||
top: "-19px"
|
||||
}}
|
||||
style={{ color: "#188ae2", right: "29px", top: "-19px" }}
|
||||
></i>
|
||||
) : (
|
||||
/* condition to add setting icon for placeholder & template flow for all widgets except signature and date */
|
||||
((!props?.pos?.type && props.pos.isStamp) ||
|
||||
(props?.pos?.type &&
|
||||
!["date", "signature"].includes(props.pos.type) &&
|
||||
!["date"].includes(props.pos.type) &&
|
||||
!props.isSignYourself)) && (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
@@ -479,7 +465,7 @@ function Placeholder(props) {
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: props?.pos?.type === textWidget ? "32px" : "47px",
|
||||
right: props?.pos?.type === textWidget ? "32px" : "51px",
|
||||
top: "-19px"
|
||||
}}
|
||||
></i>
|
||||
@@ -651,6 +637,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.2 + "px";
|
||||
} else {
|
||||
return fontSize + "px";
|
||||
}
|
||||
};
|
||||
|
||||
const getCursor = () => {
|
||||
if (props.data && props.isNeedSign) {
|
||||
@@ -745,12 +746,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,
|
||||
@@ -785,18 +790,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}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
@@ -819,7 +834,7 @@ function Placeholder(props) {
|
||||
)}
|
||||
{isTabAndMobile ? (
|
||||
<div
|
||||
className="flex items-stretch"
|
||||
className="flex items-stretch justify-center"
|
||||
style={{
|
||||
left: xPos(props.pos, props.isSignYourself),
|
||||
top: yPos(props.pos, props.isSignYourself),
|
||||
@@ -862,6 +877,7 @@ function Placeholder(props) {
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -889,6 +905,7 @@ function Placeholder(props) {
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -927,8 +944,8 @@ function Placeholder(props) {
|
||||
<span>{t("font-size")} :</span>
|
||||
<select
|
||||
className="ml-[3px] md:ml:[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={props.fontSize || clickonWidget.options?.fontSize || "12"}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
value={props.fontSize || clickonWidget.options?.fontSize || 12}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -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,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -417,6 +422,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -548,6 +555,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -611,6 +620,8 @@ function RenderPdf({
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
@@ -9,15 +9,13 @@ function SelectLanguage(props) {
|
||||
{ value: "es", text: "Española" },
|
||||
{ value: "fr", text: "Français" }
|
||||
];
|
||||
const [lang, setLang] = useState(i18next.language || "en");
|
||||
const defaultLanguage = i18next.language || "en";
|
||||
const [lang, setLang] = useState(defaultLanguage);
|
||||
// This function put query that helps to change the language
|
||||
const handleChangeLang = (e) => {
|
||||
setLang(e.target.value);
|
||||
i18n.changeLanguage(e.target.value);
|
||||
props?.updateExtUser &&
|
||||
props.updateExtUser({
|
||||
language: e.target.value
|
||||
});
|
||||
props?.updateExtUser && props.updateExtUser({ language: e.target.value });
|
||||
};
|
||||
return (
|
||||
<div
|
||||
@@ -32,9 +30,7 @@ function SelectLanguage(props) {
|
||||
!props.isProfile ? " md:w-[15%] w-[50%]" : "w-[180px]"
|
||||
} op-select op-select-bordered bg-white op-select-sm `}
|
||||
>
|
||||
<option disabled selected>
|
||||
select
|
||||
</option>
|
||||
<option disabled>select</option>
|
||||
{languages.map((item) => {
|
||||
return (
|
||||
<option key={item.value} value={item.value}>
|
||||
|
||||
@@ -1,39 +1,20 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
function SignPad({
|
||||
isSignPad,
|
||||
isStamp,
|
||||
setIsImageSelect,
|
||||
onSaveSign,
|
||||
setIsSignPad,
|
||||
setImage,
|
||||
isImageSelect,
|
||||
imageRef,
|
||||
onImageChange,
|
||||
onSaveImage,
|
||||
image,
|
||||
defaultSign,
|
||||
setSignature,
|
||||
myInitial,
|
||||
isInitial,
|
||||
setIsInitial,
|
||||
setIsStamp,
|
||||
widgetType,
|
||||
currWidgetsDetails,
|
||||
setCurrWidgetsDetails
|
||||
}) {
|
||||
|
||||
function SignPad(props) {
|
||||
const { t } = useTranslation();
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const allColor = ["blue", "red", "black"];
|
||||
const canvasRef = useRef(null);
|
||||
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isTab, setIsTab] = useState("");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(0);
|
||||
const [textHeight, setTextHeight] = useState(0);
|
||||
const [signatureType, setSignatureType] = useState("draw");
|
||||
const [signatureType, setSignatureType] = useState("");
|
||||
const [isSignTypes, setIsSignTypes] = useState(true);
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
{ value: "Dancing Script" },
|
||||
@@ -53,24 +34,58 @@ function SignPad({
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
const currentUserName = jsonSender && jsonSender?.name;
|
||||
|
||||
useEffect(() => {
|
||||
handleTab();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.signatureTypes]);
|
||||
|
||||
function handleTab() {
|
||||
const signtypes = props?.signatureTypes || [];
|
||||
const index = signtypes?.findIndex((x) => x.enabled === true);
|
||||
if (index !== -1) {
|
||||
setIsSignTypes(true);
|
||||
const tab = props?.signatureTypes[index].name;
|
||||
if (tab === "draw") {
|
||||
setIsTab("draw");
|
||||
setSignatureType("draw");
|
||||
} else if (tab === "upload") {
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
} else if (tab === "typed") {
|
||||
setIsTab("type");
|
||||
} else if (tab === "default") {
|
||||
setIsTab("mysignature");
|
||||
} else {
|
||||
setIsTab(true);
|
||||
}
|
||||
} else {
|
||||
setIsSignTypes(false);
|
||||
}
|
||||
}
|
||||
function isTabEnabled(tabName) {
|
||||
const isEnabled = props?.signatureTypes.find(
|
||||
(x) => x.name === tabName
|
||||
)?.enabled;
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
props?.setCurrWidgetsDetails({});
|
||||
if (isTab === "draw") {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.clear();
|
||||
} else if (isStamp) {
|
||||
setImage("");
|
||||
} else if (props?.isStamp) {
|
||||
props?.setImage("");
|
||||
}
|
||||
setIsSignImg("");
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
props?.setImage("");
|
||||
}
|
||||
// setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = (data) => {
|
||||
setSignature(data);
|
||||
props?.setSignature(data);
|
||||
setIsSignImg(data);
|
||||
};
|
||||
//save button component
|
||||
@@ -88,58 +103,57 @@ function SignPad({
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrWidgetsDetails({});
|
||||
if (!image) {
|
||||
props?.setCurrWidgetsDetails({});
|
||||
if (!props?.image) {
|
||||
if (isTab === "mysignature") {
|
||||
setIsSignImg("");
|
||||
if (isInitial) {
|
||||
onSaveSign(signatureType, "initials");
|
||||
if (props?.isInitial) {
|
||||
props?.onSaveSign(signatureType, "initials");
|
||||
} else {
|
||||
onSaveSign(null, "default");
|
||||
props?.onSaveSign(null, "default");
|
||||
}
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(
|
||||
props?.onSaveSign(
|
||||
null,
|
||||
false,
|
||||
!isInitial && textWidth > 150 ? 150 : textWidth,
|
||||
!isInitial && textHeight > 35 ? 35 : textHeight
|
||||
!props?.isInitial && textWidth > 150 ? 150 : textWidth,
|
||||
!props?.isInitial && textHeight > 35 ? 35 : textHeight
|
||||
);
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
canvasRef.current.clear();
|
||||
onSaveSign(signatureType);
|
||||
props?.onSaveSign(signatureType);
|
||||
}
|
||||
}
|
||||
|
||||
setPenColor("blue");
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveImage(signatureType);
|
||||
props?.onSaveImage(signatureType);
|
||||
}
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
props?.setIsSignPad(false);
|
||||
props?.setIsInitial(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
props?.setImage();
|
||||
handleTab();
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
props?.setIsStamp(false);
|
||||
}}
|
||||
type="button"
|
||||
className={`${
|
||||
isSignImg || image || isDefaultSign || textWidth
|
||||
isSignImg || props?.image || isDefaultSign || textWidth
|
||||
? ""
|
||||
: "pointer-events-none"
|
||||
} op-btn op-btn-primary shadow-lg`}
|
||||
disabled={
|
||||
(isTab === "draw" && isSignImg) ||
|
||||
(isTab === "image" && image) ||
|
||||
(isTab === "image" && props?.image) ||
|
||||
(isTab === "mysignature" && isDefaultSign) ||
|
||||
(isTab === "type" && signValue)
|
||||
? false
|
||||
: image
|
||||
: props?.image
|
||||
? false
|
||||
: true
|
||||
}
|
||||
@@ -151,12 +165,12 @@ function SignPad({
|
||||
};
|
||||
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
|
||||
useEffect(() => {
|
||||
if (currWidgetsDetails && canvasRef.current) {
|
||||
const isWidgetType = currWidgetsDetails?.type;
|
||||
const signatureType = currWidgetsDetails?.signatureType;
|
||||
const url = currWidgetsDetails?.SignUrl;
|
||||
if (props?.currWidgetsDetails && canvasRef.current) {
|
||||
const isWidgetType = props?.currWidgetsDetails?.type;
|
||||
const signatureType = props?.currWidgetsDetails?.signatureType;
|
||||
const url = props?.currWidgetsDetails?.SignUrl;
|
||||
//checking widget type and draw type signature url
|
||||
if (isInitial) {
|
||||
if (props?.isInitial) {
|
||||
if (isWidgetType === "initials" && signatureType === "draw" && url) {
|
||||
canvasRef.current.fromDataURL(url);
|
||||
}
|
||||
@@ -171,12 +185,12 @@ function SignPad({
|
||||
|
||||
const trimmedName = currentUserName && currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
const userName = props?.isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName || "");
|
||||
setFontSelect("Fasthand");
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSignPad]);
|
||||
}, [props.isSignPad]);
|
||||
useEffect(() => {
|
||||
const loadFont = async () => {
|
||||
try {
|
||||
@@ -204,7 +218,7 @@ function SignPad({
|
||||
? signValue?.trim()
|
||||
: currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
const userName = props?.isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
}
|
||||
@@ -233,7 +247,7 @@ function SignPad({
|
||||
// Draw the text content on the canvas
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
const addExtraWidth = isInitial ? 10 : 50;
|
||||
const addExtraWidth = props?.isInitial ? 10 : 50;
|
||||
const width = span.offsetWidth + addExtraWidth;
|
||||
const height = span.offsetHeight;
|
||||
setTextWidth(width);
|
||||
@@ -255,7 +269,7 @@ function SignPad({
|
||||
document.body.removeChild(span);
|
||||
// Convert the canvas to image data
|
||||
const dataUrl = canvasElement.toDataURL("image/png");
|
||||
setSignature(dataUrl);
|
||||
props?.setSignature(dataUrl);
|
||||
};
|
||||
|
||||
const PenColorComponent = (props) => {
|
||||
@@ -297,319 +311,365 @@ function SignPad({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// `handleCancelBtn` function trigger when user click on cross button
|
||||
const handleCancelBtn = () => {
|
||||
setPenColor("blue");
|
||||
props?.setIsSignPad(false);
|
||||
props?.setIsInitial(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
props?.setImage();
|
||||
handleTab();
|
||||
setSignValue("");
|
||||
props?.setIsStamp(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isSignPad && (
|
||||
{props?.isSignPad && (
|
||||
<div className="op-modal op-modal-open">
|
||||
<div className="op-modal-box px-[13px] pt-2 pb-0">
|
||||
<div className="flex justify-between text-base-content items-center">
|
||||
<div className="text-[1.2rem]">
|
||||
<div className="flex flex-row justify-between mt-[3px]">
|
||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||
{isStamp ? (
|
||||
<span className="text-base-content font-bold text-lg">
|
||||
{widgetType === "image" ||
|
||||
currWidgetsDetails?.type === "image"
|
||||
? t("upload-image")
|
||||
: t("upload-stamp-image")}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
setImage();
|
||||
if (isSignImg) {
|
||||
setSignature(isSignImg);
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
isTab === "draw"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("draw")}
|
||||
{isSignTypes ? (
|
||||
<>
|
||||
<div className="flex justify-between text-base-content items-center">
|
||||
<div className="text-[1.2rem]">
|
||||
<div className="flex flex-row justify-between mt-[3px]">
|
||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||
{props?.isStamp ? (
|
||||
<span className="text-base-content font-bold text-lg">
|
||||
{props?.widgetType === "image" ||
|
||||
props?.currWidgetsDetails?.type === "image"
|
||||
? t("upload-image")
|
||||
: t("upload-stamp-image")}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
setSignatureType("");
|
||||
}}
|
||||
className={`${
|
||||
isTab === "uploadImage"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("upload-image")}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "type"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("type")}
|
||||
</span>
|
||||
</div>
|
||||
{!isInitial && defaultSign ? (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-signature")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
isInitial &&
|
||||
myInitial && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-initials")}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
<>
|
||||
{isTabEnabled("draw") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
props?.setImage();
|
||||
if (isSignImg) {
|
||||
props?.setSignature(isSignImg);
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
isTab === "draw"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("draw")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isTabEnabled("upload") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
setSignatureType("");
|
||||
}}
|
||||
className={`${
|
||||
isTab === "uploadImage"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("upload-image")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isTabEnabled("typed") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "type"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("type")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!props?.isInitial &&
|
||||
props?.defaultSign &&
|
||||
isTabEnabled("default") ? (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-signature")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
props?.isInitial &&
|
||||
props?.myInitial &&
|
||||
isTabEnabled("default") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-initials")}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={() => {
|
||||
setPenColor("blue");
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignatureType("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-[20px] h-full">
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
className="w-full h-full object-contain bg-white"
|
||||
draggable="false"
|
||||
src={isInitial ? myInitial : defaultSign}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : isImageSelect || isStamp ? (
|
||||
!image ? (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
onClick={() => imageRef.current.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="text-[10px]">{t("upload")}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
marginBottom: 6,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
className={
|
||||
isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={imageRef}
|
||||
src={image.src}
|
||||
draggable="false"
|
||||
className=" object-contain h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="mr-[5px] text-[12px]">
|
||||
{isInitial ? t("initial-teb") : t("signature-tab")}:
|
||||
</span>
|
||||
<input
|
||||
maxLength={isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect, color: penColor }}
|
||||
type="text"
|
||||
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value && "rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px",
|
||||
color: penColor
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent convertToImg={convertToImg} />
|
||||
<SaveBtn />
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={handleCancelBtn}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current?.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
<div className="p-[20px] h-full">
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
className="w-full h-full object-contain bg-white"
|
||||
draggable="false"
|
||||
src={
|
||||
props?.isInitial
|
||||
? props?.myInitial
|
||||
: props?.defaultSign
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : props?.isImageSelect || props?.isStamp ? (
|
||||
!props?.image ? (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
onClick={() => props?.imageRef.current.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={props?.onImageChange}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={props?.imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="text-[10px]">{t("upload")}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
marginBottom: 6,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={props?.imageRef}
|
||||
src={props?.image.src}
|
||||
draggable="false"
|
||||
className=" object-contain h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="mr-[5px] text-[12px]">
|
||||
{props?.isInitial
|
||||
? t("initial-teb")
|
||||
: t("signature-tab")}
|
||||
:
|
||||
</span>
|
||||
<input
|
||||
maxLength={props?.isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect, color: penColor }}
|
||||
type="text"
|
||||
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value &&
|
||||
"rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px",
|
||||
color: penColor
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent convertToImg={convertToImg} />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(
|
||||
canvasRef.current?.toDataURL()
|
||||
)
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="">
|
||||
<div className="relative flex flex-row items-center justify-between">
|
||||
<div className="text-base-content font-bold text-lg">
|
||||
Signature
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent />
|
||||
<SaveBtn />
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={handleCancelBtn}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-3 mb-6 mt-3">
|
||||
<p>{t("at-least-one-signature-type")}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -21,9 +21,11 @@ function TextFontSetting(props) {
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
"12"
|
||||
12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
onChange={(e) => {
|
||||
props.setFontSize(parseInt(e.target.value));
|
||||
}}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -4,6 +4,7 @@ import RecipientList from "./RecipientList";
|
||||
import { useDrag } from "react-dnd";
|
||||
import WidgetList from "./WidgetList";
|
||||
import {
|
||||
color,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
@@ -43,163 +44,69 @@ function WidgetComponent({
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 5,
|
||||
text: "dropdown"
|
||||
},
|
||||
|
||||
item: { type: "BOX", id: 5, text: "dropdown" },
|
||||
collect: (monitor) => ({
|
||||
isDragDropdown: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, checkbox] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 6,
|
||||
text: "checkbox"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCheck: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 6, text: "checkbox" },
|
||||
collect: (monitor) => ({ isDragCheck: !!monitor.isDragging() })
|
||||
});
|
||||
const [, textInput] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 7,
|
||||
text: textInputWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragTextInput: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 7, text: textInputWidget },
|
||||
collect: (monitor) => ({ isDragTextInput: !!monitor.isDragging() })
|
||||
});
|
||||
const [, initials] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 8,
|
||||
text: "initials"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragInitial: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 8, text: "initials" },
|
||||
collect: (monitor) => ({ isDragInitial: !!monitor.isDragging() })
|
||||
});
|
||||
const [, name] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 9,
|
||||
text: "name"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragName: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 9, text: "name" },
|
||||
collect: (monitor) => ({ isDragName: !!monitor.isDragging() })
|
||||
});
|
||||
const [, company] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 10,
|
||||
text: "company"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCompany: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 10, text: "company" },
|
||||
collect: (monitor) => ({ isDragCompany: !!monitor.isDragging() })
|
||||
});
|
||||
const [, jobTitle] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 11,
|
||||
text: "job title"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragJobtitle: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 11, text: "job title" },
|
||||
collect: (monitor) => ({ isDragJobtitle: !!monitor.isDragging() })
|
||||
});
|
||||
const [, date] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 12,
|
||||
text: "date"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDate: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 12, text: "date" },
|
||||
collect: (monitor) => ({ isDragDate: !!monitor.isDragging() })
|
||||
});
|
||||
const [, image] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 13,
|
||||
text: "image"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragImage: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 13, text: "image" },
|
||||
collect: (monitor) => ({ isDragImage: !!monitor.isDragging() })
|
||||
});
|
||||
const [, email] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 14,
|
||||
text: "email"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragEmail: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 14, text: "email" },
|
||||
collect: (monitor) => ({ isDragEmail: !!monitor.isDragging() })
|
||||
});
|
||||
const [, radioButton] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 15,
|
||||
text: radioButtonWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragRadiotton: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 15, text: radioButtonWidget },
|
||||
collect: (monitor) => ({ isDragRadiotton: !!monitor.isDragging() })
|
||||
});
|
||||
const [, text] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 16,
|
||||
text: textWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragText: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 16, text: textWidget },
|
||||
collect: (monitor) => ({ isDragText: !!monitor.isDragging() })
|
||||
});
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [widget, setWidget] = useState([]);
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const handleModal = () => {
|
||||
setIsSignersModal(!isSignersModal);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
import { textInputWidget, textWidget } from "../../constant/Utils";
|
||||
import {
|
||||
signatureTypes,
|
||||
textInputWidget,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
@@ -11,6 +15,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const signTypes = props?.signatureType || signatureTypes;
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
@@ -21,6 +26,7 @@ const WidgetNameModal = (props) => {
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const statusArr = ["Required", "Optional"];
|
||||
const inputOpt = ["text", "email", "number"];
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.defaultdata) {
|
||||
@@ -40,12 +46,32 @@ const WidgetNameModal = (props) => {
|
||||
name: props.defaultdata?.options?.name || ""
|
||||
});
|
||||
}
|
||||
|
||||
if (signTypes.length > 0) {
|
||||
const defaultSignatureType = signTypes || [];
|
||||
setSignatureType(defaultSignatureType);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.defaultdata]);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
props.handleData(formdata);
|
||||
if (["signature", "initials"].includes(props.defaultdata?.type)) {
|
||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||
const isDefaultSignTypeOnly =
|
||||
enabledSignTypes?.length === 1 &&
|
||||
enabledSignTypes[0]?.name === "default";
|
||||
if (enabledSignTypes.length === 0) {
|
||||
alert(t("at-least-one-signature-type"));
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
alert(t("expect-default-one-more-signature-type"));
|
||||
} else {
|
||||
const data = { ...formdata, signatureType };
|
||||
props.handleData(data, props.defaultdata?.type);
|
||||
}
|
||||
} else {
|
||||
props.handleData(formdata);
|
||||
}
|
||||
setFormdata({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
@@ -53,6 +79,7 @@ const WidgetNameModal = (props) => {
|
||||
hint: "",
|
||||
textvalidate: ""
|
||||
});
|
||||
setSignatureType(signTypes);
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
@@ -102,6 +129,15 @@ const WidgetNameModal = (props) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Create a copy of the signatureType array
|
||||
const updatedSignatureType = [...signatureType];
|
||||
// Toggle the enabled value for the clicked item
|
||||
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// Update the state with the modified array
|
||||
setSignatureType(updatedSignatureType);
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
@@ -111,24 +147,30 @@ const WidgetNameModal = (props) => {
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
props.defaultdata?.type === textInputWidget ? "pt-0" : ""
|
||||
props.defaultdata?.type === textInputWidget
|
||||
? "pt-0"
|
||||
: ["signature", "initials"].includes(props.defaultdata?.type)
|
||||
? "pt-2"
|
||||
: ""
|
||||
} p-[20px] text-base-content`}
|
||||
>
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="name">
|
||||
{t("name")}
|
||||
<span className="text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="name">
|
||||
{t("name")}
|
||||
<span className="text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<>
|
||||
{isEnableSubscription && (
|
||||
@@ -226,30 +268,63 @@ const WidgetNameModal = (props) => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="flex flex-row gap-[5px] items-center">
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
||||
{t("allowed-signature-types")}
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => {
|
||||
return (
|
||||
<div key={i} className="flex flex-row gap-[5px] items-center">
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type.name}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="hint" className="text-[13px]">
|
||||
@@ -276,9 +351,9 @@ const WidgetNameModal = (props) => {
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize || props.defaultdata?.options?.fontSize || "12"
|
||||
props.fontSize || props.defaultdata?.options?.fontSize || 12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import AsyncSelect from "react-select/async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
|
||||
const SelectSigners = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -31,17 +31,19 @@ const SelectSigners = (props) => {
|
||||
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (inputValue.length > 1) {
|
||||
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||
}
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsigners`;
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const searchEmail = inputValue ? inputValue : "";
|
||||
const axiosRes = await axios.post(url, { searchEmail }, { headers });
|
||||
const contactRes = axiosRes?.data?.result || [];
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||
@@ -52,11 +54,9 @@ const SelectSigners = (props) => {
|
||||
!signerObj.find((item2) => item2.objectId === item1.objectId)
|
||||
);
|
||||
};
|
||||
|
||||
//get update signer's List if signersdata is present
|
||||
const updateSignersList =
|
||||
props?.signersData && compareArrays(res, props?.signersData);
|
||||
|
||||
const result = updateSignersList ? updateSignersList : res;
|
||||
setUserList(result);
|
||||
return await result.map((item) => ({
|
||||
@@ -68,7 +68,6 @@ const SelectSigners = (props) => {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full px-[20px] py-[10px] text-base-content">
|
||||
<div className="w-full mx-auto p-[8px]">
|
||||
@@ -85,6 +84,7 @@ const SelectSigners = (props) => {
|
||||
loadOptions={loadOptions}
|
||||
onChange={handleOptions}
|
||||
unstyled
|
||||
onFocus={() => loadOptions()}
|
||||
classNames={{
|
||||
control: () =>
|
||||
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
|
||||
|
||||
@@ -25,7 +25,7 @@ const SuggestionInput = (props) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
(async () => {
|
||||
const res = await findContact(inputValue);
|
||||
const res = await findContact(inputValue, props.jwttoken);
|
||||
if (res?.length > 0) {
|
||||
setSuggestions(res);
|
||||
setShowSuggestions(true);
|
||||
@@ -37,13 +37,14 @@ const SuggestionInput = (props) => {
|
||||
}, 1000);
|
||||
}
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputValue]);
|
||||
|
||||
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() === "") {
|
||||
|
||||
+461
-383
File diff suppressed because it is too large
Load Diff
@@ -27,21 +27,21 @@ export default function reportJson(id) {
|
||||
const templateSubAction = isEnableSubscription
|
||||
? [
|
||||
{
|
||||
btnId: "2434",
|
||||
btnId: "2435",
|
||||
btnLabel: "Embed",
|
||||
hoverLabel: "Embed",
|
||||
btnIcon: "fa-light fa-code",
|
||||
action: "Embed"
|
||||
},
|
||||
{
|
||||
btnId: "2434",
|
||||
btnId: "2436",
|
||||
btnLabel: "Copy TemplateId",
|
||||
hoverLabel: "Copy TemplateId",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
action: "CopyTemplateId"
|
||||
},
|
||||
{
|
||||
btnId: "2434",
|
||||
btnId: "2437",
|
||||
btnLabel: "Copy Public URL",
|
||||
hoverLabel: "Copy Public URL",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
|
||||
@@ -154,6 +154,14 @@ const sidebarList = [
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "webhook"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-sliders",
|
||||
title: "Preferences",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "preferences"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,17 +16,18 @@ const plans = [
|
||||
"Unlimited digital signatures",
|
||||
"Sign documents yourself",
|
||||
"Request signatures from others",
|
||||
"Unlimited templates",
|
||||
"14 field types",
|
||||
"Automatic e-signatures",
|
||||
"Completion certificates",
|
||||
"Send in order",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Document templates",
|
||||
"Import from Dropbox",
|
||||
"Contact book",
|
||||
"Document expiry support",
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
],
|
||||
@@ -34,17 +35,18 @@ const plans = [
|
||||
"Unlimited digital signatures",
|
||||
"Sign documents yourself",
|
||||
"Request signatures from others",
|
||||
"Unlimited templates",
|
||||
"14 field types",
|
||||
"Automatic e-signatures",
|
||||
"Completion certificates",
|
||||
"Send in order",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Document templates",
|
||||
"Import from Dropbox",
|
||||
"Contact book",
|
||||
"Document expiry support",
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
]
|
||||
@@ -61,10 +63,10 @@ const plans = [
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btn: { text: "Subscribe", color: "op-btn-primary" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
@@ -77,10 +79,12 @@ const plans = [
|
||||
"API Access",
|
||||
"upto 100 API signatures",
|
||||
"Custom email templates",
|
||||
"Connect your own Gmail or SMTP account for sending emails",
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 100 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
"Enforce email-based verification to confirm signer identity",
|
||||
"Embedded signing"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Everything in OpenSign™ free",
|
||||
@@ -92,10 +96,12 @@ const plans = [
|
||||
"API Access",
|
||||
"upto 240 API signatures",
|
||||
"Custom email templates",
|
||||
"Connect your own Gmail or SMTP account for sending emails",
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 240 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
"Enforce email-based verification to confirm signer identity",
|
||||
"Embedded signing"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -108,10 +114,10 @@ const plans = [
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btn: { text: "Subscribe", color: "op-btn-accent" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
@@ -120,6 +126,7 @@ const plans = [
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"BYOC - Store your documents in your own cloud storage",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 100 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
@@ -131,6 +138,7 @@ const plans = [
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"BYOC - Store your documents in your own cloud storage",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 500 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
@@ -231,10 +239,10 @@ export const paidUrl = (plan) => {
|
||||
}
|
||||
const url = {
|
||||
monthly: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
|
||||
yearly: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly"
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -265,7 +255,9 @@ const HomeLayout = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-[501]">
|
||||
{!isLoader && <Header showSidebar={showSidebar} />}
|
||||
{!isLoader && (
|
||||
<Header showSidebar={showSidebar} setIsMenu={setIsOpen} />
|
||||
)}
|
||||
</div>
|
||||
{isUserValid ? (
|
||||
<>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddAdmin = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
@@ -45,7 +46,9 @@ const AddAdmin = () => {
|
||||
setState((prev) => ({ ...prev, loading: true }));
|
||||
try {
|
||||
const app = await getAppLogo();
|
||||
if (app?.user === "exist") {
|
||||
if (app?.error === "invalid_json") {
|
||||
setErrMsg(t("server-down"));
|
||||
} else if (app?.user === "exist") {
|
||||
setErrMsg(t("admin-exists"));
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -82,77 +85,81 @@ const AddAdmin = () => {
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
const user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
const userRes = await user.save();
|
||||
if (userRes) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: "contracts_Admin"
|
||||
}
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run("addadmin", params);
|
||||
if (usersignup) {
|
||||
if (isSubscribeNews) {
|
||||
subscribeNewsletter();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
const user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
const userRes = await user.save();
|
||||
if (userRes) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: "contracts_Admin"
|
||||
}
|
||||
handleNavigation(userRes.getSessionToken());
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
if (error.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("already-exists-this-username"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
};
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res) {
|
||||
if (res.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
const usersignup = await Parse.Cloud.run("addadmin", params);
|
||||
if (usersignup) {
|
||||
if (isSubscribeNews) {
|
||||
subscribeNewsletter();
|
||||
}
|
||||
);
|
||||
handleNavigation(userRes.getSessionToken());
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
if (error.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("already-exists-this-username"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res) {
|
||||
if (res.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(error.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(error.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { getAppLogo } from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -36,18 +36,22 @@ function ForgotPassword() {
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
const username = state.email;
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(username);
|
||||
setSentStatus("success");
|
||||
} catch (err) {
|
||||
console.log("err ", err.code);
|
||||
setSentStatus("failed");
|
||||
} finally {
|
||||
setTimeout(() => setSentStatus(""), 1000);
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
const username = state.email;
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(username);
|
||||
setSentStatus("success");
|
||||
} catch (err) {
|
||||
console.log("err ", err.code);
|
||||
setSentStatus("failed");
|
||||
} finally {
|
||||
setTimeout(() => setSentStatus(""), 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+679
-267
File diff suppressed because it is too large
Load Diff
+147
-140
@@ -9,7 +9,7 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
@@ -89,167 +89,174 @@ function Login() {
|
||||
const handleSubmit = async (event) => {
|
||||
localStorage.removeItem("accesstoken");
|
||||
event.preventDefault();
|
||||
const { email, password } = state;
|
||||
if (email && password) {
|
||||
try {
|
||||
setState({ ...state, loading: true });
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
// Pass the username and password to logIn function
|
||||
const user = await Parse.User.logIn(email, password);
|
||||
if (user) {
|
||||
let _user = user.toJSON();
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", email);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||
} else {
|
||||
localStorage.setItem("profileImg", "");
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
const userSettings = appInfo.settings;
|
||||
await Parse.Cloud.run("getUserDetails")
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${menu.pageType}/${menu.pageId}`;
|
||||
let _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const checkLanguage = extUser?.get("Language");
|
||||
if (checkLanguage) {
|
||||
checkLanguage && i18n.changeLanguage(checkLanguage);
|
||||
}
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
const { email, password } = state;
|
||||
if (email && password) {
|
||||
try {
|
||||
setState({ ...state, loading: true });
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
// Pass the username and password to logIn function
|
||||
const user = await Parse.User.logIn(email, password);
|
||||
if (user) {
|
||||
let _user = user.toJSON();
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", email);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||
} else {
|
||||
localStorage.setItem("profileImg", "");
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
const userSettings = appInfo.settings;
|
||||
await Parse.Cloud.run("getUserDetails")
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${menu.pageType}/${menu.pageId}`;
|
||||
let _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const checkLanguage = extUser?.get("Language");
|
||||
if (checkLanguage) {
|
||||
checkLanguage && i18n.changeLanguage(checkLanguage);
|
||||
}
|
||||
|
||||
const results = [extUser];
|
||||
const extUser_str = JSON.stringify(results);
|
||||
const results = [extUser];
|
||||
const extUser_str = JSON.stringify(results);
|
||||
|
||||
localStorage.setItem("Extand_Class", extUser_str);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo.Email);
|
||||
localStorage.setItem("username", extInfo.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0]?.get("Phone") || "",
|
||||
company: results[0].get("Company")
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const res = await fetchSubscription();
|
||||
const plan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (plan === "freeplan") {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
|
||||
localStorage.setItem("Extand_Class", extUser_str);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo.Email);
|
||||
localStorage.setItem("username", extInfo.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0]?.get("Phone") || "",
|
||||
company: results[0].get("Company")
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const res = await fetchSubscription();
|
||||
const plan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (plan === "freeplan") {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
handlePaidRoute(plan);
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
setIsModal(true);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"You don't have access, please contact the admin."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
} else {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"You don't have access, please contact the admin."
|
||||
});
|
||||
logOutUser();
|
||||
if (isEnableSubscription) {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User not found."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isEnableSubscription) {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User not found."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// const payload = { sessionToken: user.getSessionToken() };
|
||||
// handleSubmitbtn(payload);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `Something went wrong.`
|
||||
})
|
||||
.catch((error) => {
|
||||
// const payload = { sessionToken: user.getSessionToken() };
|
||||
// handleSubmitbtn(payload);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `Something went wrong.`
|
||||
});
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.error("Error while fetching Follow", error);
|
||||
});
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.error("Error while fetching Follow", error);
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
console.log(error);
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.log(error);
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "Invalid username or password!"
|
||||
});
|
||||
console.error("Error while logging in user", error);
|
||||
} finally {
|
||||
setTimeout(
|
||||
() => setState((prev) => ({ ...prev, alertMsg: "" })),
|
||||
2000
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "Invalid username or password!"
|
||||
});
|
||||
console.error("Error while logging in user", error);
|
||||
} finally {
|
||||
setTimeout(() => setState((prev) => ({ ...prev, alertMsg: "" })), 2000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -136,10 +136,10 @@ const ManageSign = () => {
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
let file;
|
||||
if (signature) {
|
||||
file = base64StringtoFile(signature, `${replaceSpace}_sign.png`);
|
||||
file = base64StringtoFile(signature, `${replaceSpace}_sign`);
|
||||
} else {
|
||||
if (image && !isUrl) {
|
||||
file = base64StringtoFile(image, `${replaceSpace}__sign.png`);
|
||||
file = base64StringtoFile(image, `${replaceSpace}__sign`);
|
||||
}
|
||||
}
|
||||
let imgUrl;
|
||||
@@ -153,12 +153,12 @@ const ManageSign = () => {
|
||||
|
||||
let initialFile;
|
||||
if (Initials) {
|
||||
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign.png`);
|
||||
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign`);
|
||||
} else {
|
||||
if (imgInitials && !isInitialsUrl) {
|
||||
initialFile = base64StringtoFile(
|
||||
imgInitials,
|
||||
`${replaceSpace}__sign.png`
|
||||
`${replaceSpace}__sign`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -179,14 +179,18 @@ const ManageSign = () => {
|
||||
};
|
||||
function base64StringtoFile(base64String, filename) {
|
||||
let arr = base64String.split(","),
|
||||
// type of uploaded image
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
// decode base64
|
||||
bstr = atob(arr[1]),
|
||||
n = bstr.length,
|
||||
u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], filename, { type: mime });
|
||||
const ext = mime.split("/").pop();
|
||||
const name = `${filename}.${ext}`;
|
||||
return new File([u8arr], name, { type: mime });
|
||||
}
|
||||
|
||||
const uploadFile = async (file) => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { isEnableSubscription, isStaging, themeColor } from "../constant/const";
|
||||
import {
|
||||
emailRegex,
|
||||
isEnableSubscription,
|
||||
isStaging,
|
||||
themeColor
|
||||
} from "../constant/const";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
@@ -33,7 +38,10 @@ import {
|
||||
getDefaultSignature,
|
||||
onClickZoomIn,
|
||||
onClickZoomOut,
|
||||
fetchUrl
|
||||
fetchUrl,
|
||||
signatureTypes,
|
||||
handleSignatureType,
|
||||
getTenantDetails
|
||||
} from "../constant/Utils";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
@@ -132,12 +140,11 @@ function PdfRequestFiles(props) {
|
||||
const [publicRes, setPublicRes] = useState({});
|
||||
const [documentId, setDocumentId] = useState("");
|
||||
const [isPublicContact, setIsPublicContact] = useState(false);
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const [plancode, setPlanCode] = useState("");
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const divRef = useRef(null);
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const isMobile = window.innerWidth < 767;
|
||||
|
||||
let isGuestSignFlow = false;
|
||||
@@ -304,17 +311,13 @@ function PdfRequestFiles(props) {
|
||||
: [];
|
||||
if (documentData && documentData[0]?.error) {
|
||||
props?.setTemplateStatus &&
|
||||
props?.setTemplateStatus({
|
||||
status: "Invalid"
|
||||
});
|
||||
props?.setTemplateStatus({ status: "Invalid" });
|
||||
throw new Error("error: Invalid TemplateId");
|
||||
} else if (documentData && documentData.length > 0) {
|
||||
if (documentData[0]?.IsPublic) {
|
||||
//handle condition when someone use plan js then setTemplateStatus is not supporting
|
||||
props?.setTemplateStatus &&
|
||||
props?.setTemplateStatus({
|
||||
status: "Success"
|
||||
});
|
||||
props?.setTemplateStatus({ status: "Success" });
|
||||
const url =
|
||||
documentData[0] &&
|
||||
(documentData[0]?.SignedUrl || documentData[0]?.URL);
|
||||
@@ -323,8 +326,6 @@ function PdfRequestFiles(props) {
|
||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||
if (arrayBuffer === "Error") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
} else {
|
||||
setPdfArrayBuffer(arrayBuffer);
|
||||
}
|
||||
} else {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
@@ -354,24 +355,18 @@ function PdfRequestFiles(props) {
|
||||
}
|
||||
setUnSignedSigners(placeholdersOrSigners);
|
||||
setPdfDetails(documentData);
|
||||
setIsLoading({
|
||||
isLoad: false
|
||||
});
|
||||
setIsLoading({ isLoad: false });
|
||||
} else {
|
||||
props?.setTemplateStatus &&
|
||||
props?.setTemplateStatus({
|
||||
status: "Private"
|
||||
});
|
||||
setIsLoading(false);
|
||||
props?.setTemplateStatus({ status: "Private" });
|
||||
setIsLoading({ isLoad: false });
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
console.error("error: TemplateId is not public");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
props?.setTemplateStatus &&
|
||||
props?.setTemplateStatus({
|
||||
status: "Invalid"
|
||||
});
|
||||
props?.setTemplateStatus({ status: "Invalid" });
|
||||
setIsLoading(false);
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
console.error("error: Invalid TemplateId");
|
||||
@@ -388,6 +383,33 @@ function PdfRequestFiles(props) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
const fetchTenantDetails = async (contactId) => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
try {
|
||||
const tenantDetails = await getTenantDetails(
|
||||
user?.objectId, // userId
|
||||
"", // jwttoken
|
||||
contactId // contactId
|
||||
);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantDetails) {
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
|
||||
//function for get document details for perticular signer with signer'object id
|
||||
const getDocumentDetails = async (docId, isNextUser) => {
|
||||
try {
|
||||
@@ -395,11 +417,27 @@ function PdfRequestFiles(props) {
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
const contactId = jsonSender?.objectId
|
||||
? ""
|
||||
: contactBookId || signerObjectId || "";
|
||||
const tenantSignTypes = await fetchTenantDetails(contactId);
|
||||
// `currUserId` will be contactId or extUserId
|
||||
let currUserId;
|
||||
//getting document details
|
||||
const documentData = await contractDocument(docId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
const userSignatureType =
|
||||
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
|
||||
const docSignTypes =
|
||||
documentData?.[0]?.SignatureType || userSignatureType;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
const updatedPdfDetails = [...documentData];
|
||||
updatedPdfDetails[0].SignatureType = updatedSignatureType;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
const url =
|
||||
documentData[0] &&
|
||||
(documentData[0]?.SignedUrl || documentData[0]?.URL);
|
||||
@@ -408,8 +446,6 @@ function PdfRequestFiles(props) {
|
||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||
if (arrayBuffer === "Error") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
} else {
|
||||
setPdfArrayBuffer(arrayBuffer);
|
||||
}
|
||||
} else {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
@@ -423,13 +459,15 @@ function PdfRequestFiles(props) {
|
||||
const expireUpdateDate = new Date(expireDate).getTime();
|
||||
const currDate = new Date().getTime();
|
||||
const getSigners = documentData[0].Signers;
|
||||
const isTourEnabled =
|
||||
documentData[0]?.IsTourEnabled === true ? true : false;
|
||||
const getCurrentSigner = getSigners?.find(
|
||||
(data) => data.UserId.objectId === jsonSender?.objectId
|
||||
);
|
||||
|
||||
currUserId = getCurrentSigner?.objectId
|
||||
? getCurrentSigner.objectId
|
||||
: contactBookId || signerObjectId || "";
|
||||
: contactBookId || signerObjectId || ""; //signerObjectId is contactBookId refer from public template flow
|
||||
if (isEnableSubscription) {
|
||||
await checkIsSubscribed(
|
||||
documentData[0]?.ExtUserPtr?.objectId,
|
||||
@@ -598,7 +636,8 @@ function PdfRequestFiles(props) {
|
||||
checkAlreadySign ||
|
||||
!currUserId ||
|
||||
declined ||
|
||||
currDate > expireUpdateDate
|
||||
currDate > expireUpdateDate ||
|
||||
!isTourEnabled
|
||||
) {
|
||||
setRequestSignTour(true);
|
||||
} else {
|
||||
@@ -626,6 +665,8 @@ function PdfRequestFiles(props) {
|
||||
tourData?.some((data) => data?.requestSign) || false;
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest);
|
||||
} else {
|
||||
setRequestSignTour(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting tourstatus", err);
|
||||
@@ -647,6 +688,8 @@ function PdfRequestFiles(props) {
|
||||
);
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest[0]?.requestSign || false);
|
||||
} else {
|
||||
setRequestSignTour(false);
|
||||
}
|
||||
} else if (res?.length === 0) {
|
||||
const res = await contactBook(currUserId);
|
||||
@@ -663,6 +706,8 @@ function PdfRequestFiles(props) {
|
||||
);
|
||||
setTourStatus(tourData);
|
||||
setRequestSignTour(checkTourRequest[0]?.requestSign || false);
|
||||
} else {
|
||||
setRequestSignTour(false);
|
||||
}
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("user-not-exist"));
|
||||
@@ -882,9 +927,29 @@ function PdfRequestFiles(props) {
|
||||
setIsUiLoading(true);
|
||||
// `widgets` is Used to return widgets details with page number of current user
|
||||
const widgets = checkUser?.[0]?.placeHolder;
|
||||
|
||||
let pdfArrBuffer;
|
||||
//`contractDocument` function used to get updated SignedUrl
|
||||
// to resolve issue of widgets get remove automatically when more than 1 signers try to sign doc at a time
|
||||
const documentData = await contractDocument(documentId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
const url = documentData[0]?.SignedUrl || documentData[0]?.URL;
|
||||
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||
if (arrayBuffer === "Error") {
|
||||
setHandleError("Error: invalid document!");
|
||||
} else {
|
||||
pdfArrBuffer = arrayBuffer;
|
||||
}
|
||||
} else if (
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
} else {
|
||||
setHandleError("Document not Found!");
|
||||
}
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
const existingPdfBytes = pdfArrBuffer;
|
||||
try {
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
const isSignYourSelfFlow = false;
|
||||
@@ -901,169 +966,175 @@ function PdfRequestFiles(props) {
|
||||
widgets,
|
||||
pdfDoc,
|
||||
isSignYourSelfFlow,
|
||||
scale,
|
||||
pdfOriginalWH,
|
||||
containerWH
|
||||
scale
|
||||
);
|
||||
// console.log("pdfte", pdfBytes);
|
||||
//get ExistUserPtr object id of user class to get tenantDetails
|
||||
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
|
||||
let activeMailAdapter =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.active_mail_adapter;
|
||||
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
const resSign = await signPdfFun(
|
||||
pdfBytes,
|
||||
documentId,
|
||||
signerObjectId,
|
||||
objectId,
|
||||
isSubscribed,
|
||||
activeMailAdapter,
|
||||
widgets
|
||||
);
|
||||
if (resSign && resSign.status === "success") {
|
||||
setPdfUrl(resSign.data);
|
||||
setIsSigned(true);
|
||||
setSignedSigners([]);
|
||||
setUnSignedSigners([]);
|
||||
getDocumentDetails(documentId, true);
|
||||
const index = pdfDetails?.[0]?.Signers.findIndex(
|
||||
(x) => x.objectId === signerObjectId
|
||||
if (!pdfBytes?.error) {
|
||||
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
|
||||
let activeMailAdapter =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.active_mail_adapter;
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
const resSign = await signPdfFun(
|
||||
pdfBytes,
|
||||
documentId,
|
||||
signerObjectId,
|
||||
objectId,
|
||||
isSubscribed,
|
||||
activeMailAdapter,
|
||||
widgets
|
||||
);
|
||||
const newIndex = index + 1;
|
||||
const usermail = {
|
||||
Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || ""
|
||||
};
|
||||
const user = usermail?.Email
|
||||
? usermail
|
||||
: pdfDetails?.[0]?.Signers[newIndex];
|
||||
if (sendmail !== "false" && sendInOrder) {
|
||||
const requestBody = pdfDetails?.[0]?.RequestBody;
|
||||
const requestSubject = pdfDetails?.[0]?.RequestSubject;
|
||||
if (user) {
|
||||
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
|
||||
const newDate = new Date(expireDate);
|
||||
const localExpireDate = newDate.toLocaleDateString(
|
||||
"en-US",
|
||||
{
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric"
|
||||
}
|
||||
);
|
||||
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
|
||||
if (resSign && resSign.status === "success") {
|
||||
setPdfUrl(resSign.data);
|
||||
setIsSigned(true);
|
||||
setSignedSigners([]);
|
||||
setUnSignedSigners([]);
|
||||
getDocumentDetails(documentId, true);
|
||||
const index = pdfDetails?.[0]?.Signers.findIndex(
|
||||
(x) => x.objectId === signerObjectId
|
||||
);
|
||||
const newIndex = index + 1;
|
||||
const usermail = {
|
||||
Email: pdfDetails?.[0]?.Placeholders[newIndex]?.email || ""
|
||||
};
|
||||
const user = usermail?.Email
|
||||
? usermail
|
||||
: pdfDetails?.[0]?.Signers[newIndex];
|
||||
if (sendmail !== "false" && sendInOrder) {
|
||||
const requestBody = pdfDetails?.[0]?.RequestBody;
|
||||
const requestSubject = pdfDetails?.[0]?.RequestSubject;
|
||||
if (user) {
|
||||
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
|
||||
const newDate = new Date(expireDate);
|
||||
const localExpireDate = newDate.toLocaleDateString(
|
||||
"en-US",
|
||||
{ day: "numeric", month: "long", year: "numeric" }
|
||||
);
|
||||
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
|
||||
|
||||
try {
|
||||
const imgPng =
|
||||
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
|
||||
let url = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}functions/sendmailv3`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id":
|
||||
localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const objectId = user?.objectId;
|
||||
const hostUrl = window.location.origin;
|
||||
//encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function
|
||||
let encodeBase64;
|
||||
if (objectId) {
|
||||
encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}`
|
||||
);
|
||||
} else {
|
||||
encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${user.Email}`
|
||||
);
|
||||
}
|
||||
const hostPublicUrl = isStaging
|
||||
? "https://staging-app.opensignlabs.com"
|
||||
: "https://app.opensignlabs.com";
|
||||
let signPdf = props?.templateId
|
||||
? `${hostPublicUrl}/login/${encodeBase64}`
|
||||
: `${hostUrl}/login/${encodeBase64}`;
|
||||
const openSignUrl =
|
||||
"https://www.opensignlabs.com/contact-us";
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const themeBGcolor = themeColor;
|
||||
let replaceVar;
|
||||
if (requestBody && requestSubject && isSubscribed) {
|
||||
const replacedRequestBody = requestBody.replace(
|
||||
/"/g,
|
||||
"'"
|
||||
);
|
||||
const htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
|
||||
const variables = {
|
||||
document_title: pdfDetails?.[0].Name,
|
||||
sender_name: senderName,
|
||||
sender_mail: senderEmail,
|
||||
sender_phone: senderPhone,
|
||||
receiver_name: user.Name,
|
||||
receiver_email: user.Email,
|
||||
receiver_phone: user.Phone,
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
try {
|
||||
const imgPng =
|
||||
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
|
||||
let url = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}functions/sendmailv3`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id":
|
||||
localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
replaceVar = replaceMailVaribles(
|
||||
requestSubject,
|
||||
htmlReqBody,
|
||||
variables
|
||||
);
|
||||
}
|
||||
const objectId = user?.objectId;
|
||||
const hostUrl = window.location.origin;
|
||||
//encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function
|
||||
let encodeBase64;
|
||||
if (objectId) {
|
||||
encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}`
|
||||
);
|
||||
} else {
|
||||
encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${user.Email}`
|
||||
);
|
||||
}
|
||||
const hostPublicUrl = isStaging
|
||||
? "https://staging-app.opensignlabs.com"
|
||||
: "https://app.opensignlabs.com";
|
||||
let signPdf = props?.templateId
|
||||
? `${hostPublicUrl}/login/${encodeBase64}`
|
||||
: `${hostUrl}/login/${encodeBase64}`;
|
||||
const openSignUrl =
|
||||
"https://www.opensignlabs.com/contact-us";
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const themeBGcolor = themeColor;
|
||||
let replaceVar;
|
||||
if (requestBody && requestSubject && isSubscribed) {
|
||||
const replacedRequestBody = requestBody.replace(
|
||||
/"/g,
|
||||
"'"
|
||||
);
|
||||
const htmlReqBody =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
|
||||
replacedRequestBody +
|
||||
"</body> </html>";
|
||||
|
||||
let params = {
|
||||
mailProvider: activeMailAdapter,
|
||||
extUserId: extUserId,
|
||||
recipient: user.Email,
|
||||
subject: requestSubject
|
||||
? replaceVar?.subject
|
||||
: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`,
|
||||
from: senderEmail,
|
||||
plan: plancode,
|
||||
html: requestBody
|
||||
? replaceVar?.body
|
||||
: "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
|
||||
imgPng +
|
||||
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
senderEmail +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
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 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 +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
};
|
||||
await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
const variables = {
|
||||
document_title: pdfDetails?.[0].Name,
|
||||
sender_name: senderName,
|
||||
sender_mail: senderEmail,
|
||||
sender_phone: senderPhone,
|
||||
receiver_name: user.Name,
|
||||
receiver_email: user.Email,
|
||||
receiver_phone: user.Phone,
|
||||
expiry_date: localExpireDate,
|
||||
company_name: orgName,
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
|
||||
};
|
||||
replaceVar = replaceMailVaribles(
|
||||
requestSubject,
|
||||
htmlReqBody,
|
||||
variables
|
||||
);
|
||||
}
|
||||
|
||||
let params = {
|
||||
mailProvider: activeMailAdapter,
|
||||
extUserId: extUserId,
|
||||
recipient: user.Email,
|
||||
subject: requestSubject
|
||||
? replaceVar?.subject
|
||||
: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`,
|
||||
from: senderEmail,
|
||||
plan: plancode,
|
||||
html: requestBody
|
||||
? replaceVar?.body
|
||||
: "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
|
||||
imgPng +
|
||||
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
|
||||
themeBGcolor +
|
||||
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||
pdfDetails?.[0].ExtUserPtr.Name +
|
||||
" has requested you to review and sign <strong> " +
|
||||
pdfDetails?.[0].Name +
|
||||
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||
senderEmail +
|
||||
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||
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 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 +
|
||||
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a>.</p> </div></div></body> </html>"
|
||||
};
|
||||
await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: resSign.message
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
setIsAlert({ isShow: true, alertMessage: resSign.message });
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: t("pdf-uncompatible")
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setIsUiLoading(false);
|
||||
@@ -1346,9 +1417,7 @@ function PdfRequestFiles(props) {
|
||||
try {
|
||||
await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/updatecontacttour`,
|
||||
{
|
||||
contactId: signerObjectId
|
||||
},
|
||||
{ contactId: signerObjectId },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -1489,65 +1558,69 @@ function PdfRequestFiles(props) {
|
||||
//`handlePublicUser` function to use create user from public role and create document from public template
|
||||
const handlePublicUser = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
...contact,
|
||||
templateid: pdfDetails[0]?.objectId,
|
||||
role: pdfDetails[0]?.PublicRole[0]
|
||||
};
|
||||
const userRes = await axios.post(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}/functions/publicuserlinkcontacttodoc`,
|
||||
params,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||
if (!emailRegex.test(contact.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params = {
|
||||
...contact,
|
||||
templateid: pdfDetails[0]?.objectId,
|
||||
role: pdfDetails[0]?.PublicRole[0]
|
||||
};
|
||||
const userRes = await axios.post(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}/functions/publicuserlinkcontacttodoc`,
|
||||
params,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
if (userRes?.data?.result) {
|
||||
setPublicRes(userRes.data.result);
|
||||
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
|
||||
if (isEnableOTP) {
|
||||
await SendOtp();
|
||||
if (userRes?.data?.result) {
|
||||
setPublicRes(userRes.data.result);
|
||||
const isEnableOTP = pdfDetails?.[0]?.IsEnableOTP || false;
|
||||
if (isEnableOTP) {
|
||||
await SendOtp();
|
||||
} else {
|
||||
setIsPublicContact(false);
|
||||
setIsPublicTemplate(false);
|
||||
setDocumentId(userRes.data?.result?.docId);
|
||||
const contactId = userRes.data.result?.contactId;
|
||||
setSignerObjectId(contactId);
|
||||
}
|
||||
} else {
|
||||
setIsPublicContact(false);
|
||||
setIsPublicTemplate(false);
|
||||
setDocumentId(userRes.data?.result?.docId);
|
||||
const contactId = userRes.data.result?.contactId;
|
||||
setSignerObjectId(contactId);
|
||||
console.log("error in public-sign to create user details");
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("e", e);
|
||||
if (
|
||||
e?.response?.data?.error === "Insufficient Credit" ||
|
||||
e?.response?.data?.error === "Plan expired"
|
||||
) {
|
||||
handleCloseOtp();
|
||||
setIsAlert({
|
||||
title: t("insufficient-credits"),
|
||||
isShow: true,
|
||||
alertMessage: t("insufficient-credits-mssg")
|
||||
});
|
||||
} else {
|
||||
handleCloseOtp();
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("error in public-sign to create user details");
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("e", e);
|
||||
if (
|
||||
e?.response?.data?.error === "Insufficient Credit" ||
|
||||
e?.response?.data?.error === "Plan expired"
|
||||
) {
|
||||
handleCloseOtp();
|
||||
setIsAlert({
|
||||
title: t("insufficient-credits"),
|
||||
isShow: true,
|
||||
alertMessage: t("insufficient-credits-mssg")
|
||||
});
|
||||
} else {
|
||||
handleCloseOtp();
|
||||
setIsAlert({
|
||||
title: "Error",
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1929,7 +2002,7 @@ function PdfRequestFiles(props) {
|
||||
})
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
className="op-btn op-btn-secondary ml-1"
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
@@ -2072,27 +2145,30 @@ function PdfRequestFiles(props) {
|
||||
</div>
|
||||
</ModalUi>
|
||||
{/* this component is used for signature pad modal */}
|
||||
<SignPad
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setImage={setImage}
|
||||
isImageSelect={isImageSelect}
|
||||
imageRef={imageRef}
|
||||
onImageChange={onImageChange}
|
||||
setSignature={setSignature}
|
||||
image={image}
|
||||
onSaveImage={saveImage}
|
||||
onSaveSign={saveSign}
|
||||
defaultSign={defaultSignImg}
|
||||
myInitial={myInitial}
|
||||
isInitial={isInitial}
|
||||
setIsInitial={setIsInitial}
|
||||
setIsStamp={setIsStamp}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
{documentId && (
|
||||
<SignPad
|
||||
signatureTypes={signatureType}
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setImage={setImage}
|
||||
isImageSelect={isImageSelect}
|
||||
imageRef={imageRef}
|
||||
onImageChange={onImageChange}
|
||||
setSignature={setSignature}
|
||||
image={image}
|
||||
onSaveImage={saveImage}
|
||||
onSaveSign={saveSign}
|
||||
defaultSign={defaultSignImg}
|
||||
myInitial={myInitial}
|
||||
isInitial={isInitial}
|
||||
setIsInitial={setIsInitial}
|
||||
setIsStamp={setIsStamp}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
)}
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
isPdfRequestFiles={isPublicTemplate ? false : true}
|
||||
@@ -2221,6 +2297,10 @@ function PdfRequestFiles(props) {
|
||||
setIsLoading={setIsLoading}
|
||||
xyPostion={signerPos}
|
||||
setDefaultSignAlert={setDefaultSignAlert}
|
||||
isDefault={
|
||||
signatureType?.find((x) => x.name === "default")
|
||||
?.enabled || false
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,9 @@ import {
|
||||
onClickZoomOut,
|
||||
rotatePdfPage,
|
||||
handleRemoveWidgets,
|
||||
handleRotateWarning
|
||||
handleRotateWarning,
|
||||
signatureTypes,
|
||||
handleSignatureType
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -151,28 +153,23 @@ function PlaceHolderSign() {
|
||||
const [scale, setScale] = useState(1);
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const [planCode, setPlanCode] = useState("");
|
||||
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
|
||||
const [signatureType, setSignatureType] = useState(signatureTypes);
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [, drop] = useDrop({
|
||||
accept: "BOX",
|
||||
drop: (item, monitor) => addPositionOfSignature(item, monitor),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver()
|
||||
})
|
||||
collect: (monitor) => ({ isOver: !!monitor.isOver() })
|
||||
});
|
||||
|
||||
const [{ isDragSign }, dragSignature] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 1, text: "signature" },
|
||||
collect: (monitor) => ({
|
||||
isDragSign: !!monitor.isDragging()
|
||||
})
|
||||
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
|
||||
});
|
||||
const [{ isDragStamp }, dragStamp] = useDrag({
|
||||
type: "BOX",
|
||||
item: { type: "BOX", id: 2, text: "stamp" },
|
||||
collect: (monitor) => ({
|
||||
isDragStamp: !!monitor.isDragging()
|
||||
})
|
||||
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
|
||||
});
|
||||
const documentId = docId;
|
||||
useEffect(() => {
|
||||
@@ -201,10 +198,15 @@ function PlaceHolderSign() {
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantDetails) {
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
if (tenantDetails?.RequestBody) {
|
||||
setRequestBody(tenantDetails?.RequestBody);
|
||||
setRequestSubject(tenantDetails?.RequestSubject);
|
||||
}
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t("user-not-exist"));
|
||||
@@ -255,7 +257,7 @@ function PlaceHolderSign() {
|
||||
}
|
||||
//function for get document details
|
||||
const getDocumentDetails = async () => {
|
||||
fetchTenantDetails();
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
//getting document details
|
||||
const documentData = await contractDocument(documentId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
@@ -312,7 +314,18 @@ function PlaceHolderSign() {
|
||||
});
|
||||
}
|
||||
}
|
||||
setPdfDetails(documentData);
|
||||
const userSignatureType =
|
||||
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
|
||||
const docSignTypes =
|
||||
documentData?.[0]?.SignatureType || userSignatureType;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
const updatedPdfDetails = [...documentData];
|
||||
updatedPdfDetails[0].SignatureType = updatedSignatureType;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
//condition when placeholder have empty array with role details and signers array have signers data
|
||||
//and both array length are same
|
||||
//this case happen using placeholder form in auto save funtionality to save draft type document without adding any placehlder
|
||||
@@ -475,8 +488,10 @@ function PlaceHolderSign() {
|
||||
let dropData = [];
|
||||
let placeHolder;
|
||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
|
||||
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
const widgetWidth =
|
||||
defaultWidthHeight(dragTypeValue).width * containerScale;
|
||||
const widgetHeight =
|
||||
defaultWidthHeight(dragTypeValue).height * containerScale;
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
if (item === "onclick") {
|
||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||
@@ -813,9 +828,7 @@ function PlaceHolderSign() {
|
||||
placeholder,
|
||||
pdfDoc,
|
||||
isSignYourSelfFlow,
|
||||
scale,
|
||||
pdfOriginalWH,
|
||||
containerWH
|
||||
scale
|
||||
);
|
||||
const pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfBase64);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
@@ -844,17 +857,24 @@ function PlaceHolderSign() {
|
||||
};
|
||||
const alertSendEmail = async () => {
|
||||
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
|
||||
const getPrefill = signerPos?.find((data) => data.Role === "prefill");
|
||||
let isLabel = false;
|
||||
let unfilledTextWidgetId = "";
|
||||
//checking all signers placeholder exist or not
|
||||
const isPlaceholderExist = filterPrefill.every((data) => data.placeHolder);
|
||||
const prefillPlaceholder = getPrefill[0]?.placeHolder;
|
||||
const prefillPlaceholder = getPrefill?.placeHolder;
|
||||
//condition is used to check text widget data is empty or have response
|
||||
if (getPrefill && getPrefill.length > 0) {
|
||||
if (getPrefill) {
|
||||
if (prefillPlaceholder) {
|
||||
prefillPlaceholder.map((data) => {
|
||||
if (!isLabel) {
|
||||
isLabel = data.pos.some((position) => !position.options.response);
|
||||
const unfilledTextWidgets = data.pos.find(
|
||||
(position) => !position.options.response
|
||||
);
|
||||
if (unfilledTextWidgets) {
|
||||
isLabel = true;
|
||||
unfilledTextWidgetId = unfilledTextWidgets.key;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -882,6 +902,7 @@ function PlaceHolderSign() {
|
||||
}
|
||||
if (getPrefill && isLabel) {
|
||||
setIsSendAlert({ mssg: textWidget, alert: true });
|
||||
setUnSignedWidgetId(unfilledTextWidgetId);
|
||||
} else if (isSignatureExist) {
|
||||
if (isPlaceholderExist) {
|
||||
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
||||
@@ -901,30 +922,32 @@ function PlaceHolderSign() {
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (signerPos?.length > 0) {
|
||||
if (signerPos?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [signerPos, signersdata]);
|
||||
}, [signerPos, signersdata, signatureType]);
|
||||
|
||||
// `autosavedetails` is used to save doc details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
|
||||
const autosavedetails = async () => {
|
||||
const signers = signersdata?.filter(
|
||||
(x) =>
|
||||
x?.Role !== "prefill" &&
|
||||
x.objectId && {
|
||||
const signers = signersdata?.reduce((acc, x) => {
|
||||
if (x.objectId) {
|
||||
acc.push({
|
||||
__type: "Pointer",
|
||||
className: "contracts_Contactbook",
|
||||
objectId: x.objectId
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
try {
|
||||
const docCls = new Parse.Object("contracts_Document");
|
||||
docCls.id = documentId;
|
||||
docCls.set("Placeholders", signerPos);
|
||||
docCls.set("Signers", signers);
|
||||
docCls.set("SignatureType", signatureType);
|
||||
await docCls.save();
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
@@ -964,21 +987,15 @@ function PlaceHolderSign() {
|
||||
//filter label widgets after add label widgets data on pdf
|
||||
const filterPrefill = signerPos.filter((data) => data.Role !== "prefill");
|
||||
try {
|
||||
data = {
|
||||
Placeholders: filterPrefill,
|
||||
SignedUrl: pdfUrl,
|
||||
Signers: signers,
|
||||
SentToOthers: true,
|
||||
SignatureType: pdfDetails?.[0]?.SignatureType
|
||||
};
|
||||
if (updateExpiryDate) {
|
||||
data = {
|
||||
Placeholders: filterPrefill,
|
||||
SignedUrl: pdfUrl,
|
||||
Signers: signers,
|
||||
SentToOthers: true,
|
||||
ExpiryDate: { iso: updateExpiryDate, __type: "Date" }
|
||||
};
|
||||
} else {
|
||||
data = {
|
||||
Placeholders: filterPrefill,
|
||||
SignedUrl: pdfUrl,
|
||||
Signers: signers,
|
||||
SentToOthers: true
|
||||
};
|
||||
data["ExpiryDate"] = { iso: updateExpiryDate, __type: "Date" };
|
||||
}
|
||||
await axios
|
||||
.put(
|
||||
@@ -1330,7 +1347,7 @@ function PlaceHolderSign() {
|
||||
isHideLabel: isHideLabel || false,
|
||||
defaultValue: defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1368,7 +1385,7 @@ function PlaceHolderSign() {
|
||||
isReadOnly: isReadOnly || false,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1386,7 +1403,7 @@ function PlaceHolderSign() {
|
||||
values: dropdownOptions,
|
||||
defaultValue: defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1420,7 +1437,15 @@ function PlaceHolderSign() {
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
};
|
||||
const handleWidgetdefaultdata = (defaultdata) => {
|
||||
const handleWidgetdefaultdata = (defaultdata, isSignWidget) => {
|
||||
if (isSignWidget) {
|
||||
const updatedPdfDetails = [...pdfDetails];
|
||||
const signtypes = defaultdata.signatureType || signatureType;
|
||||
updatedPdfDetails[0].SignatureType = signtypes;
|
||||
// Update the SignatureType with the modified array
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
setSignatureType(signtypes);
|
||||
}
|
||||
const options = ["email", "number", "text"];
|
||||
let inputype;
|
||||
if (defaultdata.textvalidate) {
|
||||
@@ -1459,7 +1484,7 @@ function PlaceHolderSign() {
|
||||
}
|
||||
: {},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1475,7 +1500,7 @@ function PlaceHolderSign() {
|
||||
status: defaultdata.status,
|
||||
defaultValue: defaultdata.defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1541,14 +1566,12 @@ function PlaceHolderSign() {
|
||||
} else {
|
||||
updatedTourStatus = [{ placeholder: true }];
|
||||
}
|
||||
await axios
|
||||
.put(
|
||||
try {
|
||||
await axios.put(
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Users/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{ TourStatus: updatedTourStatus },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -1556,14 +1579,10 @@ function PlaceHolderSign() {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
// const json = Listdata.data;
|
||||
// const res = json.results;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("axois err ", err);
|
||||
});
|
||||
);
|
||||
} catch (err) {
|
||||
console.log("axois err ", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleRecipientSign = () => {
|
||||
@@ -1648,8 +1667,15 @@ function PlaceHolderSign() {
|
||||
const signerAssignTour = [
|
||||
{
|
||||
selector: '[data-tut="assignSigner"]',
|
||||
content:
|
||||
"You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour. ",
|
||||
content: t("attach-signer-tour"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
const textFieldTour = [
|
||||
{
|
||||
selector: '[data-tut="IsSigned"]',
|
||||
content: t("text-field-tour"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
@@ -1759,6 +1785,13 @@ function PlaceHolderSign() {
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
<Tour
|
||||
onRequestClose={() => setIsSendAlert({})}
|
||||
steps={textFieldTour}
|
||||
isOpen={isSendAlert.mssg === textWidget}
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdfDetails[0].URL}
|
||||
@@ -1779,20 +1812,18 @@ function PlaceHolderSign() {
|
||||
<div className=" w-full md:w-[95%] ">
|
||||
{/* this modal is used show alert set placeholder for all signers before send mail */}
|
||||
<ModalUi
|
||||
isOpen={isSendAlert.alert}
|
||||
isOpen={
|
||||
isSendAlert.alert && isSendAlert.mssg !== textWidget
|
||||
}
|
||||
title={
|
||||
isSendAlert.mssg === "sure" ||
|
||||
isSendAlert.mssg === textWidget
|
||||
? t("fields-required")
|
||||
: isSendAlert.mssg === "confirm" && t("send-mail")
|
||||
(isSendAlert.mssg === "confirm" && t("send-mail"))
|
||||
}
|
||||
handleClose={() => handleCloseSendmailModal()}
|
||||
>
|
||||
<div className="max-h-96 overflow-y-scroll scroll-hide p-[20px] text-base-content">
|
||||
{isSendAlert.mssg === "sure" ? (
|
||||
<span>{t("placeholder-alert-1")}</span>
|
||||
) : isSendAlert.mssg === textWidget ? (
|
||||
<p>{t("placeholder-alert-2")}</p>
|
||||
) : (
|
||||
isSendAlert.mssg === "confirm" && (
|
||||
<>
|
||||
@@ -1873,9 +1904,11 @@ function PlaceHolderSign() {
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
title={
|
||||
mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("Mails Sent")
|
||||
mailStatus === "success"
|
||||
? t("mails-sent")
|
||||
: mailStatus === "quotareached"
|
||||
? t("quota-mail-head")
|
||||
: t("mail-not-delivered")
|
||||
}
|
||||
handleClose={() => {
|
||||
setIsSend(false);
|
||||
@@ -1902,7 +1935,12 @@ function PlaceHolderSign() {
|
||||
<div className="my-3">{handleShareList()}</div>
|
||||
</div>
|
||||
) : (
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
<div className="mb-[10px]">
|
||||
<p>{t("placeholder-alert-6")}</p>
|
||||
{isCurrUser && (
|
||||
<p className="mt-1">{t("placeholder-alert-5")}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!mailStatus && (
|
||||
<div className="w-full h-[1px] bg-[#9f9f9f] my-[15px]"></div>
|
||||
@@ -2018,6 +2056,7 @@ function PlaceHolderSign() {
|
||||
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
completeBtnTitle={t("next")}
|
||||
isPlaceholder={true}
|
||||
pageNumber={pageNumber}
|
||||
allPages={allPages}
|
||||
@@ -2058,6 +2097,7 @@ function PlaceHolderSign() {
|
||||
setSignerPos={setSignerPos}
|
||||
containerWH={containerWH}
|
||||
setIsResize={setIsResize}
|
||||
isResize={isResize}
|
||||
setZIndex={setZIndex}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
signersdata={signersdata}
|
||||
@@ -2084,6 +2124,7 @@ function PlaceHolderSign() {
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -2208,6 +2249,7 @@ function PlaceHolderSign() {
|
||||
signersData={signersdata}
|
||||
/>
|
||||
<WidgetNameModal
|
||||
signatureType={signatureType}
|
||||
widgetName={widgetName}
|
||||
defaultdata={currWidgetsDetails}
|
||||
isOpen={isNameModal}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import {
|
||||
getTenantDetails,
|
||||
handleSignatureType,
|
||||
signatureTypes
|
||||
} from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
const Preferences = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTopLoader, setIsTopLoader] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
useEffect(() => {
|
||||
fetchSignType();
|
||||
}, []);
|
||||
|
||||
const fetchSignType = async () => {
|
||||
setIsTopLoader(true);
|
||||
try {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const tenantSignTypes = signatureType?.filter((x) => x.enabled === true);
|
||||
const getUser = await Parse.Cloud.run("getUserDetails");
|
||||
if (getUser) {
|
||||
const _getUser = JSON.parse(JSON.stringify(getUser));
|
||||
if (tenantSignTypes?.length > 0) {
|
||||
const signatureType = _getUser?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
signatureType
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
} else {
|
||||
const SignatureType = _getUser?.SignatureType || signatureTypes;
|
||||
setSignatureType(SignatureType);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting user details", err);
|
||||
setErrMsg("Something went wrong");
|
||||
} finally {
|
||||
setIsTopLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleCheckboxChange` is trigger when user enable/disable checkbox of respective type
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Create a copy of the signatureType array
|
||||
const updatedSignatureType = [...signatureType];
|
||||
// Toggle the enabled value for the clicked item
|
||||
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// Update the state with the modified array
|
||||
setSignatureType(updatedSignatureType);
|
||||
};
|
||||
|
||||
// `handleSave` is used save updated value signature type
|
||||
const handleSave = async () => {
|
||||
setIsLoader(true);
|
||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||
const isDefaultSignTypeOnly =
|
||||
enabledSignTypes?.length === 1 && enabledSignTypes[0]?.name === "default";
|
||||
if (enabledSignTypes.length === 0) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("at-least-one-signature-type")
|
||||
});
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("expect-default-one-more-signature-type")
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
const updateRes = await Parse.Cloud.run("updatesignaturetype", {
|
||||
SignatureType: signatureType
|
||||
});
|
||||
if (updateRes) {
|
||||
setIsAlert({ type: "success", msg: "Saved successfully." });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error updating signature type", err);
|
||||
setIsAlert({ type: "danger", msg: err.message });
|
||||
}
|
||||
}
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"API Token"} />
|
||||
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
|
||||
{isTopLoader ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{errMsg ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
|
||||
{isLoader && (
|
||||
<div className="flex justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
||||
OpenSign™ {t("Preferences")}{" "}
|
||||
<span>
|
||||
<Tooltip
|
||||
message={`OpenSign™ ${t("Preferences")}`}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</span>
|
||||
</h1>
|
||||
<div className="ml-4 mt-1 mb-2 flex flex-col">
|
||||
<div className="mb-[0.75rem]">
|
||||
<label
|
||||
htmlFor="signaturetype"
|
||||
className="text-[14px] mb-[0.7rem] font-medium"
|
||||
>
|
||||
{t("allowed-signature-types")}
|
||||
<a data-tooltip-id="signtypes-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="signtypes-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">
|
||||
{t("allowed-signature-types")}
|
||||
</p>
|
||||
<p>{t("allowed-signature-types-help.p1")}</p>
|
||||
<p className="p-[5px] ml-2">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">Draw: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l1")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Typed: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l2")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Upload: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l3")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Default: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l4")}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
export default Preferences;
|
||||
@@ -10,7 +10,7 @@ import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import {
|
||||
getAppLogo,
|
||||
openInNewTab,
|
||||
@@ -83,92 +83,96 @@ const Signup = () => {
|
||||
};
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
var user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
let res = user.save();
|
||||
res
|
||||
.then(async (r) => {
|
||||
if (r) {
|
||||
let roleData = appInfo.settings;
|
||||
if (roleData && roleData.length > 0) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: appInfo.defaultRole
|
||||
}
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run(
|
||||
"usersignup",
|
||||
params
|
||||
);
|
||||
if (usersignup) {
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan =
|
||||
param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(r.id);
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
var user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
let res = user.save();
|
||||
res
|
||||
.then(async (r) => {
|
||||
if (r) {
|
||||
let roleData = appInfo.settings;
|
||||
if (roleData && roleData.length > 0) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: appInfo.defaultRole
|
||||
}
|
||||
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run(
|
||||
"usersignup",
|
||||
params
|
||||
);
|
||||
if (usersignup) {
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan =
|
||||
param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(r.id);
|
||||
}
|
||||
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("user-already-exist-name"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res1) {
|
||||
if (res1.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("user-already-exist-name"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res1) {
|
||||
if (res1.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -36,7 +36,8 @@ import {
|
||||
getDefaultSignature,
|
||||
onClickZoomIn,
|
||||
onClickZoomOut,
|
||||
rotatePdfPage
|
||||
rotatePdfPage,
|
||||
signatureTypes
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
@@ -132,6 +133,7 @@ function SignYourSelf() {
|
||||
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({
|
||||
@@ -423,9 +425,7 @@ function SignYourSelf() {
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||
: defaultWidthHeight(dragTypeValue).width;
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||
: defaultWidthHeight(dragTypeValue).height;
|
||||
const getHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
|
||||
dropObj = {
|
||||
xPosition: getWidth / 2 + containerWH.width / 2,
|
||||
@@ -456,9 +456,7 @@ function SignYourSelf() {
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getWidth
|
||||
: defaultWidthHeight(dragTypeValue).width;
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getHeight
|
||||
: defaultWidthHeight(dragTypeValue).height;
|
||||
const getHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
dropObj = {
|
||||
xPosition: getXPosition / (containerScale * scale),
|
||||
yPosition: getYPosition / (containerScale * scale),
|
||||
@@ -558,7 +556,7 @@ function SignYourSelf() {
|
||||
};
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (xyPostion?.length > 0) {
|
||||
if (xyPostion?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
@@ -617,12 +615,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -649,10 +643,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;
|
||||
@@ -665,14 +658,19 @@ function SignYourSelf() {
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
isSignYourSelfFlow,
|
||||
scale,
|
||||
pdfOriginalWH,
|
||||
containerWH
|
||||
scale
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
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);
|
||||
@@ -684,10 +682,7 @@ function SignYourSelf() {
|
||||
});
|
||||
} else {
|
||||
console.log("err in signing", err.message);
|
||||
if (
|
||||
err.message ===
|
||||
"PKCS#12 MAC could not be verified. Invalid password?"
|
||||
) {
|
||||
if (err?.message?.includes("password")) {
|
||||
setIsAlert({
|
||||
header: t("error"),
|
||||
isShow: true,
|
||||
@@ -754,10 +749,10 @@ function SignYourSelf() {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
//change image width and height to 100/40 in png base64
|
||||
const getNewse64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64
|
||||
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||
//change image width and height to 300/120 in png base64
|
||||
const imagebase64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64 (without type)
|
||||
const suffixbase64 = imagebase64 && imagebase64.split(",").pop();
|
||||
const params = {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
@@ -783,7 +778,7 @@ function SignYourSelf() {
|
||||
const handleStop = (event, dragElement) => {
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
if (isDragging && dragElement) {
|
||||
if (!isResize && isDragging && dragElement) {
|
||||
event.preventDefault();
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
@@ -1028,7 +1023,6 @@ function SignYourSelf() {
|
||||
const getPageNumer = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (getPageNumer.length > 0) {
|
||||
const getXYdata = getPageNumer[0].pos;
|
||||
const getPosData = getXYdata;
|
||||
@@ -1058,7 +1052,7 @@ function SignYourSelf() {
|
||||
isReadOnly: isReadOnly,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||||
}
|
||||
@@ -1092,7 +1086,6 @@ function SignYourSelf() {
|
||||
const getPageNumer = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (getPageNumer.length > 0) {
|
||||
const getXYdata = getPageNumer[0].pos;
|
||||
const getPosData = getXYdata;
|
||||
@@ -1102,8 +1095,7 @@ function SignYourSelf() {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize: fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||||
}
|
||||
@@ -1302,6 +1294,7 @@ function SignYourSelf() {
|
||||
/>
|
||||
{/* this is modal of signature pad */}
|
||||
<SignPad
|
||||
signatureTypes={signatureTypes}
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
@@ -1400,6 +1393,8 @@ function SignYourSelf() {
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,11 @@ import {
|
||||
onClickZoomOut,
|
||||
onClickZoomIn,
|
||||
handleRemoveWidgets,
|
||||
handleRotateWarning
|
||||
handleRotateWarning,
|
||||
color,
|
||||
signatureTypes,
|
||||
getTenantDetails,
|
||||
handleSignatureType
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
@@ -68,10 +72,7 @@ const TemplatePlaceholder = () => {
|
||||
const [isSendAlert, setIsSendAlert] = useState(false);
|
||||
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isRotate, setIsRotate] = useState({
|
||||
status: false,
|
||||
degree: 0
|
||||
});
|
||||
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: t("loading-mssg")
|
||||
@@ -102,50 +103,21 @@ const TemplatePlaceholder = () => {
|
||||
const [isTextSetting, setIsTextSetting] = useState(false);
|
||||
const [pdfLoad, setPdfLoad] = useState(false);
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [, drop] = useDrop({
|
||||
accept: "BOX",
|
||||
drop: (item, monitor) => addPositionOfSignature(item, monitor),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver()
|
||||
})
|
||||
collect: (monitor) => ({ isOver: !!monitor.isOver() })
|
||||
});
|
||||
const [{ isDragSign }, dragSignature] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 1,
|
||||
text: "signature"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragSign: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 1, text: "signature" },
|
||||
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
|
||||
});
|
||||
const [{ isDragStamp }, dragStamp] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 2,
|
||||
text: "stamp"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragStamp: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 2, text: "stamp" },
|
||||
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
|
||||
});
|
||||
const [uniqueId, setUniqueId] = useState("");
|
||||
const [isModalRole, setIsModalRole] = useState(false);
|
||||
@@ -166,6 +138,7 @@ const TemplatePlaceholder = () => {
|
||||
const [fontColor, setFontColor] = useState();
|
||||
const [zoomPercent, setZoomPercent] = useState(0);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplate();
|
||||
@@ -213,9 +186,38 @@ const TemplatePlaceholder = () => {
|
||||
handleNavigation(plan);
|
||||
}
|
||||
}
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
if (user) {
|
||||
try {
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantDetails) {
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
} else {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
|
||||
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
||||
const fetchTemplate = async () => {
|
||||
try {
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const params = { templateId: templateId };
|
||||
const templateDeatils = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
|
||||
@@ -237,7 +239,18 @@ const TemplatePlaceholder = () => {
|
||||
if (isEnableSubscription) {
|
||||
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
|
||||
}
|
||||
setPdfDetails(documentData);
|
||||
const userSignatureType =
|
||||
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
|
||||
const docSignTypes =
|
||||
documentData?.[0]?.SignatureType || userSignatureType;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
const updatedPdfDetails = [...documentData];
|
||||
updatedPdfDetails[0].SignatureType = updatedSignatureType;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
setIsSigners(true);
|
||||
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
|
||||
setIsSelectId(0);
|
||||
@@ -260,11 +273,7 @@ const TemplatePlaceholder = () => {
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
Role: x.Role,
|
||||
Id: x.Id,
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
|
||||
}
|
||||
});
|
||||
setSignersData(updatedSigners);
|
||||
@@ -287,11 +296,7 @@ const TemplatePlaceholder = () => {
|
||||
documentData[0].Placeholders.length > 0
|
||||
) {
|
||||
let updatedSigners = documentData[0].Placeholders.map((x) => {
|
||||
return {
|
||||
Role: x.Role,
|
||||
Id: x.Id,
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
|
||||
});
|
||||
setSignerPos(documentData[0].Placeholders);
|
||||
setUniqueId(updatedSigners[0].Id);
|
||||
@@ -304,17 +309,33 @@ const TemplatePlaceholder = () => {
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
} else {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
const res = await contractUsers();
|
||||
if (res[0] && res.length) {
|
||||
setSignerUserId(res[0].objectId);
|
||||
setCurrentEmail(res[0].Email);
|
||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourstatus && tourstatus.length > 0) {
|
||||
setTourStatus(tourstatus);
|
||||
const checkTourRecipients = tourstatus.filter(
|
||||
(data) => data.templateTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||
}
|
||||
}
|
||||
setIsLoading({ isLoad: false });
|
||||
} else if (res === "Error: Something went wrong!") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading({ isLoad: false });
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
@@ -324,37 +345,6 @@ const TemplatePlaceholder = () => {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
const res = await contractUsers();
|
||||
if (res[0] && res.length) {
|
||||
setSignerUserId(res[0].objectId);
|
||||
setCurrentEmail(res[0].Email);
|
||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourstatus && tourstatus.length > 0) {
|
||||
setTourStatus(tourstatus);
|
||||
const checkTourRecipients = tourstatus.filter(
|
||||
(data) => data.templateTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||
}
|
||||
}
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
} else if (res === "Error: Something went wrong!") {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
}
|
||||
};
|
||||
|
||||
//function for setting position after drop signature button over pdf
|
||||
@@ -379,8 +369,10 @@ const TemplatePlaceholder = () => {
|
||||
);
|
||||
const key = randomId();
|
||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
|
||||
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
const widgetWidth =
|
||||
defaultWidthHeight(dragTypeValue).width * containerScale;
|
||||
const widgetHeight =
|
||||
defaultWidthHeight(dragTypeValue).height * containerScale;
|
||||
let dropData = [],
|
||||
currentPagePosition;
|
||||
let placeHolder;
|
||||
@@ -402,10 +394,7 @@ const TemplatePlaceholder = () => {
|
||||
Height: widgetHeight / (containerScale * scale)
|
||||
};
|
||||
dropData.push(dropObj);
|
||||
placeHolder = {
|
||||
pageNumber: pageNumber,
|
||||
pos: dropData
|
||||
};
|
||||
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
||||
} else {
|
||||
const offset = monitor.getClientOffset();
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
@@ -436,10 +425,7 @@ const TemplatePlaceholder = () => {
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
placeHolder = {
|
||||
pageNumber: pageNumber,
|
||||
pos: dropData
|
||||
};
|
||||
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
||||
}
|
||||
let filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
|
||||
const getPlaceHolder = filterSignerPos?.placeHolder;
|
||||
@@ -456,10 +442,7 @@ const TemplatePlaceholder = () => {
|
||||
);
|
||||
const getPos = currentPagePosition?.pos;
|
||||
const newSignPos = getPos.concat(dropData);
|
||||
let xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
pos: newSignPos
|
||||
};
|
||||
let xyPos = { pageNumber: pageNumber, pos: newSignPos };
|
||||
updatePlace.push(xyPos);
|
||||
const updatesignerPos = signerPos.map((x) =>
|
||||
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
|
||||
@@ -495,7 +478,6 @@ const TemplatePlaceholder = () => {
|
||||
setWidgetType(dragTypeValue);
|
||||
setSignKey(key);
|
||||
setSelectWidgetId(key);
|
||||
|
||||
setIsMailSend(false);
|
||||
} else {
|
||||
setIsReceipent(false);
|
||||
@@ -594,9 +576,7 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
}
|
||||
setIsMailSend(false);
|
||||
setTimeout(() => {
|
||||
setIsDragging(false);
|
||||
}, 200);
|
||||
setTimeout(() => setIsDragging(false), 200);
|
||||
};
|
||||
//function for delete signature block
|
||||
const handleDeleteSign = (key, Id) => {
|
||||
@@ -712,7 +692,7 @@ const TemplatePlaceholder = () => {
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [signerPos]);
|
||||
}, [signerPos, signatureType]);
|
||||
|
||||
// `autosavedetails` is used to save template details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
|
||||
const autosavedetails = async () => {
|
||||
@@ -734,6 +714,7 @@ const TemplatePlaceholder = () => {
|
||||
templateCls.id = templateId;
|
||||
templateCls.set("Placeholders", signerPos);
|
||||
templateCls.set("Signers", signers);
|
||||
templateCls.set("SignatureType", signatureType);
|
||||
await templateCls.save();
|
||||
} catch (err) {
|
||||
console.log("error in autosave template", err);
|
||||
@@ -742,8 +723,7 @@ const TemplatePlaceholder = () => {
|
||||
|
||||
const handleSaveTemplate = async () => {
|
||||
if (signersdata?.length) {
|
||||
const loadObj = { isLoad: true, message: t("loading-mssg") };
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: true, message: t("loading-mssg") });
|
||||
setIsSendAlert(false);
|
||||
let signers = [];
|
||||
if (signersdata?.length > 0) {
|
||||
@@ -781,7 +761,9 @@ const TemplatePlaceholder = () => {
|
||||
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||
NextReminderDate: pdfDetails[0]?.NextReminderDate,
|
||||
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
|
||||
URL: pdfUrl
|
||||
IsTourEnabled: pdfDetails[0]?.IsTourEnabled === true ? true : false,
|
||||
URL: pdfUrl,
|
||||
SignatureType: signatureType
|
||||
};
|
||||
const updateTemplate = new Parse.Object("contracts_Template");
|
||||
updateTemplate.id = templateId;
|
||||
@@ -890,9 +872,7 @@ const TemplatePlaceholder = () => {
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Users/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{ TourStatus: updatedTourStatus },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -1135,7 +1115,7 @@ const TemplatePlaceholder = () => {
|
||||
isReadOnly: isReadOnly || false,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1173,7 +1153,7 @@ const TemplatePlaceholder = () => {
|
||||
defaultValue: defaultValue,
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1191,7 +1171,7 @@ const TemplatePlaceholder = () => {
|
||||
values: dropdownOptions,
|
||||
defaultValue: defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1226,7 +1206,15 @@ const TemplatePlaceholder = () => {
|
||||
setFontColor();
|
||||
};
|
||||
|
||||
const handleWidgetdefaultdata = (defaultdata) => {
|
||||
const handleWidgetdefaultdata = (defaultdata, isSignWidget) => {
|
||||
if (isSignWidget) {
|
||||
const updatedPdfDetails = [...pdfDetails];
|
||||
const signtypes = defaultdata.signatureType || signatureType;
|
||||
updatedPdfDetails[0].SignatureType = signtypes;
|
||||
// Update the SignatureType with the modified array
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
setSignatureType(signtypes);
|
||||
}
|
||||
const options = ["email", "number", "text"];
|
||||
let inputype;
|
||||
if (defaultdata.textvalidate) {
|
||||
@@ -1266,7 +1254,7 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
: {},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1282,7 +1270,7 @@ const TemplatePlaceholder = () => {
|
||||
status: defaultdata.status,
|
||||
defaultValue: defaultdata.defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1599,6 +1587,7 @@ const TemplatePlaceholder = () => {
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1721,6 +1710,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
</ModalUi>
|
||||
<WidgetNameModal
|
||||
signatureType={signatureType}
|
||||
widgetName={widgetName}
|
||||
defaultdata={currWidgetsDetails}
|
||||
isOpen={isNameModal}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { NavLink, useNavigate } from "react-router-dom";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
const UpdateExistUserAdmin = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -35,33 +36,37 @@ const UpdateExistUserAdmin = () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmitLoading(true);
|
||||
try {
|
||||
const updateUserAsAdmin = await Parse.Cloud.run(
|
||||
"updateuserasadmin",
|
||||
formdata
|
||||
);
|
||||
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
|
||||
if (updateUserAsAdmin === "admin_created") {
|
||||
setIsAlert({ type: "success", msg: t("admin-created") });
|
||||
navigate("/");
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsSubmitLoading(true);
|
||||
try {
|
||||
const updateUserAsAdmin = await Parse.Cloud.run(
|
||||
"updateuserasadmin",
|
||||
formdata
|
||||
);
|
||||
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
|
||||
if (updateUserAsAdmin === "admin_created") {
|
||||
setIsAlert({ type: "success", msg: t("admin-created") });
|
||||
navigate("/");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in updateuserasadmin", err.code);
|
||||
if (err.code === 404) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
|
||||
} else {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitLoading(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert(() => ({ type: "danger", msg: "" }));
|
||||
}, 2000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in updateuserasadmin", err.code);
|
||||
if (err.code === 404) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
|
||||
} else {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitLoading(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert(() => ({ type: "danger", msg: "" }));
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "./Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
import { getTenantDetails } from "../constant/Utils";
|
||||
import { emailRegex } from "../constant/const";
|
||||
const AddContact = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = useState("");
|
||||
@@ -12,6 +14,7 @@ const AddContact = (props) => {
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
// Load user details from localStorage when the component mounts
|
||||
useEffect(() => {
|
||||
@@ -26,127 +29,61 @@ const AddContact = (props) => {
|
||||
}, [addYourself]);
|
||||
|
||||
const checkUserExist = async () => {
|
||||
const user = Parse.User.current();
|
||||
try {
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", user.getEmail());
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/isuserincontactbook`;
|
||||
const token = props?.jwttoken
|
||||
? { jwttoken: props?.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const axiosRes = await axios.post(url, {}, { headers });
|
||||
const contactRes = axiosRes?.data?.result || {};
|
||||
if (!contactRes?.objectId) {
|
||||
setIsUserExist(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
console.log("err ", err);
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
props.details(parseData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details(parseData);
|
||||
}
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
const userId = user?.objectId || "";
|
||||
const tenantDetails = await getTenantDetails(userId, props.jwttoken);
|
||||
const tenantId = tenantDetails?.objectId || "";
|
||||
if (tenantId) {
|
||||
try {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/savecontact`;
|
||||
const token = props?.jwttoken
|
||||
? { jwttoken: props?.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const data = { name, email, phone, tenantId };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const axiosRes = await axios.post(url, data, { headers });
|
||||
const contactRes = axiosRes?.data?.result || {};
|
||||
if (contactRes?.objectId) {
|
||||
props.details(contactRes);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
@@ -155,18 +92,19 @@ const AddContact = (props) => {
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
setIsLoader(false);
|
||||
if (err?.response?.data?.error?.includes("already exists")) {
|
||||
alert(t("add-signer-alert"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log("err", err);
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -34,11 +34,13 @@ function DownloadPdfZip(props) {
|
||||
|
||||
try {
|
||||
// Fetch the first PDF (Signed Document)
|
||||
const docId =
|
||||
props.isDocId && !pdfDetails?.[0]?.IsEnableOTP
|
||||
? pdfDetails?.[0]?.objectId
|
||||
: "";
|
||||
const signedUrl = await getSignedUrl(pdfUrl, docId);
|
||||
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}`);
|
||||
|
||||
@@ -15,7 +15,10 @@ import {
|
||||
copytoData,
|
||||
fetchUrl,
|
||||
getSignedUrl,
|
||||
replaceMailVaribles
|
||||
getTenantDetails,
|
||||
handleSignatureType,
|
||||
replaceMailVaribles,
|
||||
signatureTypes
|
||||
} from "../constant/Utils";
|
||||
import Confetti from "react-confetti";
|
||||
import EditorToolbar, {
|
||||
@@ -77,6 +80,7 @@ const ReportTable = (props) => {
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const [isEmbed, setIsEmbed] = useState(false);
|
||||
const [isPublicTour, setIsPublicTour] = useState();
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
const startIndex = (currentPage - 1) * props.docPerPage;
|
||||
@@ -195,6 +199,33 @@ const ReportTable = (props) => {
|
||||
}
|
||||
}, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]);
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
if (user) {
|
||||
try {
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantDetails) {
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
} else {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
|
||||
// `handleURL` is used to open microapp
|
||||
const handleURL = async (item, act) => {
|
||||
if (props.ReportName === "Templates") {
|
||||
@@ -203,9 +234,7 @@ const ReportTable = (props) => {
|
||||
} else {
|
||||
setActLoader({ [`${item.objectId}_${act.btnId}`]: true });
|
||||
try {
|
||||
const params = {
|
||||
templateId: item.objectId
|
||||
};
|
||||
const params = { templateId: item.objectId };
|
||||
const templateDeatils = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
|
||||
params,
|
||||
@@ -249,6 +278,16 @@ const ReportTable = (props) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const docSignTypes = Doc?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
const SignatureType =
|
||||
updatedSignatureType.length > 0
|
||||
? { SignatureType: updatedSignatureType }
|
||||
: {};
|
||||
let placeholdersArr = [];
|
||||
if (Doc.Placeholders?.length > 0) {
|
||||
placeholdersArr = Doc.Placeholders;
|
||||
@@ -274,7 +313,9 @@ const ReportTable = (props) => {
|
||||
SendinOrder: Doc?.SendinOrder || false,
|
||||
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: Doc?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false
|
||||
IsEnableOTP: Doc?.IsEnableOTP || false,
|
||||
FileAdapterId: Doc?.FileAdapterId || "",
|
||||
...SignatureType
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(
|
||||
@@ -602,25 +643,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);
|
||||
}
|
||||
@@ -671,12 +702,21 @@ const ReportTable = (props) => {
|
||||
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 {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal({ [item.objectId]: true });
|
||||
} else {
|
||||
const signedUrl = await getSignedUrl(url);
|
||||
const signedUrl = await getSignedUrl(
|
||||
url,
|
||||
docId,
|
||||
fileAdapterId,
|
||||
templateId
|
||||
);
|
||||
await fetchUrl(signedUrl, pdfName);
|
||||
}
|
||||
setActLoader({});
|
||||
@@ -910,6 +950,13 @@ const ReportTable = (props) => {
|
||||
}
|
||||
);
|
||||
const templateRes = axiosRes.data && axiosRes.data.result;
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const docSignTypes = templateRes?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
setPlaceholders(templateRes?.Placeholders);
|
||||
setTemplateDetails(templateRes);
|
||||
setIsLoader({});
|
||||
@@ -1165,7 +1212,7 @@ const ReportTable = (props) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="p-2 w-full overflow-hidden bg-base-100 text-base-content op-card shadow-lg">
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti
|
||||
@@ -1226,11 +1273,17 @@ const ReportTable = (props) => {
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isDashboard && props.List?.length > 0 ? "min-h-[317px]" : "h-full"
|
||||
} overflow-auto w-full`}
|
||||
className={`overflow-auto w-full border-b ${
|
||||
props.List?.length > 0
|
||||
? isDashboard
|
||||
? "min-h-[317px]"
|
||||
: currentList?.length === props.docPerPage
|
||||
? "h-fit"
|
||||
: "h-screen"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<table className="op-table border-collapse w-full ">
|
||||
<table className="op-table border-collapse w-full mb-4">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
{props.heading?.map((item, index) => (
|
||||
@@ -1311,7 +1364,14 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
<tr
|
||||
className={`${
|
||||
currentList?.length === props.docPerPage
|
||||
? "last:border-none"
|
||||
: ""
|
||||
} border-y-[1px] `}
|
||||
key={index}
|
||||
>
|
||||
{props.heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
{startIndex + index + 1}
|
||||
@@ -1479,13 +1539,13 @@ const ReportTable = (props) => {
|
||||
}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("submit")}
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleClose(item)}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("cancel")}
|
||||
{t("no")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1590,9 +1650,7 @@ const ReportTable = (props) => {
|
||||
className={
|
||||
act.action !== "option"
|
||||
? `${
|
||||
act?.btnColor
|
||||
? act.btnColor
|
||||
: ""
|
||||
act?.btnColor || ""
|
||||
} op-btn op-btn-sm mr-1`
|
||||
: "text-base-content focus:outline-none text-lg mr-2 relative"
|
||||
}
|
||||
@@ -1609,9 +1667,10 @@ const ReportTable = (props) => {
|
||||
)}`}
|
||||
</span>
|
||||
)}
|
||||
{/* template report */}
|
||||
{isOption[item.objectId] &&
|
||||
act.action === "option" && (
|
||||
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
|
||||
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow-black/20 shadow bg-base-100 text-base-content rounded-box">
|
||||
{act.subaction?.map((subact) => (
|
||||
<li
|
||||
key={subact.btnId}
|
||||
@@ -1665,9 +1724,10 @@ const ReportTable = (props) => {
|
||||
{t(`btnLabel.${act.btnLabel}`)}
|
||||
</span>
|
||||
)}
|
||||
{/* doc report */}
|
||||
{isOption[item.objectId] &&
|
||||
act.action === "option" && (
|
||||
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow bg-base-100 text-base-content rounded-box">
|
||||
<ul className="absolute -right-1 top-auto z-[70] w-max op-dropdown-content op-menu shadow-black/20 shadow bg-base-100 text-base-content rounded-box">
|
||||
{act.subaction?.map((subact) => (
|
||||
<li
|
||||
key={subact.btnId}
|
||||
@@ -1920,6 +1980,7 @@ const ReportTable = (props) => {
|
||||
Placeholders={placeholders}
|
||||
item={templateDeatils}
|
||||
handleClose={handleQuickSendClose}
|
||||
signatureType={signatureType}
|
||||
/>
|
||||
)}
|
||||
</ModalUi>
|
||||
@@ -2129,6 +2190,24 @@ const ReportTable = (props) => {
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
{props.List?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{props.List.length > props.docPerPage && (
|
||||
@@ -2160,24 +2239,6 @@ const ReportTable = (props) => {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{props.List?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">
|
||||
{t("no-data-avaliable")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
title={t("add-contact")}
|
||||
isOpen={isContactform}
|
||||
|
||||
@@ -19,11 +19,16 @@ const LinkUserModal = (props) => {
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
signersData={props?.signersData}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
<div className="op-divider text-base-content mx-[25%] my-1">
|
||||
{t("or")}
|
||||
</div>
|
||||
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
|
||||
<AddContact
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "My Signature",
|
||||
"API Token": "API Token",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferences",
|
||||
"Teams": "Teams",
|
||||
"Team-update-successfully": "Team Updated successfully.",
|
||||
"Users": "Users"
|
||||
@@ -291,7 +292,7 @@
|
||||
"subject": "Subject",
|
||||
"body": "Body",
|
||||
"add-contact": "Add Contact",
|
||||
"add-signer-alert": "Contact already exist!",
|
||||
"add-signer-alert": "Contact already exist! Please select it from ‘Signers’ dropdown",
|
||||
"record-delete-alert": "Record deleted successfully!",
|
||||
"record-revoke-alert": "Record revoked successfully!",
|
||||
"mail-sent-alert": "Mail sent successfully.",
|
||||
@@ -445,7 +446,8 @@
|
||||
"cutomize-email": "Cutomize Email",
|
||||
"upgrade-to-customize-email": "Upgrade to customize Email",
|
||||
"sign-url": "Sign url",
|
||||
"Mails Sent": "Mails Sent",
|
||||
"mails-sent": "Mails Sent",
|
||||
"mail-not-delivered": "Mail not delivered",
|
||||
"document-alert": "Document Alert",
|
||||
"owner-subscription-expired": "Owner's subscription has expired.",
|
||||
"subscription-expired": "Subscription Expired",
|
||||
@@ -672,6 +674,28 @@
|
||||
"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.",
|
||||
"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."
|
||||
"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 Settings",
|
||||
"public-tour-message": "The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it.",
|
||||
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>",
|
||||
"text-field-tour": "Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
|
||||
"attach-signer-tour": "You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
|
||||
"allowed-signature-types": "Allowed signature types",
|
||||
"at-least-one-signature-type": "At least one signature type should be enabled.",
|
||||
"expect-default-one-signature-type": "Please enable one additional signature type apart from the default.",
|
||||
"add-signer-note": "Note - This contact will be added to your contact list",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "This signature preference controls which signing options are available to your signers. Only the signature types you select will appear in the signature widget when a document is signed. For example, if you choose only the 'Draw' option in preferences, your signer will see only the 'Draw' option in the signature widget, while the other three options will not be available.",
|
||||
"l1": "Create a handwritten signature directly on the screen using your finger or a stylus.",
|
||||
"l2": "Enter your name and choose from various font styles to create a signature.",
|
||||
"l3": "Upload an existing digital version of your signature.",
|
||||
"l4": "Use a pre-set signature style saved under 'My Signature' in your settings."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferencias",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
@@ -291,7 +292,7 @@
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe!",
|
||||
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
@@ -445,7 +446,8 @@
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"Mails Sent": "Correos enviados",
|
||||
"mails-sent": "Correos enviados",
|
||||
"mail-not-delivered": "correo no entregado",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
@@ -673,5 +675,27 @@
|
||||
"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."
|
||||
"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",
|
||||
"text-field-tour": "Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
|
||||
"attach-signer-tour": "Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
|
||||
"allowed-signature-types": "Tipos de firma permitidos",
|
||||
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
|
||||
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
|
||||
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
|
||||
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
|
||||
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
|
||||
"l3": "Cargue una versión digital existente de su firma.",
|
||||
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"My Signature": "Ma signature",
|
||||
"API Token": "Jeton API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Préférences",
|
||||
"Teams": "Équipe",
|
||||
"Team-update-successfully": "Équipe mise à jour avec succès.",
|
||||
"Users": "Utilisateurs"
|
||||
@@ -290,7 +291,7 @@
|
||||
"subject": "Sujet",
|
||||
"body": "Corps",
|
||||
"add-contact": "Ajouter le contact",
|
||||
"add-signer-alert": "Le contact existe déjà !",
|
||||
"add-signer-alert": "Le contact existe déjà ! Veuillez le sélectionner dans la liste déroulante 'Signataires'",
|
||||
"record-delete-alert": "Enregistrement supprimé avec succès !",
|
||||
"record-revoke-alert": " Enregistrement révoqué avec succès ! ",
|
||||
"mail-sent-alert": "Courriel envoyé avec succès.",
|
||||
@@ -444,7 +445,8 @@
|
||||
"cutomize-email": "Personnaliser l'e-mail",
|
||||
"upgrade-to-customize-email": "Mise à niveau pour personnaliser l'e-mail",
|
||||
"sign-url": "Signer l'URL",
|
||||
"Mails Sent": "Mails envoyés avec succès !",
|
||||
"mails-sent": "Mails envoyés avec succès !",
|
||||
"mail-not-delivered": "Courrier non distribué",
|
||||
"document-alert": "Alerte document",
|
||||
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
|
||||
"subscription-expired": "Abonnement expiré",
|
||||
@@ -639,7 +641,7 @@
|
||||
"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.",
|
||||
"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",
|
||||
@@ -671,6 +673,28 @@
|
||||
"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. "
|
||||
"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>",
|
||||
"text-field-tour": "Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
|
||||
"attach-signer-tour": "Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
|
||||
"allowed-signature-types": "Types de signature autorisés",
|
||||
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
|
||||
"expect-default-one-signature-type": "Veuillez activer un type de signature supplémentaire en plus de celui par défaut.",
|
||||
"add-signer-note": "Remarque - Ce contact sera ajouté à votre liste de contacts",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Cette préférence de signature contrôle les options de signature disponibles pour vos signataires. Seuls les types de signature que vous sélectionnez apparaîtront dans le widget de signature lorsqu'un document est signé. Par exemple, si vous choisissez uniquement l'option 'Draw' dans les préférences, votre signataire ne verra que l'option 'Draw' dans le widget de signature, tandis que les trois autres options ne seront pas disponibles.",
|
||||
"l1": "Créez une signature manuscrite directement sur l'écran à l'aide de votre doigt ou d'un stylet.",
|
||||
"l2": "Entrez votre nom et choisissez parmi différents styles de police pour créer une signature.",
|
||||
"l3": "Téléchargez une version numérique existante de votre signature.",
|
||||
"l4": "Utilisez un style de signature prédéfini enregistré sous 'Ma signature' dans vos paramètres."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +127,15 @@ export const updateMailCount = async (extUserId, plan, monthchange) => {
|
||||
};
|
||||
|
||||
export function formatWidgetOptions(type, options) {
|
||||
const colorsArr = ['red', 'black', 'blue', 'yellow'];
|
||||
const fontSizes = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||
const status = options?.required === true ? 'required' : 'optional' || 'required';
|
||||
const defaultValue = options?.default || '';
|
||||
const values = options?.values || [];
|
||||
const color = options?.color ? options.color : 'black';
|
||||
const fontColor = colorsArr.includes(color) ? color : 'black';
|
||||
const size = options?.fontsize ? parseInt(options.fontsize) : 12;
|
||||
const fontSize = fontSizes.includes(size) ? size : 12;
|
||||
switch (type) {
|
||||
case 'signature':
|
||||
return { name: 'signature', status: 'required' };
|
||||
@@ -140,13 +146,34 @@ export function formatWidgetOptions(type, options) {
|
||||
case 'image':
|
||||
return { status: status, name: options.name || 'image' };
|
||||
case 'email':
|
||||
return { status: status, name: options.name || 'email', validation: { type: 'email' } };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'email',
|
||||
validation: { type: 'email' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'name':
|
||||
return { status: status, name: options.name || 'name' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'name',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'job title':
|
||||
return { status: status, name: options.name || 'job title' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'job title',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'company':
|
||||
return { status: status, name: options.name || 'company' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'company',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'date': {
|
||||
let today = new Date();
|
||||
let dd = String(today.getDate()).padStart(2, '0');
|
||||
@@ -160,6 +187,8 @@ export function formatWidgetOptions(type, options) {
|
||||
name: options.name || 'date',
|
||||
response: defaultValue || today,
|
||||
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'textbox':
|
||||
@@ -169,6 +198,8 @@ export function formatWidgetOptions(type, options) {
|
||||
defaultValue: defaultValue,
|
||||
hint: options.hint,
|
||||
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'checkbox': {
|
||||
const arr = options?.values;
|
||||
@@ -188,6 +219,8 @@ export function formatWidgetOptions(type, options) {
|
||||
maxRequiredCount: options?.validation?.maxselections || 0,
|
||||
},
|
||||
defaultValue: selectedvalues || [],
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'radio button': {
|
||||
@@ -198,6 +231,8 @@ export function formatWidgetOptions(type, options) {
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'dropdown':
|
||||
@@ -206,6 +241,8 @@ export function formatWidgetOptions(type, options) {
|
||||
name: options.name || 'dropdown',
|
||||
values: values,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
@@ -234,3 +271,7 @@ export const planCredits = {
|
||||
'teams-monthly': 100,
|
||||
'teams-yearly': 500,
|
||||
};
|
||||
|
||||
export function parseJwt(token) {
|
||||
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -9,8 +9,6 @@ import getDocument from './routes/getDocument.js';
|
||||
import getContact from './routes/getContact.js';
|
||||
import deleteContact from './routes/deleteContact.js';
|
||||
import getContactList from './routes/getContactList.js';
|
||||
import draftDocument from './routes/draftDocument.js';
|
||||
import createTemplate from './routes/createTemplate.js';
|
||||
import getTemplate from './routes/getTemplate.js';
|
||||
import deletedTemplate from './routes/deleteTemplate.js';
|
||||
import getTemplatetList from './routes/getTemplateList.js';
|
||||
@@ -31,6 +29,7 @@ import createFolder from './routes/createFolder.js';
|
||||
import updateFolder from './routes/updateFolder.js';
|
||||
import getFolderList from './routes/getFolderList.js';
|
||||
import deleteFolder from './routes/deleteFolder.js';
|
||||
import draftTemplate from './routes/draftTemplate.js';
|
||||
dotenv.config();
|
||||
const storage = multer.memoryStorage();
|
||||
const upload = multer({ storage: storage });
|
||||
@@ -60,9 +59,6 @@ app.post('/createdocumentwithbinary', upload.array('file', 1), createDocumentwit
|
||||
// create Document with co-ordinate
|
||||
app.post('/createdocument', createDocumentwithCoordinate);
|
||||
|
||||
// create Document with base64 without placeholder
|
||||
app.post('/draftdocument', draftDocument);
|
||||
|
||||
// create Document with templateId
|
||||
app.post('/createdocument/:template_id', createDocumentWithTemplate);
|
||||
|
||||
@@ -78,12 +74,12 @@ app.delete('/document/:document_id', deleteDocument);
|
||||
// get all types of documents on the basis of doctype
|
||||
app.get('/documentlist/:doctype', getDocumentList);
|
||||
|
||||
// create draft template with file base64
|
||||
app.post('/drafttemplate', draftTemplate);
|
||||
|
||||
// create Template with co-ordinate
|
||||
app.post('/createtemplate', createTemplatewithCoordinate);
|
||||
|
||||
// create Template
|
||||
app.post('/drafttemplate', createTemplate);
|
||||
|
||||
// create Template with binary
|
||||
app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplatewithCoordinate);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
const send_email = request.body.send_email;
|
||||
const email_subject = request.body.email_subject;
|
||||
const email_body = request.body.email_body;
|
||||
const sendInOrder = request.body.sendInOrder || false;
|
||||
const sendInOrder = request.body.sendInOrder || true;
|
||||
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||
|
||||
try {
|
||||
@@ -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));
|
||||
@@ -199,6 +200,10 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
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', {
|
||||
@@ -209,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);
|
||||
|
||||
@@ -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) {
|
||||
@@ -61,9 +62,10 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const email_subject = request.body.email_subject;
|
||||
const email_body = request.body.email_body;
|
||||
const sendInOrder = request.body.sendInOrder || false;
|
||||
const sendInOrder = request.body.sendInOrder || true;
|
||||
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
@@ -87,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');
|
||||
@@ -114,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');
|
||||
@@ -126,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);
|
||||
|
||||
@@ -164,7 +174,11 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
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;
|
||||
|
||||
@@ -1,140 +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);
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
|
||||
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);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
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) {
|
||||
@@ -16,8 +17,9 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
const folderId = request.body.folderId;
|
||||
const base64File = request.body.file;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const SendinOrder = request.body.sendInOrder || true;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
@@ -40,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');
|
||||
@@ -52,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);
|
||||
}
|
||||
@@ -83,6 +97,7 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
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;
|
||||
@@ -186,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');
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
import axios from 'axios';
|
||||
import { customAPIurl, saveFileUsage } from '../../../../Utils.js';
|
||||
|
||||
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
export default async function draftDocument(request, response) {
|
||||
const name = request.body.title;
|
||||
const note = request.body.note;
|
||||
const description = request.body.description;
|
||||
const signers = request.body.signers;
|
||||
const folderId = request.body.folderId;
|
||||
const base64File = request.body.file;
|
||||
const send_email = request.body.send_email || true;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || false;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
|
||||
// console.log('fileData ', fileData);
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
|
||||
try {
|
||||
const reqToken = request.headers['x-api-token'];
|
||||
if (!reqToken) {
|
||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||
}
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('token', reqToken);
|
||||
tokenQuery.include('userId');
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: parseUser.userId.objectId,
|
||||
};
|
||||
// Valid Token then proceed request
|
||||
if (signers && signers.length > 0) {
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const file = new Parse.File(`${name}.pdf`, {
|
||||
base64: base64File,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const extUserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
};
|
||||
|
||||
const folderPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Document',
|
||||
objectId: folderId,
|
||||
};
|
||||
|
||||
const object = new Parse.Object('contracts_Document');
|
||||
object.set('Name', name);
|
||||
if (note) {
|
||||
object.set('Note', note);
|
||||
}
|
||||
if (description) {
|
||||
object.set('Description', description);
|
||||
}
|
||||
if (SendinOrder) {
|
||||
object.set('SendinOrder', SendinOrder);
|
||||
}
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsSendMail', send_email);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
if (base64File) {
|
||||
parseSigners = signers;
|
||||
} else {
|
||||
parseSigners = JSON.parse(signers);
|
||||
}
|
||||
let createContactUrl = protocol + '/v1/createcontact';
|
||||
|
||||
let contact = [];
|
||||
for (const obj of parseSigners) {
|
||||
const body = {
|
||||
name: obj?.name || '',
|
||||
email: obj?.email || '',
|
||||
phone: obj?.phone || '',
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(createContactUrl, body, {
|
||||
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
|
||||
});
|
||||
// console.log('res ', res.data);
|
||||
contact.push({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: res.data?.objectId,
|
||||
});
|
||||
} catch (err) {
|
||||
// console.log('err ', err.response);
|
||||
if (err?.response?.data?.objectId) {
|
||||
contact.push({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: err.response.data?.objectId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
object.set('Signers', contact);
|
||||
}
|
||||
if (folderId) {
|
||||
object.set('Folder', folderPtr);
|
||||
}
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
newACL.setReadAccess(userPtr.objectId, true);
|
||||
newACL.setWriteAccess(userPtr.objectId, true);
|
||||
object.setACL(newACL);
|
||||
const res = await object.save(null, { useMasterKey: true });
|
||||
if (request.posthog) {
|
||||
request.posthog?.capture({
|
||||
distinctId: parseUser.userId.email,
|
||||
event: 'api_draft_document',
|
||||
properties: { response_code: 200 },
|
||||
});
|
||||
}
|
||||
return response.json({
|
||||
objectId: res.id,
|
||||
url: `${baseUrl.origin}/placeholdersign/${res.id}`,
|
||||
});
|
||||
} else {
|
||||
if (request.posthog) {
|
||||
request.posthog?.capture({
|
||||
distinctId: parseUser.userId.email,
|
||||
event: 'api_draft_document',
|
||||
properties: { response_code: 400 },
|
||||
});
|
||||
}
|
||||
return response.status(400).json({ error: 'Please provide signers!' });
|
||||
}
|
||||
} else {
|
||||
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import axios from 'axios';
|
||||
import {
|
||||
color,
|
||||
customAPIurl,
|
||||
saveFileUsage,
|
||||
formatWidgetOptions,
|
||||
sanitizeFileName,
|
||||
} from '../../../../Utils.js';
|
||||
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
export default async function draftTemplate(request, response) {
|
||||
const name = request.body.title;
|
||||
const note = request.body.note;
|
||||
const description = request.body.description;
|
||||
const signers = request.body.signers;
|
||||
const folderId = request.body.folderId;
|
||||
const base64File = request.body.file;
|
||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||
const SendinOrder = request.body.sendInOrder || true;
|
||||
const isEnableOTP = request.body?.enableOTP === true ? true : false;
|
||||
const isTourEnabled = request.body?.enableTour || false;
|
||||
const isPublic = request.body?.isPublic;
|
||||
const protocol = customAPIurl();
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL).origin;
|
||||
|
||||
try {
|
||||
const reqToken = request.headers['x-api-token'];
|
||||
if (!reqToken) {
|
||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||
}
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('token', reqToken);
|
||||
tokenQuery.include('userId');
|
||||
const token = await tokenQuery.first({ useMasterKey: true });
|
||||
if (token !== undefined) {
|
||||
// Valid Token then proceed request
|
||||
const parseUser = JSON.parse(JSON.stringify(token));
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: parseUser.userId.objectId,
|
||||
};
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
contractsUser.include('TenantId');
|
||||
const extUser = await contractsUser.first({ useMasterKey: true });
|
||||
const parseExtUser = JSON.parse(JSON.stringify(extUser));
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const filename = sanitizeFileName(`${name}.pdf`);
|
||||
let adapter = {};
|
||||
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
|
||||
if (ActiveFileAdapter) {
|
||||
adapter =
|
||||
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
|
||||
}
|
||||
if (adapter?.id) {
|
||||
const filedata = Buffer.from(base64File, 'base64');
|
||||
// `uploadFileToS3` is used to save document in user's file storage
|
||||
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
|
||||
} else {
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
}
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const extUserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
};
|
||||
const object = new Parse.Object('contracts_Template');
|
||||
object.set('Name', name);
|
||||
if (note) {
|
||||
object.set('Note', note);
|
||||
}
|
||||
if (description) {
|
||||
object.set('Description', description);
|
||||
}
|
||||
if (SendinOrder) {
|
||||
object.set('SendinOrder', SendinOrder);
|
||||
}
|
||||
object.set('URL', fileUrl);
|
||||
object.set('CreatedBy', userPtr);
|
||||
object.set('ExtUserPtr', extUserPtr);
|
||||
object.set('IsEnableOTP', isEnableOTP);
|
||||
object.set('IsTourEnabled', isTourEnabled);
|
||||
const Public = isPublic !== undefined ? isPublic : false;
|
||||
if (isPublic !== undefined) {
|
||||
object.set('IsPublic', Public);
|
||||
}
|
||||
let contact = [];
|
||||
if (signers && signers.length > 0) {
|
||||
let parseSigners;
|
||||
if (base64File) {
|
||||
parseSigners = signers;
|
||||
} else {
|
||||
parseSigners = JSON.parse(signers);
|
||||
}
|
||||
let createContactUrl = protocol + '/v1/createcontact';
|
||||
|
||||
for (const [index, element] of parseSigners.entries()) {
|
||||
if (element?.name && element?.email) {
|
||||
const body = {
|
||||
name: element?.name,
|
||||
email: element?.email,
|
||||
phone: element?.phone || '',
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(createContactUrl, body, {
|
||||
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
|
||||
});
|
||||
// console.log('res ', res.data);
|
||||
const contactPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: res.data?.objectId,
|
||||
};
|
||||
const newObj = { ...element, contactPtr: contactPtr, index: index };
|
||||
contact.push(newObj);
|
||||
} catch (err) {
|
||||
// console.log('err ', err.response);
|
||||
if (err?.response?.data?.objectId) {
|
||||
const contactPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: err.response.data?.objectId,
|
||||
};
|
||||
const newObj = { ...element, contactPtr: contactPtr, index: index };
|
||||
contact.push(newObj);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const newObj = { ...element, contactPtr: {}, index: index };
|
||||
contact.push(newObj);
|
||||
}
|
||||
}
|
||||
const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId);
|
||||
if (updatedSigners && updatedSigners.length > 0) {
|
||||
object.set(
|
||||
'Signers',
|
||||
updatedSigners?.map(x => x.contactPtr)
|
||||
);
|
||||
}
|
||||
let updatePlaceholders = [];
|
||||
for (const signer of contact) {
|
||||
const placeHolder = [];
|
||||
if (signer.widgets?.length > 0) {
|
||||
for (const widget of signer.widgets) {
|
||||
const pageNumber = widget.page;
|
||||
const page = placeHolder.find(page => page.pageNumber === pageNumber);
|
||||
const options = formatWidgetOptions(widget.type, widget.options);
|
||||
const widgetData = {
|
||||
isStamp: widget.type === 'stamp' || widget.type === 'image',
|
||||
key: randomId(),
|
||||
isDrag: false,
|
||||
scale: 1,
|
||||
isMobile: false,
|
||||
zIndex: 1,
|
||||
type: widget.type === 'textbox' ? 'text input' : widget.type,
|
||||
options: options,
|
||||
Width: widget.w,
|
||||
Height: widget.h,
|
||||
xPosition: widget.x,
|
||||
yPosition: widget.y,
|
||||
};
|
||||
|
||||
if (page) {
|
||||
page.pos.push(widgetData);
|
||||
} else {
|
||||
placeHolder.push({ pageNumber, pos: [widgetData] });
|
||||
}
|
||||
}
|
||||
}
|
||||
updatePlaceholders.push({
|
||||
signerObjId: signer?.contactPtr?.objectId || '',
|
||||
signerPtr: signer?.contactPtr,
|
||||
Role: signer.role,
|
||||
Id: randomId(),
|
||||
blockColor: color[signer?.index],
|
||||
placeHolder,
|
||||
});
|
||||
}
|
||||
if (updatePlaceholders?.length > 0) {
|
||||
object.set('Placeholders', updatePlaceholders);
|
||||
}
|
||||
}
|
||||
if (folderId) {
|
||||
object.set('Folder', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Template',
|
||||
objectId: folderId,
|
||||
});
|
||||
}
|
||||
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
|
||||
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
|
||||
}
|
||||
const newACL = new Parse.ACL();
|
||||
newACL.setPublicReadAccess(false);
|
||||
newACL.setPublicWriteAccess(false);
|
||||
newACL.setReadAccess(userPtr.objectId, true);
|
||||
newACL.setWriteAccess(userPtr.objectId, true);
|
||||
object.setACL(newACL);
|
||||
const res = await object.save(null, { useMasterKey: true });
|
||||
if (request.posthog) {
|
||||
request.posthog?.capture({
|
||||
distinctId: parseUser.userId.email,
|
||||
event: 'api_draft_template',
|
||||
properties: { response_code: 200 },
|
||||
});
|
||||
}
|
||||
const JwtToken = jwt.sign(
|
||||
{ user_email: parseUser.userId.email, template_id: res.id },
|
||||
reqToken
|
||||
);
|
||||
|
||||
return response.json({ objectId: res.id, url: `${baseUrl}/drafttemplate/${JwtToken}` });
|
||||
} else {
|
||||
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
@@ -65,6 +66,7 @@ export default async function getDocument(request, response) {
|
||||
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) {
|
||||
@@ -103,6 +103,7 @@ export default async function getDocumentList(request, response) {
|
||||
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));
|
||||
@@ -64,6 +65,7 @@ export default async function getTemplate(request, response) {
|
||||
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) {
|
||||
@@ -91,6 +91,7 @@ export default async function getTemplatetList(request, response) {
|
||||
enableOTP: template?.IsEnableOTP || false,
|
||||
createdAt: template.createdAt,
|
||||
updatedAt: template.updatedAt,
|
||||
enableTour: template?.IsTourEnabled || false,
|
||||
}));
|
||||
|
||||
return response.json({ result: updateRes });
|
||||
|
||||
@@ -35,7 +35,7 @@ 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) {
|
||||
|
||||
@@ -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', 'enableOTP'];
|
||||
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');
|
||||
@@ -51,7 +52,9 @@ export default async function updateDocument(request, response) {
|
||||
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', 'enableOTP'];
|
||||
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');
|
||||
@@ -51,6 +52,9 @@ export default async function updateTemplate(request, response) {
|
||||
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) {
|
||||
|
||||
@@ -70,6 +70,20 @@ 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';
|
||||
import addPfxFile from './parsefunction/addPfxFile.js';
|
||||
import getPfxFile from './parsefunction/getPfxFile.js';
|
||||
import getTenant from './parsefunction/getTenant.js';
|
||||
import getSigners from './parsefunction/getSigners.js';
|
||||
import saveFile from './parsefunction/saveFile.js';
|
||||
import savecontact from './parsefunction/savecontact.js';
|
||||
import isUserInContactBook from './parsefunction/isUserInContactBook.js';
|
||||
import updateTourStatus from './parsefunction/updateTourStatus.js';
|
||||
import saveTemplate from './parsefunction/saveTemplate.js';
|
||||
import updateToPublicTemplate from './parsefunction/updateToPublicTemplate.js';
|
||||
import updateSignatureType from './parsefunction/updatesignaturetype.js';
|
||||
|
||||
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||
@@ -152,3 +166,17 @@ 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);
|
||||
Parse.Cloud.define('addpfx', addPfxFile);
|
||||
Parse.Cloud.define('getpfx', getPfxFile);
|
||||
Parse.Cloud.define('gettenant', getTenant);
|
||||
Parse.Cloud.define('getsigners', getSigners);
|
||||
Parse.Cloud.define('savetemplate', saveTemplate);
|
||||
Parse.Cloud.define('savefile', saveFile);
|
||||
Parse.Cloud.define('savecontact', savecontact);
|
||||
Parse.Cloud.define('isuserincontactbook', isUserInContactBook);
|
||||
Parse.Cloud.define('updatetourstatus', updateTourStatus);
|
||||
Parse.Cloud.define('updatetopublictemplate', updateToPublicTemplate);
|
||||
Parse.Cloud.define('updatesignaturetype', updateSignatureType);
|
||||
|
||||
@@ -1,33 +1,64 @@
|
||||
export default async function AllowedCredits(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
try {
|
||||
const extUser = new Parse.Query('contracts_Users');
|
||||
extUser.equalTo('UserId', {
|
||||
import { parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
async function checkCredits(userId) {
|
||||
const extUser = new Parse.Query('contracts_Users');
|
||||
extUser.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const resExtUser = await extUser.first({ useMasterKey: true });
|
||||
if (resExtUser) {
|
||||
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
subscription.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: request.user.id,
|
||||
className: 'partners_Tenant',
|
||||
objectId: _resExtUser.TenantId.objectId,
|
||||
});
|
||||
const resExtUser = await extUser.first({ useMasterKey: true });
|
||||
if (resExtUser) {
|
||||
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
subscription.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: _resExtUser.TenantId.objectId,
|
||||
});
|
||||
subscription.include('ExtUserPtr');
|
||||
const resSub = await subscription.first({ useMasterKey: true });
|
||||
if (resSub) {
|
||||
const _resSub = JSON.parse(JSON.stringify(resSub));
|
||||
const AllowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const AddonCredits = _resSub?.AddonCredits || 0;
|
||||
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
|
||||
subscription.include('ExtUserPtr');
|
||||
const resSub = await subscription.first({ useMasterKey: true });
|
||||
if (resSub) {
|
||||
const _resSub = JSON.parse(JSON.stringify(resSub));
|
||||
const AllowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const AddonCredits = _resSub?.AddonCredits || 0;
|
||||
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
}
|
||||
export default async function AllowedCredits(request) {
|
||||
const jwttoken = request?.headers?.jwttoken || '';
|
||||
|
||||
try {
|
||||
if (request?.user) {
|
||||
return await checkCredits(request.user.id);
|
||||
} else if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
return await checkCredits(userId);
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in allowedCredits', err);
|
||||
|
||||
@@ -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'));
|
||||
|
||||
@@ -1,30 +1,63 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
export default async function GetTemplate(request) {
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const templateId = request.params.templateId;
|
||||
const ispublic = request.params.ispublic;
|
||||
|
||||
const jwttoken = request.headers?.jwttoken;
|
||||
const sessiontoken = request.headers?.sessiontoken;
|
||||
try {
|
||||
if (!ispublic) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
const userId = userRes.data && userRes.data.objectId;
|
||||
if (templateId && userId) {
|
||||
let userEmail;
|
||||
if (jwttoken) {
|
||||
try {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
userEmail = decoded?.user_email;
|
||||
} else {
|
||||
return { error: 'Invalid token!' };
|
||||
}
|
||||
} else {
|
||||
return { error: 'Invalid token!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in jwt', err);
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else if (sessiontoken) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Session-Token': sessiontoken,
|
||||
},
|
||||
});
|
||||
userEmail = userRes.data && userRes.data.email;
|
||||
}
|
||||
if (templateId && userEmail) {
|
||||
try {
|
||||
let template = new Parse.Query('contracts_Template');
|
||||
template.equalTo('objectId', templateId);
|
||||
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.equalTo('Email', userEmail);
|
||||
extUserQuery.include('TeamIds');
|
||||
const extUser = await extUserQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
@@ -48,12 +81,16 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
template.include('Placeholders.signerPtr');
|
||||
}
|
||||
}
|
||||
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;
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.PfxFile;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
@@ -61,6 +98,8 @@ export default async function GetTemplate(request) {
|
||||
console.log('err', err);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else if (templateId && ispublic) {
|
||||
try {
|
||||
@@ -69,10 +108,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 });
|
||||
// console.log("res ", res)
|
||||
if (res) {
|
||||
return res;
|
||||
const templateRes = JSON.parse(JSON.stringify(res));
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapters;
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.PfxFile;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
@@ -85,7 +128,7 @@ export default async function GetTemplate(request) {
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err', err);
|
||||
if (err.code == 209) {
|
||||
if (err?.response?.data?.code === 209 || err.code == 209) {
|
||||
return { error: 'Invalid session token' };
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
|
||||
@@ -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,7 +61,7 @@ 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);
|
||||
@@ -63,6 +69,11 @@ const createDocumentFromTemplate = async (template, existContact, index) => {
|
||||
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 || '');
|
||||
if (template?.SignatureType?.length > 0) {
|
||||
object.set('SignatureType', template?.SignatureType);
|
||||
}
|
||||
let signers = template?.Signers || [];
|
||||
const signerobj = {
|
||||
__type: 'Pointer',
|
||||
@@ -97,7 +108,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));
|
||||
@@ -246,6 +257,7 @@ const deductcount = async _resSub => {
|
||||
export default async function PublicUserLinkContactToDoc(req) {
|
||||
const email = req.params.email;
|
||||
const templateid = req.params.templateid;
|
||||
const signatureType = req.params.signatureType;
|
||||
const name = req.params.name;
|
||||
const phone = req.params.phone;
|
||||
const role = req.params.role;
|
||||
@@ -254,6 +266,7 @@ 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) {
|
||||
@@ -292,8 +305,11 @@ export default async function PublicUserLinkContactToDoc(req) {
|
||||
const existContact = await contactCls.first({ useMasterKey: true });
|
||||
if (existContact) {
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
const _template_json = template_json;
|
||||
_template_json.SignatureType =
|
||||
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(template_json, existContact, index);
|
||||
const docRes = await createDocumentFromTemplate(_template_json, existContact, index);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
@@ -315,12 +331,15 @@ export default async function PublicUserLinkContactToDoc(req) {
|
||||
Email: email,
|
||||
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));
|
||||
const _template_json = template_json;
|
||||
_template_json.SignatureType =
|
||||
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
|
||||
// if user present on platform create contact on the basis of extended user details
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
const docRes = await createDocumentFromTemplate(template_json, contactRes, index);
|
||||
const docRes = await createDocumentFromTemplate(_template_json, contactRes, index);
|
||||
if (docRes) {
|
||||
await deductcount(_resSub);
|
||||
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
|
||||
@@ -342,14 +361,17 @@ export default async function PublicUserLinkContactToDoc(req) {
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr.TenantId,
|
||||
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
const _template_json = template_json;
|
||||
_template_json.SignatureType =
|
||||
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(
|
||||
template_json,
|
||||
_template_json,
|
||||
contactRes,
|
||||
index
|
||||
);
|
||||
@@ -379,14 +401,17 @@ export default async function PublicUserLinkContactToDoc(req) {
|
||||
Email: email,
|
||||
Phone: phone,
|
||||
CreatedBy: _tempRes.CreatedBy,
|
||||
TenantId: _tempRes.ExtUserPtr.TenantId,
|
||||
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
|
||||
};
|
||||
const template_json = JSON.parse(JSON.stringify(tempRes));
|
||||
const _template_json = template_json;
|
||||
_template_json.SignatureType =
|
||||
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
|
||||
// Create new contract on the basis provided contact details by user and userId from _User class
|
||||
const contactRes = await saveRoleContact(contact);
|
||||
//update contact in placeholder, signers and update ACl in provide document
|
||||
const docRes = await createDocumentFromTemplate(
|
||||
template_json,
|
||||
_template_json,
|
||||
contactRes,
|
||||
index
|
||||
);
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// `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;
|
||||
}
|
||||
|
||||
// `getExtUser` get ext user details
|
||||
async function getExtUser(request) {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
extUserCls.include('TenantId');
|
||||
return await extUserCls.first({ useMasterKey: true });
|
||||
}
|
||||
|
||||
// `saveTenant` save file adapter details in tenant class
|
||||
async function saveTenant(tenantId, fileAdapters, activeAdapter) {
|
||||
const tenantCls = new Parse.Object('partners_Tenant');
|
||||
tenantCls.id = tenantId;
|
||||
if (fileAdapters?.length > 0) {
|
||||
tenantCls.set('FileAdapters', fileAdapters);
|
||||
}
|
||||
if (activeAdapter) {
|
||||
tenantCls.set('ActiveFileAdapter', activeAdapter);
|
||||
} else {
|
||||
tenantCls.unset('ActiveFileAdapter');
|
||||
}
|
||||
return await tenantCls.save(null, { useMasterKey: true });
|
||||
}
|
||||
|
||||
// `updateTenantSchema` is used add FileAdapter in protected fields
|
||||
async function updateTenantSchema() {
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
const currentSchema = await tenantSchema.get();
|
||||
let clp = currentSchema.classLevelPermissions;
|
||||
// Public permission ("*")
|
||||
const role = '*';
|
||||
if (!clp.protectedFields || Object.keys(clp.protectedFields).length === 0) {
|
||||
// Initialize protectedFields if it doesn't exist
|
||||
clp.protectedFields = { [role]: [] };
|
||||
}
|
||||
// save FileAdapters field is in protectedFields if not exists
|
||||
if (!clp.protectedFields[role]?.includes('FileAdapters')) {
|
||||
clp.protectedFields[role].push('FileAdapters');
|
||||
// Update the class schema with the modified CLP
|
||||
tenantSchema.setCLP(clp);
|
||||
await tenantSchema.update();
|
||||
}
|
||||
}
|
||||
|
||||
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 extUser = await getExtUser(request);
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
const tenantId = extUser?.get('TenantId')?.id;
|
||||
// assign existing file adapters or empty array
|
||||
let fileAdapters = _extUser?.TenantId?.FileAdapters || [];
|
||||
const uniqueId = generateId(10);
|
||||
let id = extUser?.get('TenantId')?.id + '_' + uniqueId;
|
||||
const index = fileAdapters?.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 };
|
||||
fileAdapters[index] = { ...fileAdapters[index], ...adapterConfig };
|
||||
id = fileAdapters[index].id;
|
||||
}
|
||||
} else {
|
||||
if (bucketName && region && endpoint && baseUrl) {
|
||||
// If the object with the given fileAdapterName doesn't exist, add a new one
|
||||
fileAdapters.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.');
|
||||
}
|
||||
}
|
||||
const updateTenant = await saveTenant(tenantId, fileAdapters, id);
|
||||
await updateTenantSchema();
|
||||
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 extUser = await getExtUser(request);
|
||||
if (extUser) {
|
||||
const tenantId = extUser?.get('TenantId')?.id;
|
||||
const updateTenant = await saveTenant(tenantId);
|
||||
await updateTenantSchema();
|
||||
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,58 @@
|
||||
// `updateTenantSchema` is used add FileAdapter in protected fields
|
||||
async function updateTenantSchema() {
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
const currentSchema = await tenantSchema.get();
|
||||
let clp = currentSchema.classLevelPermissions;
|
||||
// Public permission ("*")
|
||||
const role = '*';
|
||||
if (!clp.protectedFields || Object.keys(clp.protectedFields).length === 0) {
|
||||
// Initialize protectedFields if it doesn't exist
|
||||
clp.protectedFields = { [role]: [] };
|
||||
}
|
||||
// save PfxFile field is in protectedFields if not exists
|
||||
if (!clp.protectedFields[role]?.includes('PfxFile')) {
|
||||
clp.protectedFields[role].push('PfxFile');
|
||||
// Update the class schema with the modified CLP
|
||||
tenantSchema.setCLP(clp);
|
||||
await tenantSchema.update();
|
||||
}
|
||||
}
|
||||
|
||||
export default async function addPfxFile(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const pfxBase64 = request.params.pfxBase64;
|
||||
const title = request.params.title;
|
||||
const password = request.params.password;
|
||||
const provider = request.params.provider;
|
||||
if (provider === 'opensign' || (pfxBase64 && password)) {
|
||||
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;
|
||||
if (provider === 'opensign') {
|
||||
tenantCls.unset('PfxFile');
|
||||
} else {
|
||||
tenantCls.set('PfxFile', { title: title, password: password, base64: pfxBase64 });
|
||||
}
|
||||
const updateTenant = await tenantCls.save(null, { useMasterKey: true });
|
||||
await updateTenantSchema();
|
||||
return { 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.');
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ export default async function callWebhook(request) {
|
||||
const appId = process.env.APP_ID;
|
||||
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;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
|
||||
import { cloudServerUrl, parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const licenseKey = process.env.LICENSE_KEY;
|
||||
@@ -30,7 +30,8 @@ async function deductcount(docsCount, extUserId, subscription) {
|
||||
console.log('Err in deduct in quick send', err);
|
||||
}
|
||||
}
|
||||
async function sendMail(document, sessionToken) {
|
||||
async function sendMail(document) {
|
||||
//sessionToken
|
||||
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||
|
||||
// console.log("pdfDetails", pdfDetails);
|
||||
@@ -57,7 +58,7 @@ async function sendMail(document, sessionToken) {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
sessionToken: sessionToken,
|
||||
// sessionToken: sessionToken,
|
||||
};
|
||||
const objectId = signerMail[i]?.signerObjId;
|
||||
const hostUrl = baseUrl.origin;
|
||||
@@ -112,13 +113,232 @@ async function sendMail(document, sessionToken) {
|
||||
}
|
||||
}
|
||||
}
|
||||
async function callwebhookevent(document) {
|
||||
const Signers = document.Signers;
|
||||
const allSigner = document?.Placeholders?.map(item => {
|
||||
if (item.signerObjId) {
|
||||
const signer = Signers?.find(e => item?.signerPtr?.objectId === e?.objectId);
|
||||
if (signer) {
|
||||
return {
|
||||
role: item?.Role || '',
|
||||
name: signer?.Name || '',
|
||||
email: signer?.Email || '',
|
||||
phone: signer?.Phone || '',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return { role: item?.Role || '', name: '', email: item?.email || '', phone: '' };
|
||||
}
|
||||
});
|
||||
const params = {
|
||||
event: 'created',
|
||||
body: {
|
||||
objectId: document?.objectId,
|
||||
file: document?.SignedUrl || document?.URL,
|
||||
name: document?.Name,
|
||||
note: document?.Note || '',
|
||||
description: document?.Description || '',
|
||||
signers: allSigner,
|
||||
createdBy: document?.ExtUserPtr.Email,
|
||||
createdAt: document?.createdAt,
|
||||
},
|
||||
};
|
||||
try {
|
||||
await axios
|
||||
.post(document?.ExtUserPtr?.Webhook, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: document.CreatedBy.objectId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err?.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
webhook.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: document.CreatedBy.objectId,
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err?.message);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('Err ', err?.message);
|
||||
}
|
||||
}
|
||||
async function batchQuery(userId, Documents, Ip, parseConfig) {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const resExt = await extCls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
try {
|
||||
const requests = Documents.map(x => {
|
||||
const Signers = x.Signers;
|
||||
const allSigner = x?.Placeholders?.map(
|
||||
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
|
||||
).filter(signer => Object.keys(signer).length > 0);
|
||||
const date = new Date();
|
||||
const isoDate = date.toISOString();
|
||||
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
if (allSigner && allSigner.length > 0) {
|
||||
allSigner.forEach(x => {
|
||||
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
Acl = { ...Acl, ...obj };
|
||||
});
|
||||
}
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/app/classes/contracts_Document',
|
||||
body: {
|
||||
Name: x.Name,
|
||||
URL: x.URL,
|
||||
Note: x.Note,
|
||||
Description: x.Description,
|
||||
CreatedBy: x.CreatedBy,
|
||||
SendinOrder: x.SendinOrder || true,
|
||||
ExtUserPtr: {
|
||||
__type: 'Pointer',
|
||||
className: x.ExtUserPtr.className,
|
||||
objectId: x.ExtUserPtr?.objectId,
|
||||
},
|
||||
Placeholders: x.Placeholders.map(y =>
|
||||
y?.signerPtr?.objectId
|
||||
? {
|
||||
...y,
|
||||
signerPtr: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.signerPtr.objectId,
|
||||
},
|
||||
signerObjId: y.signerObjId,
|
||||
}
|
||||
: { ...y, signerPtr: {}, signerObjId: '' }
|
||||
),
|
||||
SignedUrl: x.URL || x.SignedUrl,
|
||||
SentToOthers: true,
|
||||
Signers: allSigner?.map(y => ({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.objectId,
|
||||
})),
|
||||
ACL: Acl,
|
||||
SentToOthers: true,
|
||||
RemindOnceInEvery: x.RemindOnceInEvery || 5,
|
||||
AutomaticReminders: x.AutomaticReminders || false,
|
||||
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
IsTourEnabled: x?.IsTourEnabled || false,
|
||||
FileAdapterId: x?.FileAdapterId || '',
|
||||
...(x?.SignatureType ? { SignatureType: x?.SignatureType } : {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
// console.log('requests ', requests);
|
||||
if (licenseKey) {
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
subscription.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: _resExt.TenantId.objectId,
|
||||
});
|
||||
subscription.include('ExtUserPtr');
|
||||
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
|
||||
const resSub = await subscription.first({ useMasterKey: true });
|
||||
if (resSub) {
|
||||
const _resSub = JSON.parse(JSON.stringify(resSub));
|
||||
const allowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const addonCredits = _resSub?.AddonCredits || 0;
|
||||
const totalcredits = allowedCredits + addonCredits;
|
||||
if (requests?.length <= totalcredits) {
|
||||
const response = await axios.post('batch', { requests: requests }, parseConfig);
|
||||
// Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const updateDocuments = Documents.map((x, i) => ({
|
||||
...x,
|
||||
objectId: response.data[i]?.success?.objectId,
|
||||
createdAt: response.data[i]?.success?.createdAt,
|
||||
}));
|
||||
deductcount(response.data.length, resExt.id, _resSub);
|
||||
for (let i = 0; i < updateDocuments.length; i++) {
|
||||
sendMail(updateDocuments[i], ''); //sessionToken
|
||||
}
|
||||
callwebhookevent(Documents[0]);
|
||||
return 'success';
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(429, 'Quota reached, Please buy credits and try again later.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
'Please purchase or renew your subscription.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (requests?.length > 0) {
|
||||
const newrequests = [requests?.[0]];
|
||||
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
|
||||
// Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const document = Documents?.[0];
|
||||
const updateDocuments = {
|
||||
...document,
|
||||
objectId: response.data[0]?.success?.objectId,
|
||||
createdAt: response.data[0]?.success?.createdAt,
|
||||
};
|
||||
deductcount(response.data.length, resExt.id);
|
||||
sendMail(updateDocuments); //sessionToken
|
||||
callwebhookevent(Documents[0]);
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
|
||||
const msg =
|
||||
error?.response?.data?.error ||
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
'Something went wrong.';
|
||||
console.log('Error performing batch query:', code, msg);
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
}
|
||||
export default async function createBatchDocs(request) {
|
||||
const strDocuments = request.params.Documents;
|
||||
const sessionToken = request.headers['sessiontoken'];
|
||||
const sessionToken = request.headers?.sessiontoken;
|
||||
const jwttoken = request.headers?.jwttoken;
|
||||
const Documents = JSON.parse(strDocuments);
|
||||
const Ip = request?.headers?.['x-real-ip'] || '';
|
||||
const parseConfig = {
|
||||
baseURL: serverUrl, //localStorage.getItem('baseUrl'),
|
||||
baseURL: serverUrl,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': sessionToken,
|
||||
@@ -126,165 +346,38 @@ export default async function createBatchDocs(request) {
|
||||
},
|
||||
};
|
||||
try {
|
||||
const user = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
if (user.data) {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user.data.objectId,
|
||||
});
|
||||
const resExt = await extCls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
try {
|
||||
const requests = Documents.map(x => {
|
||||
const Signers = x.Signers;
|
||||
const allSigner = x?.Placeholders?.map(
|
||||
item =>
|
||||
Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
|
||||
).filter(signer => Object.keys(signer).length > 0);
|
||||
const date = new Date();
|
||||
const isoDate = date.toISOString();
|
||||
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
if (allSigner && allSigner.length > 0) {
|
||||
allSigner.forEach(x => {
|
||||
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
Acl = { ...Acl, ...obj };
|
||||
});
|
||||
}
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/app/classes/contracts_Document',
|
||||
body: {
|
||||
Name: x.Name,
|
||||
URL: x.URL,
|
||||
Note: x.Note,
|
||||
Description: x.Description,
|
||||
CreatedBy: x.CreatedBy,
|
||||
SendinOrder: x.SendinOrder || true,
|
||||
ExtUserPtr: {
|
||||
__type: 'Pointer',
|
||||
className: x.ExtUserPtr.className,
|
||||
objectId: x.ExtUserPtr.objectId,
|
||||
},
|
||||
Placeholders: x.Placeholders.map(y =>
|
||||
y?.signerPtr?.objectId
|
||||
? {
|
||||
...y,
|
||||
signerPtr: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.signerPtr.objectId,
|
||||
},
|
||||
signerObjId: y.signerObjId,
|
||||
}
|
||||
: { ...y, signerPtr: {}, signerObjId: '' }
|
||||
),
|
||||
SignedUrl: x.URL || x.SignedUrl,
|
||||
SentToOthers: true,
|
||||
Signers: allSigner?.map(y => ({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.objectId,
|
||||
})),
|
||||
ACL: Acl,
|
||||
SentToOthers: true,
|
||||
RemindOnceInEvery: x.RemindOnceInEvery || 5,
|
||||
AutomaticReminders: x.AutomaticReminders || false,
|
||||
TimeToCompleteDays: x.TimeToCompleteDays || 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
},
|
||||
};
|
||||
});
|
||||
// console.log('requests ', requests);
|
||||
if (licenseKey) {
|
||||
const subscription = new Parse.Query('contracts_Subscriptions');
|
||||
subscription.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: _resExt.TenantId.objectId,
|
||||
});
|
||||
subscription.include('ExtUserPtr');
|
||||
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
|
||||
const resSub = await subscription.first({ useMasterKey: true });
|
||||
if (resSub) {
|
||||
const _resSub = JSON.parse(JSON.stringify(resSub));
|
||||
const allowedCredits = _resSub?.AllowedCredits || 0;
|
||||
const addonCredits = _resSub?.AddonCredits || 0;
|
||||
const totalcredits = allowedCredits + addonCredits;
|
||||
if (requests?.length <= totalcredits) {
|
||||
const response = await axios.post('batch', { requests: requests }, parseConfig);
|
||||
// // Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const updateDocuments = Documents.map((x, i) => ({
|
||||
...x,
|
||||
objectId: response.data[i]?.success?.objectId,
|
||||
createdAt: response.data[i]?.success?.createdAt,
|
||||
}));
|
||||
deductcount(response.data.length, resExt.id, _resSub);
|
||||
for (let i = 0; i < updateDocuments.length; i++) {
|
||||
sendMail(updateDocuments[i], sessionToken);
|
||||
}
|
||||
return 'success';
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(
|
||||
429,
|
||||
'Quota reached, Please buy credits and try again later.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_QUERY,
|
||||
'Please purchase or renew your subscription.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (requests?.length > 0) {
|
||||
const newrequests = [requests?.[0]];
|
||||
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
|
||||
// Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const document = Documents?.[0];
|
||||
const updateDocuments = {
|
||||
...document,
|
||||
objectId: response.data[0]?.success?.objectId,
|
||||
createdAt: response.data[0]?.success?.createdAt,
|
||||
};
|
||||
deductcount(response.data.length, resExt.id);
|
||||
sendMail(updateDocuments, sessionToken);
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
|
||||
const msg =
|
||||
error?.response?.data?.error ||
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
'Something went wrong.';
|
||||
console.log('Error performing batch query:', code, msg);
|
||||
throw new Parse.Error(code, msg);
|
||||
if (request?.user) {
|
||||
return await batchQuery(request.user.id, Documents, Ip, parseConfig);
|
||||
} else if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
return await batchQuery(userId, Documents, Ip, parseConfig);
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in createbatchdoc', err);
|
||||
const code = err?.code || 400;
|
||||
const msg = err?.message || 'Something went wrong.';
|
||||
new Parse.Error(code, msg);
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export default async function declinedocument(request) {
|
||||
|
||||
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;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
export default async function getDocument(request) {
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const docId = request.params.docId;
|
||||
|
||||
const jwttoken = request?.headers?.jwttoken || '';
|
||||
const sessiontoken = request?.headers?.sessiontoken || '';
|
||||
try {
|
||||
if (docId) {
|
||||
try {
|
||||
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');
|
||||
@@ -20,21 +23,24 @@ export default async function getDocument(request) {
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const IsEnableOTP = res?.get('IsEnableOTP') || false;
|
||||
const document = JSON.parse(JSON.stringify(res));
|
||||
delete document.ExtUserPtr.TenantId.FileAdapters;
|
||||
delete document?.ExtUserPtr?.TenantId?.PfxFile;
|
||||
if (!IsEnableOTP) {
|
||||
return res;
|
||||
return document;
|
||||
} else {
|
||||
if (request?.headers?.['sessiontoken']) {
|
||||
if (sessiontoken) {
|
||||
try {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
'X-Parse-Session-Token': sessiontoken,
|
||||
},
|
||||
});
|
||||
const userId = userRes.data && userRes.data?.objectId;
|
||||
const acl = res.getACL();
|
||||
if (userId && acl && acl.getReadAccess(userId)) {
|
||||
return res;
|
||||
return document;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
@@ -42,6 +48,37 @@ export default async function getDocument(request) {
|
||||
console.log('err user in not authenticated', err);
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else if (jwttoken) {
|
||||
try {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
const acl = res.getACL();
|
||||
if (userId && acl && acl.getReadAccess(userId)) {
|
||||
return document;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid token!' };
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in jwt', err);
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
export default async function getPfxFile(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 title = _extUser?.TenantId?.PfxFile?.title || '';
|
||||
const pfxBase64 = _extUser?.TenantId?.PfxFile?.base64 || '';
|
||||
const password = _extUser?.TenantId?.PfxFile?.password || '';
|
||||
return { title: title, password: password, base64: pfxBase64 };
|
||||
} 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
|
||||
});
|
||||
@@ -29,26 +29,51 @@ 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 (docId) {
|
||||
const fileAdapterId = request.params.fileAdapterId || '';
|
||||
if (docId || templateId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
query.notEqualTo('IsEnableOTP', true);
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
query.include('Placeholders');
|
||||
query.include('DeclineBy');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
export default async function getSigners(request) {
|
||||
const jwttoken = request.headers.jwttoken || '';
|
||||
const search = request.params.search || '';
|
||||
const searchEmail = request.params.searchEmail || '';
|
||||
try {
|
||||
if (request.user) {
|
||||
const contactbook = new Parse.Query('contracts_Contactbook');
|
||||
contactbook.equalTo('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: request?.user?.id,
|
||||
});
|
||||
if (search) {
|
||||
contactbook.matches('Name', new RegExp(search, 'i'));
|
||||
} else if (searchEmail) {
|
||||
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
|
||||
}
|
||||
contactbook.notEqualTo('IsDeleted', true);
|
||||
const contactRes = await contactbook.find({ sessionToken: request.user.getSessionToken() });
|
||||
const _contactRes = JSON.parse(JSON.stringify(contactRes));
|
||||
return _contactRes;
|
||||
} else if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
const contactbook = new Parse.Query('contracts_Contactbook');
|
||||
contactbook.equalTo('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
if (search) {
|
||||
contactbook.matches('Name', new RegExp(search, 'i'));
|
||||
} else if (searchEmail) {
|
||||
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
|
||||
}
|
||||
contactbook.notEqualTo('IsDeleted', true);
|
||||
const contactRes = await contactbook.find({ useMasterKey: true });
|
||||
const _contactRes = JSON.parse(JSON.stringify(contactRes));
|
||||
return _contactRes;
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in get signers', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,57 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
export default async function getSubscription(request) {
|
||||
const extUserId = request.params.extUserId || '';
|
||||
const contactId = request.params.contactId || '';
|
||||
const ispublic = request.params.ispublic || false;
|
||||
const jwttoken = request.headers?.jwttoken || '';
|
||||
|
||||
if (extUserId) {
|
||||
if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const verifyToken = jwttoken;
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('Email', decoded?.user_email);
|
||||
const exUser = await extCls.first({ useMasterKey: true });
|
||||
if (exUser) {
|
||||
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subscriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: exUser.get('TenantId').id,
|
||||
});
|
||||
subscriptionCls.descending('createdAt');
|
||||
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
|
||||
if (subcripitions) {
|
||||
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
|
||||
return { status: 'success', result: _subcripitions };
|
||||
} else {
|
||||
return { status: 'success', result: {} };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'User not found!' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid token!' };
|
||||
}
|
||||
}
|
||||
} else if (extUserId) {
|
||||
try {
|
||||
let userId;
|
||||
//`ispublic` is used in public profile to get subscription details
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
async function getTenantByUserId(userId, contactId) {
|
||||
try {
|
||||
if (contactId) {
|
||||
const contactquery = new Parse.Query('contracts_Contactbook');
|
||||
contactquery.equalTo('objectId', contactId);
|
||||
const contactuser = await contactquery.first({ useMasterKey: true });
|
||||
if (contactuser) {
|
||||
const tenantId = contactuser?.get('TenantId')?.id;
|
||||
if (tenantId) {
|
||||
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
|
||||
tenantCreditsQuery.equalTo('objectId', tenantId);
|
||||
tenantCreditsQuery.exclude('FileAdapters,PfxFile,ContactNumber');
|
||||
const res = await tenantCreditsQuery.first({ useMasterKey: true });
|
||||
return res;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const extuser = await query.first({ useMasterKey: true });
|
||||
if (extuser) {
|
||||
const user = extuser?.get('CreatedBy')?.id || userId;
|
||||
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
|
||||
tenantCreditsQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user,
|
||||
});
|
||||
tenantCreditsQuery.exclude('FileAdapters,PfxFile');
|
||||
const res = await tenantCreditsQuery.first({ useMasterKey: true });
|
||||
return res;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in getTenant ', err);
|
||||
return 'user does not exist!';
|
||||
}
|
||||
}
|
||||
export default async function getTenant(request) {
|
||||
const jwttoken = request.headers.jwttoken || '';
|
||||
const userId = request.params.userId || '';
|
||||
const contactId = request.params.contactId || '';
|
||||
if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const verifyToken = jwttoken;
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const apiUserId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: apiUserId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
return await getTenantByUserId(apiUserId, contactId);
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid token!' };
|
||||
}
|
||||
}
|
||||
} else if (userId || contactId) {
|
||||
return await getTenantByUserId(userId, contactId);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
import { parseJwt } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
async function getUserDetails(request) {
|
||||
const reqEmail = request.params.email;
|
||||
const jwttoken = request?.headers?.jwttoken || '';
|
||||
if (reqEmail || request.user) {
|
||||
try {
|
||||
const userId = request.params.userId;
|
||||
@@ -14,7 +18,9 @@ async function getUserDetails(request) {
|
||||
userQuery.include('UserId');
|
||||
userQuery.include('CreatedBy');
|
||||
userQuery.exclude('CreatedBy.authData');
|
||||
userQuery.exclude('TenantId.FileAdapters');
|
||||
userQuery.exclude('google_refresh_token');
|
||||
userQuery.exclude('TenantId.PfxFile');
|
||||
if (userId) {
|
||||
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
}
|
||||
@@ -34,6 +40,48 @@ async function getUserDetails(request) {
|
||||
const msg = err?.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else if (jwttoken) {
|
||||
const jwtDecode = parseJwt(jwttoken);
|
||||
if (jwtDecode?.user_email) {
|
||||
const userCls = new Parse.Query(Parse.User);
|
||||
userCls.equalTo('email', jwtDecode?.user_email);
|
||||
const userRes = await userCls.first({ useMasterKey: true });
|
||||
const userId = userRes?.id;
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('userId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const appRes = await tokenQuery.first({ useMasterKey: true });
|
||||
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
|
||||
if (decoded?.user_email) {
|
||||
try {
|
||||
const userQuery = new Parse.Query('contracts_Users');
|
||||
userQuery.equalTo('Email', decoded?.user_email);
|
||||
userQuery.include('TenantId');
|
||||
userQuery.include('UserId');
|
||||
userQuery.include('CreatedBy');
|
||||
userQuery.exclude('CreatedBy.authData');
|
||||
userQuery.exclude('TenantId.FileAdapters');
|
||||
userQuery.exclude('google_refresh_token');
|
||||
userQuery.exclude('TenantId.PfxFile');
|
||||
const res = await userQuery.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
return res;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err ', err);
|
||||
const code = err?.code || 400;
|
||||
const msg = err?.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid token!' };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user