mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
Generated
+79
-119
@@ -8,9 +8,9 @@
|
||||
"name": "open_sign",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.1",
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@radix-ui/themes": "^2.0.3",
|
||||
"@react-pdf/renderer": "^3.4.0",
|
||||
"@react-pdf/renderer": "^3.4.4",
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"file-saver": "^2.0.5",
|
||||
@@ -36,7 +36,7 @@
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-rnd": "^10.4.10",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
@@ -44,7 +44,7 @@
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.3",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.2",
|
||||
"reactour": "^1.19.3",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
@@ -53,14 +53,14 @@
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs2": "^7.24.1",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"@babel/runtime-corejs2": "^7.24.5",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"commitizen": "^4.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.37",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.3.1",
|
||||
"tailwindcss": "^3.4.1"
|
||||
@@ -1994,9 +1994,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime-corejs2": {
|
||||
"version": "7.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.24.1.tgz",
|
||||
"integrity": "sha512-De0q0utpUPiXnc7+B7Ku86mJ0eDItC/v3uFa/lQkq63XnHyZiytDHeCIvechlnVwwpU2ChjGF7c3I+mBrTudwg==",
|
||||
"version": "7.24.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.24.5.tgz",
|
||||
"integrity": "sha512-cC9jiO6s/IN+xwCHYy1AGrcFJ4bwgIwb8HX1KaoEpRsznLlO4x9eBP6AX7RIeMSWlQqEj2WHox637OS8cDq6Ew==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"core-js": "^2.6.12",
|
||||
@@ -2989,9 +2989,9 @@
|
||||
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
|
||||
},
|
||||
"node_modules/@formkit/auto-animate": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.1.tgz",
|
||||
"integrity": "sha512-0/Z2cuNXWVVIG/l0SpcHAWFhGdvLJ8DRvEfRWvmojtmRWfEy+LWNwgDazbZqY0qQYtkHcoEK3jBLkhiZaB/4Ig=="
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.2.tgz",
|
||||
"integrity": "sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ=="
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.14",
|
||||
@@ -5381,41 +5381,41 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/font": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-2.4.4.tgz",
|
||||
"integrity": "sha512-yjK5eSY+LcbxS0m+sOYln8GdgIbUgti4xjwf14kx8OSsOMJQJyHFALHMh2cLcKJR9yZeqVDo1FwCsY6gw1yCkg==",
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-2.5.1.tgz",
|
||||
"integrity": "sha512-Hyb2zBb92Glc3lvhmJfy4dO2Mj29KB26Uk12Ua9EhKAdiuCTLBqgP8Oe1cGwrvDI7xA4OOcwvBMdYh0vhOUHzA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/types": "^2.4.1",
|
||||
"@react-pdf/types": "^2.5.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"fontkit": "^2.0.2",
|
||||
"is-url": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/image": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-2.3.5.tgz",
|
||||
"integrity": "sha512-YK8oeD4cYTbX2KwdsY5DEDpvsKQoGbf3kwlxAQwYm8wrEol8V/5oCl8H8ZqdZIYQhx2XPp63cocQfMCZGwf+4w==",
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-2.3.6.tgz",
|
||||
"integrity": "sha512-7iZDYZrZlJqNzS6huNl2XdMcLFUo68e6mOdzQeJ63d5eApdthhSHBnkGzHfLhH5t8DCpZNtClmklzuLL63ADfw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/png-js": "^2.3.1",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"jay-peg": "^1.0.1"
|
||||
"jay-peg": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/layout": {
|
||||
"version": "3.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-3.11.3.tgz",
|
||||
"integrity": "sha512-SwfIWmZl3yZDgb4Qa9ebjAZTd/FyI5E2curSfra6SuAkrsEF037cmVfxCTgYCkPwpVMv/omOENPI+WjhOKPofA==",
|
||||
"version": "3.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-3.12.1.tgz",
|
||||
"integrity": "sha512-BxSeykDxvADlpe4OGtQ7NH46QXq3uImAYsTHOPLCwbXMniQ1O3uCBx7H+HthxkCNshgYVPp9qS3KyvQv/oIZwg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/fns": "2.2.1",
|
||||
"@react-pdf/image": "^2.3.5",
|
||||
"@react-pdf/pdfkit": "^3.1.7",
|
||||
"@react-pdf/image": "^2.3.6",
|
||||
"@react-pdf/pdfkit": "^3.1.10",
|
||||
"@react-pdf/primitives": "^3.1.1",
|
||||
"@react-pdf/stylesheet": "^4.2.4",
|
||||
"@react-pdf/stylesheet": "^4.2.5",
|
||||
"@react-pdf/textkit": "^4.4.1",
|
||||
"@react-pdf/types": "^2.4.1",
|
||||
"@react-pdf/types": "^2.5.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"emoji-regex": "^10.3.0",
|
||||
"queue": "^6.0.1",
|
||||
@@ -5428,16 +5428,16 @@
|
||||
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw=="
|
||||
},
|
||||
"node_modules/@react-pdf/pdfkit": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-3.1.7.tgz",
|
||||
"integrity": "sha512-iXMmATqW5Aycpzm8nNsZOHV/DX2lpd8l5VkaTy1HjzHgQ3MHoV45BHwRph53soEqoPo7rUfpjuSi7X3hngPWGA==",
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-3.1.10.tgz",
|
||||
"integrity": "sha512-P/qPBtCFo2HDJD0i6NfbmoBRrsOVO8CIogYsefwG4fklTo50zNgnMM5U1WLckTuX8Qt1ThiQuokmTG5arheblA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/png-js": "^2.3.1",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"fontkit": "^2.0.2",
|
||||
"jay-peg": "^1.0.1",
|
||||
"jay-peg": "^1.0.2",
|
||||
"vite-compatible-readable-stream": "^3.6.1"
|
||||
}
|
||||
},
|
||||
@@ -5455,15 +5455,15 @@
|
||||
"integrity": "sha512-miwjxLwTnO3IjoqkTVeTI+9CdyDggwekmSLhVCw+a/7FoQc+gF3J2dSKwsHvAcVFM0gvU8mzCeTofgw0zPDq0w=="
|
||||
},
|
||||
"node_modules/@react-pdf/render": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-3.4.3.tgz",
|
||||
"integrity": "sha512-9LL059vfwrK1gA0uIA4utpQ/pUH9EW/yia4bb7pCoARs8IlupY5UP265jgax15ua0p+MdUwShZzQ9rilu7kGsw==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-3.4.4.tgz",
|
||||
"integrity": "sha512-CfGxWmVgrY3JgmB1iMnz2W6Ck+8pisZeFt8vGlxP+JfT+0onr208pQvGSV5KwA9LGhAdABxqc/+y17V3vtKdFA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/fns": "2.2.1",
|
||||
"@react-pdf/primitives": "^3.1.1",
|
||||
"@react-pdf/textkit": "^4.4.1",
|
||||
"@react-pdf/types": "^2.4.1",
|
||||
"@react-pdf/types": "^2.5.0",
|
||||
"abs-svg-path": "^0.1.1",
|
||||
"color-string": "^1.9.1",
|
||||
"normalize-svg-path": "^1.1.0",
|
||||
@@ -5472,17 +5472,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/renderer": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-3.4.0.tgz",
|
||||
"integrity": "sha512-8i/B8xJZ4AwINM/RRBnawBXfNACaehQT/bM19TOTUjHoddYaRKM1g1+qOZx58yfxfr5TFRs4fORxEPU1mGmR1A==",
|
||||
"version": "3.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-3.4.4.tgz",
|
||||
"integrity": "sha512-j1TWMHHXDeHdoQE3xjhBh0MZ2rn7wHIlP/uglr/EJZXqnPbfg6bfLzRJCM6bs+XJV3d8+zLQjHf6sF/fWcBDfg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/font": "^2.4.4",
|
||||
"@react-pdf/layout": "^3.11.3",
|
||||
"@react-pdf/pdfkit": "^3.1.7",
|
||||
"@react-pdf/font": "^2.5.1",
|
||||
"@react-pdf/layout": "^3.12.1",
|
||||
"@react-pdf/pdfkit": "^3.1.10",
|
||||
"@react-pdf/primitives": "^3.1.1",
|
||||
"@react-pdf/render": "^3.4.3",
|
||||
"@react-pdf/types": "^2.4.1",
|
||||
"@react-pdf/render": "^3.4.4",
|
||||
"@react-pdf/types": "^2.5.0",
|
||||
"events": "^3.3.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.2",
|
||||
@@ -5503,13 +5503,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/stylesheet": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-4.2.4.tgz",
|
||||
"integrity": "sha512-CgRfDzeMtnV0GL7zSn381NubmgwqKhFKcK1YrWX3azl/KWVh52jjFd3HWi6dvcETNT862mjWz5MnExe4WOBJXA==",
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-4.2.5.tgz",
|
||||
"integrity": "sha512-XnmapeCW+hDuNdVwpuvO04WKv71wAs8aH+saIq29Bo2fp1SxznHTcQArTZtK6Wgr/E9BHXeB2iAPpUZuI6G+xA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@react-pdf/fns": "2.2.1",
|
||||
"@react-pdf/types": "^2.4.1",
|
||||
"@react-pdf/types": "^2.5.0",
|
||||
"color-string": "^1.9.1",
|
||||
"hsl-to-hex": "^1.0.0",
|
||||
"media-engine": "^1.0.3",
|
||||
@@ -5529,9 +5529,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-pdf/types": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.4.1.tgz",
|
||||
"integrity": "sha512-w8pk7svhjVj5f7d7kjEGXSk26ffCqRSQcgWR4DwcFltNpSM18ZJmzmM6WrNeeP437y48LlykLnmGDA3oATakgw=="
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.5.0.tgz",
|
||||
"integrity": "sha512-XsVRkt0hQ60I4e3leAVt+aZR3KJCaJd179BfJHAv4F4x6Vq3yqkry8lcbUWKGKDw1j3/8sW4FsgGR41SFvsG9A=="
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "2.2.1",
|
||||
@@ -7237,9 +7237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.18",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
|
||||
"integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==",
|
||||
"version": "10.4.19",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
|
||||
"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -7256,7 +7256,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"browserslist": "^4.23.0",
|
||||
"caniuse-lite": "^1.0.30001591",
|
||||
"caniuse-lite": "^1.0.30001599",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.0",
|
||||
@@ -8108,9 +8108,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001593",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz",
|
||||
"integrity": "sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==",
|
||||
"version": "1.0.30001616",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz",
|
||||
"integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -11097,11 +11097,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
|
||||
},
|
||||
"node_modules/fast-memoize": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz",
|
||||
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw=="
|
||||
},
|
||||
"node_modules/fast-url-parser": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
|
||||
@@ -13385,9 +13380,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jay-peg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.0.1.tgz",
|
||||
"integrity": "sha512-zBfjkGbuuNXk8JW+rEePpPEbRRjupS8q+5yPak7kjy3e2GvvNwsLle9okEFvfGyZA6HvtSSiYrVd1/jgnYebaQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.0.2.tgz",
|
||||
"integrity": "sha512-fyV3NVvv6pTys/3BTapBUGAWAuU9rM2gRcgijZHzptd5KKL+s+S7hESFN+wOsbDH1MzFwdlRAXi0aGxS6uiMKg==",
|
||||
"dependencies": {
|
||||
"restructure": "^3.0.0"
|
||||
}
|
||||
@@ -15942,11 +15937,6 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/lodash.pick": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz",
|
||||
"integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q=="
|
||||
},
|
||||
"node_modules/lodash.sortby": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
|
||||
@@ -17650,9 +17640,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.37",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz",
|
||||
"integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==",
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -19373,12 +19363,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/re-resizable": {
|
||||
"version": "6.9.6",
|
||||
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.6.tgz",
|
||||
"integrity": "sha512-0xYKS5+Z0zk+vICQlcZW+g54CcJTTmHluA7JUUgvERDxnKAnytylcyPsA+BSFi759s5hPlHmBRegFrwXs2FuBQ==",
|
||||
"dependencies": {
|
||||
"fast-memoize": "^2.5.1"
|
||||
},
|
||||
"version": "6.9.14",
|
||||
"resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.14.tgz",
|
||||
"integrity": "sha512-2UbPrpezMr6gkHKNCRA/N6QGGU237SKOZ78yMHId204A/oXWSAREAIuGZNQ9qlrJosewzcsv2CphZH3u7hC6ng==",
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
@@ -19971,45 +19958,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-rnd": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.1.tgz",
|
||||
"integrity": "sha512-0m887AjQZr6p2ADLNnipquqsDq4XJu/uqVqI3zuoGD19tRm6uB83HmZWydtkilNp5EWsOHbLGF4IjWMdd5du8Q==",
|
||||
"version": "10.4.10",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.10.tgz",
|
||||
"integrity": "sha512-YjQAgEeSbNUoOXSD9ZBvIiLVizFb+bNhpDk8DbIRHA557NW02CXbwsAeOTpJQnsdhEL+NP2I+Ssrwejqcodtjg==",
|
||||
"dependencies": {
|
||||
"re-resizable": "6.9.6",
|
||||
"react-draggable": "4.4.5",
|
||||
"tslib": "2.3.1"
|
||||
"re-resizable": "6.9.14",
|
||||
"react-draggable": "4.4.6",
|
||||
"tslib": "2.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.3.0",
|
||||
"react-dom": ">=16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-rnd/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/react-rnd/node_modules/react-draggable": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz",
|
||||
"integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0",
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-rnd/node_modules/tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
|
||||
@@ -20236,15 +20197,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/reactour": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.2.tgz",
|
||||
"integrity": "sha512-gbbUiLn8LbpbDW6k+QlVySkblvOICBd3AWMGRKPQghhmuMlOHNqgjW32JrT406lZHUvVlA8+umYD/2MmCHkNOQ==",
|
||||
"version": "1.19.3",
|
||||
"resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.3.tgz",
|
||||
"integrity": "sha512-a2/j+xmbGlPMQhMy/zdpSmJN1STc0PcLUxhJbAC+bOcYdWHStFR02j6dhNuF/+IeqpX/d44PvlrRXSTt4LGk6w==",
|
||||
"dependencies": {
|
||||
"@rooks/use-mutation-observer": "4.11.2",
|
||||
"classnames": "2.3.1",
|
||||
"focus-outline-manager": "^1.0.2",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lodash.pick": "4.4.0",
|
||||
"prop-types": "15.7.2",
|
||||
"react-focus-lock": "2.5.2",
|
||||
"scroll-smooth": "1.1.1",
|
||||
@@ -20702,9 +20662,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/restructure": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.0.tgz",
|
||||
"integrity": "sha512-Xj8/MEIhhfj9X2rmD9iJ4Gga9EFqVlpMj3vfLnV2r/Mh5jRMryNV+6lWh9GdJtDBcBSPIqzRdfBQ3wDtNFv/uw=="
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.1.tgz",
|
||||
"integrity": "sha512-6neDpI/yE9eogQo22qmWwKIA9wFPRyYjQleDEh6zaNAf2ZPqLJYUvNBJBWEWNoBlCeQMQkvIOe2YI/K2GOag+g=="
|
||||
},
|
||||
"node_modules/retry": {
|
||||
"version": "0.13.1",
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.1",
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@radix-ui/themes": "^2.0.3",
|
||||
"@react-pdf/renderer": "^3.4.0",
|
||||
"@react-pdf/renderer": "^3.4.4",
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"file-saver": "^2.0.5",
|
||||
@@ -31,7 +31,7 @@
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-rnd": "^10.4.10",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
@@ -39,7 +39,7 @@
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.3",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.2",
|
||||
"reactour": "^1.19.3",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
@@ -83,14 +83,14 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs2": "^7.24.1",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"@babel/runtime-corejs2": "^7.24.5",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"commitizen": "^4.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.37",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.3.1",
|
||||
"tailwindcss": "^3.4.1"
|
||||
|
||||
@@ -109,6 +109,10 @@ function App() {
|
||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/login/:base64url"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||
<Route
|
||||
path="/forgetpassword"
|
||||
|
||||
@@ -20,7 +20,7 @@ export function EmailBody(props) {
|
||||
required
|
||||
value={props.requestSubject}
|
||||
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||
placeholder="${senderName} has requested you to sign ${documentName}"
|
||||
placeholder='${senderName} has requested you to sign "${documentName}"'
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
<label className="text-sm ml-2 mt-3">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import printModule from "print-js";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
@@ -7,8 +7,9 @@ import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import Certificate from "./Certificate";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
import axios from "axios";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import Parse from "parse";
|
||||
|
||||
function Header({
|
||||
isPdfRequestFiles,
|
||||
@@ -39,6 +40,7 @@ function Header({
|
||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const navigate = useNavigate();
|
||||
const [isCertificate, setIsCertificate] = useState(false);
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
//for go to previous page
|
||||
function previousPage() {
|
||||
@@ -61,30 +63,43 @@ function Header({
|
||||
const handleToPrint = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const pdf = await getBase64FromUrl(pdfUrl);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
try {
|
||||
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
const pdf = await getBase64FromUrl(url);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" &&
|
||||
navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadPdf = () => {
|
||||
const handleDownloadPdf = async () => {
|
||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
try {
|
||||
const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
@@ -102,55 +117,41 @@ function Header({
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
}
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
try {
|
||||
const data = {
|
||||
docId: pdfDetails[0]?.objectId
|
||||
};
|
||||
const docDetails = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getDocument`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
if (docDetails.data && docDetails.data.result) {
|
||||
const doc = docDetails.data.result;
|
||||
if (doc?.CertificateUrl) {
|
||||
await fetch(doc?.CertificateUrl);
|
||||
const certificateUrl = doc?.CertificateUrl;
|
||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||
setIsCertificate(false);
|
||||
} else {
|
||||
setIsCertificate(true);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in download in certificate", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const GenerateCertificate = () => {
|
||||
//after generate download certifcate pdf
|
||||
const handleDownload = (pdfBlob, fileName) => {
|
||||
if (pdfBlob) {
|
||||
const url = window.URL.createObjectURL(pdfBlob);
|
||||
// Create a temporary anchor element
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
// Append the anchor to the body
|
||||
document.body.appendChild(link);
|
||||
// Programmatically click the anchor to trigger the download
|
||||
link.click();
|
||||
// Remove the anchor from the body
|
||||
document.body.removeChild(link);
|
||||
// Release the object URL to free up resources
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PDFDownloadLink
|
||||
onClick={(e) => e.preventDefault()}
|
||||
style={{ textDecoration: "none", zIndex: "35" }}
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
>
|
||||
{({ blob, loading }) => (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleDownload(
|
||||
blob,
|
||||
`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`
|
||||
)
|
||||
}
|
||||
disabled={loading}
|
||||
className=" md:bg-[#08bc66] border-none focus:outline-none flex flex-row items-center md:shadow md:rounded-[3px] py-[3px] md:px-[11px] md:text-white text-black md:font-[500] text-[13px] mr-[5px]"
|
||||
>
|
||||
<i className="fa-solid fa-award py-[3px]" aria-hidden="true"></i>
|
||||
<span className="md:hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||
{isMobile && isShowHeader ? (
|
||||
@@ -240,32 +241,24 @@ function Header({
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
) : (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<GenerateCertificate />
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
</>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
{isSignYourself && (
|
||||
<DropdownMenu.Item
|
||||
@@ -442,25 +435,17 @@ function Header({
|
||||
alreadySign ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
Certificate
|
||||
</span>
|
||||
</button>
|
||||
) : (
|
||||
<GenerateCertificate />
|
||||
)}
|
||||
</>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
@@ -520,23 +505,17 @@ function Header({
|
||||
) : isCompleted ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
) : (
|
||||
<GenerateCertificate />
|
||||
)}
|
||||
</>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
@@ -592,6 +571,16 @@ function Header({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isCertificate}
|
||||
title={"Generating certificate"}
|
||||
handleClose={() => setIsCertificate(false)}
|
||||
>
|
||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
|
||||
<p>Completion certificate is generating,</p>
|
||||
<p>please wait for some time if not download try again later</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -214,8 +214,8 @@ function SignPad({
|
||||
const fontfamily = fontStyle
|
||||
? fontStyle
|
||||
: fontSelect
|
||||
? fontSelect
|
||||
: "Fasthand";
|
||||
? fontSelect
|
||||
: "Fasthand";
|
||||
|
||||
//creating span for getting text content width
|
||||
const span = document.createElement("span");
|
||||
@@ -267,10 +267,10 @@ function SignPad({
|
||||
key === 0 && penColor === "blue"
|
||||
? "2px solid blue"
|
||||
: key === 1 && penColor === "red"
|
||||
? "2px solid red"
|
||||
: key === 2 && penColor === "black"
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
? "2px solid red"
|
||||
: key === 2 && penColor === "black"
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
onClick={() => {
|
||||
props?.convertToImg &&
|
||||
@@ -536,7 +536,6 @@ function SignPad({
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
@@ -553,7 +552,7 @@ function SignPad({
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import { rejectBtn, submitBtn, themeColor } from "../../constant/const";
|
||||
|
||||
function VerifyEmail(props) {
|
||||
return (
|
||||
<div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded">
|
||||
<div className="bg-white rounded outline-none md:w-[40%] w-[80%]">
|
||||
<div
|
||||
style={{ backgroundColor: themeColor }}
|
||||
className=" text-white p-[10px] rounded-t"
|
||||
>
|
||||
OTP verification
|
||||
</div>
|
||||
{props.isVerifyModal ? (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
props.setIsVerifyModal(false);
|
||||
props.handleVerifyEmail(e);
|
||||
}}
|
||||
>
|
||||
<div className="px-6 py-3">
|
||||
<label className="mb-2">Enter OTP</label>
|
||||
<input
|
||||
required
|
||||
type="tel"
|
||||
pattern="[0-9]{4}"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter OTP received over email"
|
||||
value={props.otp}
|
||||
onChange={(e) => props.setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="px-6 my-3">
|
||||
<button type="submit" className={submitBtn}>
|
||||
Verify
|
||||
</button>
|
||||
<button
|
||||
className={`${rejectBtn} ml-2`}
|
||||
onClick={(e) => props.handleResend(e)}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
) : props.otpLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "150px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-6 py-3">
|
||||
<p className="mb-2">Please verify your email !</p>
|
||||
<hr />
|
||||
<div className="px-0 mt-3">
|
||||
<button
|
||||
className={submitBtn}
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
props.handleVerifyBtn();
|
||||
}}
|
||||
>
|
||||
Send OTP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default VerifyEmail;
|
||||
@@ -541,54 +541,93 @@ export const signPdfFun = async (
|
||||
setIsAlert,
|
||||
objectId,
|
||||
isSubscribed,
|
||||
activeMailAdapter
|
||||
activeMailAdapter,
|
||||
xyPosition
|
||||
) => {
|
||||
let singleSign,
|
||||
isCustomCompletionMail = false;
|
||||
|
||||
//get tenant details
|
||||
const tenantDetails = await getTenantDetails(objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert("User does not exist");
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
(!isEnableSubscription || isSubscribed)
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
|
||||
singleSign = {
|
||||
mailProvider: activeMailAdapter,
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
userId: signerObjectId,
|
||||
isCustomCompletionMail: isCustomCompletionMail
|
||||
};
|
||||
const response = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
try {
|
||||
//get tenant details
|
||||
const tenantDetails = await getTenantDetails(objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert("User does not exist");
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
(!isEnableSubscription || isSubscribed)
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
})
|
||||
.then((Listdata) => {
|
||||
const json = Listdata.data;
|
||||
const res = json.result;
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// below for loop is used to get first signature of user to send if to signpdf
|
||||
// for adding it in completion certificate
|
||||
let getSignature;
|
||||
for (let item of xyPosition) {
|
||||
const typeExist = item.pos.some((data) => data?.type);
|
||||
if (typeExist) {
|
||||
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||
break;
|
||||
} else {
|
||||
getSignature = item.pos.find((data) => !data.isStamp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
let base64Sign = getSignature.SignUrl;
|
||||
//check https type signature (default signature exist) then convert in base64
|
||||
const isUrl = base64Sign.includes("https");
|
||||
if (isUrl) {
|
||||
try {
|
||||
base64Sign = await fetchImageBase64(base64Sign);
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
}
|
||||
}
|
||||
//change image width and height to 104/44 in png base64
|
||||
const getNewse64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64
|
||||
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||
|
||||
singleSign = {
|
||||
mailProvider: activeMailAdapter,
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
userId: signerObjectId,
|
||||
isCustomCompletionMail: isCustomCompletionMail,
|
||||
signature: suffixbase64
|
||||
};
|
||||
const response = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
// sessionToken: localStorage.getItem("accesstoken")
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
})
|
||||
.then((Listdata) => {
|
||||
const json = Listdata.data;
|
||||
const res = json.result;
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const randomId = () => {
|
||||
@@ -1125,7 +1164,6 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
||||
const imageType = event.target.files[0].type;
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(event.target.files[0]);
|
||||
|
||||
reader.onloadend = function (e) {
|
||||
let width, height;
|
||||
const image = new Image();
|
||||
@@ -1153,6 +1191,50 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
||||
};
|
||||
};
|
||||
|
||||
//convert https url to base64
|
||||
export const fetchImageBase64 = async (imageUrl) => {
|
||||
try {
|
||||
const response = await fetch(imageUrl);
|
||||
const blob = await response.blob();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onloadend = () => {
|
||||
const base64data = reader.result;
|
||||
resolve(base64data);
|
||||
};
|
||||
reader.onerror = (error) => {
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error("Error converting URL to base64:", error);
|
||||
}
|
||||
};
|
||||
//function for select image and upload image
|
||||
export const changeImageWH = async (base64Image) => {
|
||||
const newWidth = 100;
|
||||
const newHeight = 40;
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.src = base64Image;
|
||||
img.onload = async () => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
canvas.width = newWidth;
|
||||
canvas.height = newHeight;
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||
const resizedBase64 = canvas.toDataURL("image/png", 1);
|
||||
resolve(resizedBase64);
|
||||
};
|
||||
img.onerror = (error) => {
|
||||
reject(error);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
//function for embed multiple signature using pdf-lib
|
||||
export const multiSignEmbed = async (
|
||||
pngUrl,
|
||||
@@ -1931,3 +2013,18 @@ export const convertPdfArrayBuffer = async (url) => {
|
||||
return "Error";
|
||||
}
|
||||
};
|
||||
|
||||
//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function
|
||||
export const handleSendOTP = async (email) => {
|
||||
try {
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||
};
|
||||
const body = { email: email };
|
||||
await axios.post(url, body, { headers: headers });
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ const plans = [
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"Everything in OpenSign™ free",
|
||||
"Field validations",
|
||||
"Regular expression validations",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
@@ -52,7 +52,7 @@ const plans = [
|
||||
"API Access",
|
||||
"100 API signatures included",
|
||||
"DocumentId removal from signed docs",
|
||||
"Custom email templates(coming soon)"
|
||||
"Custom email templates"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+455
-151
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { formJson } from "../json/FormJson";
|
||||
import AddUser from "../components/AddUser";
|
||||
@@ -11,8 +11,11 @@ import SignersInput from "../components/shared/fields/SignersInput";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { getFileName } from "../constant/Utils";
|
||||
import { getFileName, toDataUrl } from "../constant/Utils";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import axios from "axios";
|
||||
import { isEnableSubscription, submitBtn } from "../constant/const";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
@@ -32,6 +35,8 @@ function Form() {
|
||||
|
||||
const Forms = (props) => {
|
||||
const maxFileSize = 20;
|
||||
const abortController = new AbortController();
|
||||
const inputFileRef = useRef(null);
|
||||
const navigate = useNavigate();
|
||||
const [signers, setSigners] = useState([]);
|
||||
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
@@ -40,7 +45,9 @@ const Forms = (props) => {
|
||||
Description: "",
|
||||
Note: "",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "false"
|
||||
SendinOrder: "false",
|
||||
password: "",
|
||||
file: ""
|
||||
});
|
||||
const [fileupload, setFileUpload] = useState("");
|
||||
const [fileload, setfileload] = useState(false);
|
||||
@@ -49,11 +56,16 @@ const Forms = (props) => {
|
||||
const [isAlert, setIsAlert] = useState(false);
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [isErr, setIsErr] = useState("");
|
||||
const [isPassword, setIsPassword] = useState(false);
|
||||
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||
const [isCorrectPass, setIsCorrectPass] = useState(true);
|
||||
const handleStrInput = (e) => {
|
||||
setIsCorrectPass(true);
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
useEffect(() => {
|
||||
handleReset();
|
||||
return () => abortController.abort();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.title]);
|
||||
|
||||
@@ -76,6 +88,7 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
try {
|
||||
let files = e.target.files;
|
||||
setFormData((prev) => ({ ...prev, file: e.target.files[0] }));
|
||||
if (typeof files[0] !== "undefined") {
|
||||
const mb = Math.round(files[0].size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
@@ -86,16 +99,179 @@ const Forms = (props) => {
|
||||
e.target.value = "";
|
||||
return;
|
||||
} else {
|
||||
try {
|
||||
const res = await getFileAsArrayBuffer(files[0]);
|
||||
const pdfBytes = await PDFDocument.load(res);
|
||||
console.log("pdfbytes ", pdfBytes);
|
||||
handleFileUpload(files[0]);
|
||||
} catch (err) {
|
||||
alert(`Currently encrypted pdf files are not supported.`);
|
||||
setFileUpload("");
|
||||
e.target.value = "";
|
||||
console.log("err ", err);
|
||||
if (files?.[0]?.type === "application/pdf") {
|
||||
try {
|
||||
const res = await getFileAsArrayBuffer(files[0]);
|
||||
await PDFDocument.load(res);
|
||||
handleFileUpload(files[0]);
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
await Parse.Cloud.run("encryptedpdf", {
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in sending posthog encryptedpdf", err);
|
||||
}
|
||||
// console.log("err ", err);
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const fileName = files?.[0].name;
|
||||
const size = files?.[0].size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("password", "");
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data"
|
||||
// sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, formData, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(
|
||||
name,
|
||||
pdfFile,
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
}
|
||||
} catch (err) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
} else {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setIsDecrypting(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("err ", err);
|
||||
setFileUpload("");
|
||||
e.target.value = "";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const isImage = files?.[0]?.type.includes("image/");
|
||||
if (isImage) {
|
||||
const image = await toDataUrl(files[0]);
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
let embedImg;
|
||||
if (files?.[0]?.type === "image/png") {
|
||||
embedImg = await pdfDoc.embedPng(image);
|
||||
} else {
|
||||
embedImg = await pdfDoc.embedJpg(image);
|
||||
}
|
||||
|
||||
// Get image dimensions
|
||||
const imageWidth = embedImg.width;
|
||||
const imageHeight = embedImg.height;
|
||||
const page = pdfDoc.addPage([imageWidth, imageHeight]);
|
||||
page.drawImage(embedImg, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: imageWidth,
|
||||
height: imageHeight
|
||||
});
|
||||
const getFile = await pdfDoc.save({
|
||||
useObjectStreams: false
|
||||
});
|
||||
setfileload(true);
|
||||
const fileName = files[0].name;
|
||||
const size = files[0].size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...getFile],
|
||||
"application/pdf"
|
||||
);
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round(
|
||||
(loaded * 100) / total
|
||||
);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
setFileUpload(response.url());
|
||||
setfileload(false);
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
e.target.value = "";
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
} else {
|
||||
if (isEnableSubscription) {
|
||||
try {
|
||||
setfileload(true);
|
||||
const url = "https://ai.nxglabs.in/docxtopdf";
|
||||
let formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data",
|
||||
sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
signal: abortController.signal
|
||||
};
|
||||
const res = await axios.post(url, formData, config);
|
||||
if (res.data) {
|
||||
setFileUpload(res.data.url);
|
||||
setfileload(false);
|
||||
}
|
||||
} catch (err) {
|
||||
e.target.value = "";
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.log("err in libreconverter ", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -286,7 +462,79 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
setTimeout(() => setIsReset(false), 50);
|
||||
};
|
||||
const handlePasswordSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsPassword(false);
|
||||
setfileload(true);
|
||||
try {
|
||||
const fileName = formData?.file?.name;
|
||||
const size = formData?.file?.size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||
let Data = new FormData();
|
||||
Data.append("file", formData?.file);
|
||||
Data.append("password", formData.password);
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data"
|
||||
// sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, Data, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
setIsDecrypting(false);
|
||||
// Upload the file to Parse Server
|
||||
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
|
||||
|
||||
await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Retrieve the URL of the uploaded file
|
||||
if (parseFile.url()) {
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setFileUpload(parseFile.url());
|
||||
setfileload(false);
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, parseFile.url(), tenantId);
|
||||
return parseFile.url();
|
||||
}
|
||||
} catch (err) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
if (err?.response?.status === 401) {
|
||||
setIsPassword(true);
|
||||
setIsCorrectPass(false);
|
||||
} else {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setFormData((prev) => ({ ...prev, password: "" }));
|
||||
setIsDecrypting(false);
|
||||
if (inputFileRef.current) {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const handeCloseModal = () => {
|
||||
setIsPassword(false);
|
||||
setFormData((prev) => ({ ...prev, file: "", password: "" }));
|
||||
setIsDecrypting(false);
|
||||
setfileload(false);
|
||||
if (inputFileRef.current) {
|
||||
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<Title title={props?.title} />
|
||||
@@ -310,166 +558,222 @@ const Forms = (props) => {
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
||||
{fileload && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
File<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setFileUpload("")}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<>
|
||||
<ModalUi
|
||||
isOpen={isPassword}
|
||||
handleClose={() => handeCloseModal()}
|
||||
title={"Enter Pdf Password"}
|
||||
>
|
||||
<form onSubmit={handlePasswordSubmit}>
|
||||
<div className="px-6 pt-3 pb-2">
|
||||
<label className="mb-2 text-xs">Password</label>
|
||||
<input
|
||||
type="file"
|
||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
accept="application/pdf"
|
||||
type="text"
|
||||
name="password"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter pdf password"
|
||||
required
|
||||
/>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
{!isCorrectPass ? (
|
||||
<p className="ml-2 text-[11px] text-red-600">
|
||||
Please provide correct password
|
||||
</p>
|
||||
) : (
|
||||
<p className="ml-2 text-[11px] text-transparent text-red-600">
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="px-6 my-3">
|
||||
<button type="submit" className={submitBtn}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalUi>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
||||
{fileload && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{props.title === "New Template"
|
||||
? "Template Title"
|
||||
: "Document Title"}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Name"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.signers && (
|
||||
<SignersInput onChange={handleSigners} isReset={isReset} required />
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Note<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Note"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<SelectFolder
|
||||
onSuccess={handleFolder}
|
||||
folderCls={props.Cls}
|
||||
isReset={isReset}
|
||||
/>
|
||||
|
||||
{props.title === "Request Signatures" && (
|
||||
{isDecrypting && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span className="text-black text-sm">
|
||||
Decrypting pdf please wait...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
{`File (pdf, png, jpg, jpeg${
|
||||
isEnableSubscription ? ", docx)" : ")"
|
||||
}`}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setFileUpload("")}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<input
|
||||
type="file"
|
||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
ref={inputFileRef}
|
||||
accept={
|
||||
isEnableSubscription
|
||||
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
|
||||
: "application/pdf,image/png,image/jpeg"
|
||||
}
|
||||
required
|
||||
/>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Time To Complete (Days)
|
||||
{props.title === "New Template"
|
||||
? "Template Title"
|
||||
: "Document Title"}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
name="Name"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Send In Order</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.signers && (
|
||||
<SignersInput
|
||||
onChange={handleSigners}
|
||||
isReset={isReset}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Note<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Note"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<SelectFolder
|
||||
onSuccess={handleFolder}
|
||||
folderCls={props.Cls}
|
||||
isReset={isReset}
|
||||
/>
|
||||
|
||||
{props.title === "Request Signatures" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Time To Complete (Days)
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
className=""
|
||||
onChange={handleStrInput}
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
<div className="text-center">Yes</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">No</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Send In Order</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
className=""
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">Yes</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">No</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
className={`${
|
||||
isSubmit && "cursor-progress"
|
||||
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
|
||||
type="submit"
|
||||
disabled={isSubmit}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div
|
||||
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
onClick={() => handleReset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
className={`${
|
||||
isSubmit && "cursor-progress"
|
||||
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
|
||||
type="submit"
|
||||
disabled={isSubmit}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div
|
||||
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
onClick={() => handleReset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,9 +6,10 @@ import axios from "axios";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import { contractUsers, getAppLogo } from "../constant/Utils";
|
||||
import logo from "../assets/images/logo.png";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
|
||||
function GuestLogin() {
|
||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
||||
const { id, userMail, contactBookId, base64url } = useParams();
|
||||
let navigate = useNavigate();
|
||||
const [email, setEmail] = useState(userMail);
|
||||
const [OTP, setOTP] = useState("");
|
||||
@@ -16,7 +17,8 @@ function GuestLogin() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [appLogo, setAppLogo] = useState("");
|
||||
|
||||
const [documentId, setDocumentId] = useState(id);
|
||||
const [contactId, setContactId] = useState(contactBookId);
|
||||
useEffect(() => {
|
||||
handleServerUrl();
|
||||
|
||||
@@ -36,17 +38,24 @@ function GuestLogin() {
|
||||
setAppLogo(logo);
|
||||
}
|
||||
|
||||
//split url in array from '&'
|
||||
localStorage.clear();
|
||||
const checkSplit = serverUrl.split("&");
|
||||
const server = checkSplit[0];
|
||||
const parseId = checkSplit[1];
|
||||
const appName = checkSplit[2];
|
||||
|
||||
const newServer = server.replaceAll("%2F", "/");
|
||||
const parseId = appInfo.appId;
|
||||
const newServer = `${appInfo.baseUrl}/`;
|
||||
const appName = appInfo.appname;
|
||||
localStorage.setItem("baseUrl", newServer);
|
||||
localStorage.setItem("parseAppId", parseId);
|
||||
localStorage.setItem("_appName", appName);
|
||||
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
|
||||
if (!id) {
|
||||
//`atob` function is used to decode base64
|
||||
const decodebase64 = atob(base64url);
|
||||
//split url in array from '/'
|
||||
const checkSplit = decodebase64.split("/");
|
||||
setDocumentId(checkSplit[0]);
|
||||
setEmail(checkSplit[1]);
|
||||
setContactId(checkSplit[2]);
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
@@ -74,7 +83,7 @@ function GuestLogin() {
|
||||
};
|
||||
let body = {
|
||||
email: email.toString(),
|
||||
docId: id
|
||||
docId: documentId
|
||||
};
|
||||
let Otp = await axios.post(url, body, { headers: headers });
|
||||
|
||||
@@ -137,7 +146,7 @@ function GuestLogin() {
|
||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||
localStorage.setItem("isGuestSigner", true);
|
||||
setLoading(false);
|
||||
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
|
||||
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
|
||||
@@ -64,6 +64,7 @@ function Opensigndrive() {
|
||||
const sortingValue = ["Name", "Date"];
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [tourData, setTourData] = useState();
|
||||
const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
|
||||
const orderName = {
|
||||
Ascending: "Ascending",
|
||||
Descending: "Descending",
|
||||
@@ -134,7 +135,9 @@ function Opensigndrive() {
|
||||
//function for get all pdf document list
|
||||
const getPdfDocumentList = async (disbaleLoading) => {
|
||||
setLoading(true);
|
||||
checkTourStatus();
|
||||
if (showTourFirstTIme) {
|
||||
checkTourStatus();
|
||||
}
|
||||
if (!disbaleLoading) {
|
||||
setIsLoading({ isLoad: true, message: "This might take some time" });
|
||||
}
|
||||
@@ -250,17 +253,23 @@ function Opensigndrive() {
|
||||
}, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes
|
||||
|
||||
//function for handle folder name path
|
||||
const handleRoute = (index) => {
|
||||
setPdfData([]);
|
||||
const handleRoute = (index, folderData) => {
|
||||
setSkip(0);
|
||||
// after onclick on route filter route from that index
|
||||
const updateFolderName = folderName.filter((x, i) => {
|
||||
if (i <= index) {
|
||||
return x;
|
||||
}
|
||||
});
|
||||
setFolderName(updateFolderName);
|
||||
//get route details after onclick path of folder name
|
||||
const getCurrentId = folderData[index];
|
||||
const getLastId = updateFolderName[updateFolderName.length - 1];
|
||||
setDocId(getLastId.objectId);
|
||||
//below condition is used to check if user click on same route which already open then don't change any thing
|
||||
if (docId !== getCurrentId.objectId) {
|
||||
setPdfData([]);
|
||||
setDocId(getLastId.objectId);
|
||||
}
|
||||
};
|
||||
|
||||
//function for add new folder name
|
||||
@@ -439,7 +448,7 @@ function Opensigndrive() {
|
||||
return (
|
||||
<React.Fragment key={id}>
|
||||
<span
|
||||
onClick={() => handleRoute(id)}
|
||||
onClick={() => handleRoute(id, folderData)}
|
||||
style={{
|
||||
color: "#a64b4e",
|
||||
fontWeight: "400",
|
||||
@@ -474,6 +483,7 @@ function Opensigndrive() {
|
||||
|
||||
const closeTour = async () => {
|
||||
setIsTour(false);
|
||||
setShowTourFirstTime(false);
|
||||
if (isDontShow) {
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const appId = localStorage.getItem("parseAppId");
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import { DndProvider } from "react-dnd";
|
||||
@@ -25,7 +26,8 @@ import {
|
||||
replaceMailVaribles,
|
||||
fetchSubscription,
|
||||
convertPdfArrayBuffer,
|
||||
contractUsers
|
||||
contractUsers,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
@@ -35,6 +37,7 @@ import PdfDeclineModal from "../primitives/PdfDeclineModal";
|
||||
import Title from "../components/Title";
|
||||
import DefaultSignature from "../components/pdf/DefaultSignature";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
|
||||
function PdfRequestFiles() {
|
||||
const { docId } = useParams();
|
||||
@@ -57,10 +60,12 @@ function PdfRequestFiles() {
|
||||
const imageRef = useRef(null);
|
||||
const [handleError, setHandleError] = useState();
|
||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
});
|
||||
|
||||
const [defaultSignImg, setDefaultSignImg] = useState();
|
||||
const [isDocId, setIsDocId] = useState(false);
|
||||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||||
@@ -100,6 +105,9 @@ function PdfRequestFiles() {
|
||||
const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false);
|
||||
const [extUserId, setExtUserId] = useState("");
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const divRef = useRef(null);
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const rowLevel =
|
||||
@@ -139,6 +147,44 @@ function PdfRequestFiles() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current]);
|
||||
|
||||
//function to use resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
setOtpLoader(false);
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
try {
|
||||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||
otp: otp,
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
if (resEmail?.message === "Email is verified.") {
|
||||
setIsEmailVerified(true);
|
||||
} else if (resEmail?.message === "Email is already verified.") {
|
||||
setIsEmailVerified(true);
|
||||
}
|
||||
setOtp("");
|
||||
alert(resEmail.message);
|
||||
setIsVerifyModal(false);
|
||||
// handleRecipientSign();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
async function checkIsSubscribed(extUserId, contactId) {
|
||||
const isGuestSign = location.pathname.includes("/load/") || false;
|
||||
const res = await fetchSubscription(extUserId, contactId, isGuestSign);
|
||||
@@ -238,6 +284,33 @@ function PdfRequestFiles() {
|
||||
setExpiredDate(expireDateFormat);
|
||||
}
|
||||
|
||||
const isGuestSign = location.pathname.includes("/load/");
|
||||
if (
|
||||
!isGuestSign &&
|
||||
!isCompleted &&
|
||||
!declined &&
|
||||
currDate < expireUpdateDate
|
||||
) {
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified;
|
||||
isEmailVerified = currentUser?.emailVerified;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
}
|
||||
} catch (e) {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (documentData.length > 0) {
|
||||
const checkDocIdExist =
|
||||
documentData[0].AuditTrail &&
|
||||
@@ -271,6 +344,7 @@ function PdfRequestFiles() {
|
||||
) {
|
||||
const params = {
|
||||
event: "viewed",
|
||||
contactId: currUserId,
|
||||
body: {
|
||||
objectId: documentData?.[0].objectId,
|
||||
file: documentData?.[0]?.SignedUrl || documentData?.[0]?.URL,
|
||||
@@ -471,7 +545,8 @@ function PdfRequestFiles() {
|
||||
const maxCount =
|
||||
requiredCheckbox[i].options?.validation?.maxRequiredCount;
|
||||
const parseMax = maxCount && parseInt(maxCount);
|
||||
const response = requiredCheckbox[i].options?.response?.length;
|
||||
const response =
|
||||
requiredCheckbox[i].options?.response?.length;
|
||||
const defaultValue =
|
||||
requiredCheckbox[i].options?.defaultValue?.length;
|
||||
if (parseMin === 0 && parseMax === 0) {
|
||||
@@ -570,193 +645,214 @@ function PdfRequestFiles() {
|
||||
const pngUrl = checkUser[0].placeHolder;
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
const isSignYourSelfFlow = false;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
//embed document's object id to all pages in pdf document
|
||||
if (!HeaderDocId) {
|
||||
if (!isDocId) {
|
||||
await embedDocId(pdfDoc, documentId, allPages);
|
||||
}
|
||||
}
|
||||
//embed multi signature in pdf
|
||||
const pdfBytes = await multiSignEmbed(
|
||||
pngUrl,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
isSignYourSelfFlow,
|
||||
containerWH
|
||||
);
|
||||
//get ExistUserPtr object id of user class to get tenantDetails
|
||||
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
|
||||
//get ExistUserPtr email to get userDetails
|
||||
const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
const res = await contractUsers(currentUserEmail);
|
||||
let activeMailAdapter = "";
|
||||
if (res === "Error: Something went wrong!") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading({
|
||||
isLoad: false
|
||||
});
|
||||
} else if (!res || res?.length === 0) {
|
||||
activeMailAdapter = "";
|
||||
} else if (res[0] && res.length) {
|
||||
activeMailAdapter = res[0]?.active_mail_adapter;
|
||||
}
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
try {
|
||||
const res = await signPdfFun(
|
||||
pdfBytes,
|
||||
documentId,
|
||||
signerObjectId,
|
||||
setIsAlert,
|
||||
objectId,
|
||||
isSubscribed,
|
||||
activeMailAdapter
|
||||
);
|
||||
if (res && res.status === "success") {
|
||||
setPdfUrl(res.data);
|
||||
setIsSigned(true);
|
||||
setSignedSigners([]);
|
||||
setUnSignedSigners([]);
|
||||
getDocumentDetails();
|
||||
const index = pdfDetails?.[0].Signers.findIndex(
|
||||
(x) => x.Email === jsonSender.email
|
||||
);
|
||||
const newIndex = index + 1;
|
||||
const user = pdfDetails?.[0].Signers[newIndex];
|
||||
if (user) {
|
||||
setIsCompleted({
|
||||
isModal: true,
|
||||
message:
|
||||
"You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers."
|
||||
});
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
const isSignYourSelfFlow = false;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
//embed document's object id to all pages in pdf document
|
||||
if (!HeaderDocId) {
|
||||
if (!isDocId) {
|
||||
await embedDocId(pdfDoc, documentId, allPages);
|
||||
}
|
||||
if (sendInOrder) {
|
||||
const requestBody = pdfDetails?.[0]?.RequestBody;
|
||||
const requestSubject = pdfDetails?.[0]?.RequestSubject;
|
||||
}
|
||||
//embed multi signature in pdf
|
||||
const pdfBytes = await multiSignEmbed(
|
||||
pngUrl,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
isSignYourSelfFlow,
|
||||
containerWH
|
||||
);
|
||||
//get ExistUserPtr object id of user class to get tenantDetails
|
||||
const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId;
|
||||
//get ExistUserPtr email to get userDetails
|
||||
const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
const res = await contractUsers(currentUserEmail);
|
||||
let activeMailAdapter = "";
|
||||
if (res === "Error: Something went wrong!") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading({
|
||||
isLoad: false
|
||||
});
|
||||
} else if (!res || res?.length === 0) {
|
||||
activeMailAdapter = "";
|
||||
} else if (res[0] && res.length) {
|
||||
activeMailAdapter = res[0]?.active_mail_adapter;
|
||||
}
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
try {
|
||||
const res = await signPdfFun(
|
||||
pdfBytes,
|
||||
documentId,
|
||||
signerObjectId,
|
||||
setIsAlert,
|
||||
objectId,
|
||||
isSubscribed,
|
||||
activeMailAdapter,
|
||||
pngUrl
|
||||
);
|
||||
if (res && res.status === "success") {
|
||||
setPdfUrl(res.data);
|
||||
setIsSigned(true);
|
||||
setSignedSigners([]);
|
||||
setUnSignedSigners([]);
|
||||
getDocumentDetails();
|
||||
const index = pdfDetails?.[0].Signers.findIndex(
|
||||
(x) => x.Email === jsonSender.email
|
||||
);
|
||||
const newIndex = index + 1;
|
||||
const user = pdfDetails?.[0].Signers[newIndex];
|
||||
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"
|
||||
setIsCompleted({
|
||||
isModal: true,
|
||||
message:
|
||||
"You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers."
|
||||
});
|
||||
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
|
||||
}
|
||||
if (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 serverUrl = localStorage.getItem("baseUrl");
|
||||
const newServer = serverUrl.replaceAll("/", "%2F");
|
||||
const objectId = user.objectId;
|
||||
const serverParams = `${newServer}&${localStorage.getItem(
|
||||
"parseAppId"
|
||||
)}&${localStorage.getItem("_appName")}`;
|
||||
const hostUrl = window.location.origin;
|
||||
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`;
|
||||
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 &&
|
||||
(!isEnableSubscription || 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}>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
|
||||
const encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${user.Email}/${objectId}`
|
||||
);
|
||||
}
|
||||
let signPdf = `${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 &&
|
||||
(!isEnableSubscription || 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,
|
||||
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 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}>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,
|
||||
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 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 {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
} catch (err) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
setIsUiLoading(false);
|
||||
if (err && err.message.includes("is encrypted.")) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: `Currently encrypted pdf files are not supported.`
|
||||
});
|
||||
} else {
|
||||
console.log("err in request signing", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: `Something went wrong.`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
setIsSignPad(false);
|
||||
@@ -1148,9 +1244,9 @@ function PdfRequestFiles() {
|
||||
isDecline.currnt === "Sure"
|
||||
? "Are you sure want to decline this document ?"
|
||||
: isDecline.currnt === "YouDeclined"
|
||||
? "You have declined this document!"
|
||||
: isDecline.currnt === "another" &&
|
||||
"You can not sign this document as it has been declined/revoked."
|
||||
? "You have declined this document!"
|
||||
: isDecline.currnt === "another" &&
|
||||
"You can not sign this document as it has been declined/revoked."
|
||||
}
|
||||
footerMessage={isDecline.currnt === "Sure"}
|
||||
declineDoc={declineDoc}
|
||||
@@ -1162,6 +1258,18 @@ function PdfRequestFiles() {
|
||||
headMsg="Document Expired!"
|
||||
bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`}
|
||||
/>
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
isVerifyModal={isVerifyModal}
|
||||
setIsVerifyModal={setIsVerifyModal}
|
||||
handleVerifyEmail={handleVerifyEmail}
|
||||
setOtp={setOtp}
|
||||
otp={otp}
|
||||
otpLoader={otpLoader}
|
||||
handleVerifyBtn={handleVerifyBtn}
|
||||
handleResend={handleResend}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ModalUi
|
||||
headerColor={defaultSignImg ? themeColor : "#dc3545"}
|
||||
@@ -1229,7 +1337,10 @@ function PdfRequestFiles() {
|
||||
</ModalUi>
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdfDetails[0] && pdfDetails[0].SignedUrl}
|
||||
signPdfUrl={
|
||||
pdfDetails[0] &&
|
||||
(pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL)
|
||||
}
|
||||
allPages={allPages}
|
||||
setAllPages={setAllPages}
|
||||
setPageNumber={setPageNumber}
|
||||
|
||||
@@ -201,16 +201,16 @@ function PlaceHolderSign() {
|
||||
|
||||
setDefaultBody(defaultRequestBody);
|
||||
setDefaultSubject(
|
||||
`{{sender_name}} has requested you to sign {{document_title}}`
|
||||
`{{sender_name}} has requested you to sign "{{document_title}}"`
|
||||
);
|
||||
} else {
|
||||
setRequestBody(defaultRequestBody);
|
||||
setRequestSubject(
|
||||
`{{sender_name}} has requested you to sign {{document_title}}`
|
||||
`{{sender_name}} has requested you to sign "{{document_title}}"`
|
||||
);
|
||||
setDefaultBody(defaultRequestBody);
|
||||
setDefaultSubject(
|
||||
`{{sender_name}}has requested you to sign {{document_title}}`
|
||||
`{{sender_name}}has requested you to sign "{{document_title}}"`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -967,15 +967,13 @@ function PlaceHolderSign() {
|
||||
const shareLinkList = [];
|
||||
let signerMail = signersdata;
|
||||
for (let i = 0; i < signerMail.length; i++) {
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const newServer = serverUrl.replaceAll("/", "%2F");
|
||||
const objectId = signerMail[i].objectId;
|
||||
const serverParams = `${newServer}&${localStorage.getItem(
|
||||
"parseAppId"
|
||||
)}&${localStorage.getItem("_appName")}`;
|
||||
|
||||
const hostUrl = window.location.origin;
|
||||
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
|
||||
//encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}`
|
||||
);
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
shareLinkList.push({ signerEmail: signerMail[i].Email, url: signPdf });
|
||||
}
|
||||
return shareLinkList.map((data, ind) => {
|
||||
@@ -1049,15 +1047,13 @@ function PlaceHolderSign() {
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const newServer = serverUrl.replaceAll("/", "%2F");
|
||||
const objectId = signerMail[i].objectId;
|
||||
const serverParams = `${newServer}&${localStorage.getItem(
|
||||
"parseAppId"
|
||||
)}&${localStorage.getItem("_appName")}`;
|
||||
|
||||
const hostUrl = window.location.origin;
|
||||
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
|
||||
//encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(
|
||||
`${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}`
|
||||
);
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
const openSignUrl = "https://www.opensignlabs.com/";
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
@@ -1103,7 +1099,7 @@ function PlaceHolderSign() {
|
||||
recipient: signerMail[i].Email,
|
||||
subject: isCustomize
|
||||
? replaceVar?.subject
|
||||
: `${senderName} has requested you to sign ${documentName}`,
|
||||
: `${senderName} has requested you to sign "${documentName}"`,
|
||||
from: senderEmail,
|
||||
html: isCustomize
|
||||
? replaceVar?.body
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import axios from "axios";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
@@ -29,7 +30,10 @@ import {
|
||||
textWidget,
|
||||
getTenantDetails,
|
||||
checkIsSubscribed,
|
||||
convertPdfArrayBuffer
|
||||
convertPdfArrayBuffer,
|
||||
fetchImageBase64,
|
||||
changeImageWH,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
@@ -42,6 +46,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
@@ -89,6 +94,7 @@ function SignYourSelf() {
|
||||
const [containerWH, setContainerWH] = useState({});
|
||||
const [isPageCopy, setIsPageCopy] = useState(false);
|
||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||
status: false
|
||||
});
|
||||
@@ -105,6 +111,9 @@ function SignYourSelf() {
|
||||
const [isCompleted, setIsCompleted] = useState(false);
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const [activeMailAdapter, setActiveMailAdapter] = useState("");
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -194,13 +203,18 @@ function SignYourSelf() {
|
||||
setPdfDetails(documentData);
|
||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||
const url = documentData[0] && 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: Something went wrong!");
|
||||
if (url) {
|
||||
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||
if (arrayBuffer === "Error") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
} else {
|
||||
setPdfArrayBuffer(arrayBuffer);
|
||||
}
|
||||
} else {
|
||||
setPdfArrayBuffer(arrayBuffer);
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
|
||||
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||
if (isCompleted) {
|
||||
setIsCompleted(true);
|
||||
@@ -219,6 +233,29 @@ function SignYourSelf() {
|
||||
setSignBtnPosition([]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCompleted) {
|
||||
//check current user email verified or not
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified;
|
||||
isEmailVerified = currentUser?.emailVerified;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
}
|
||||
} catch (e) {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
@@ -527,6 +564,43 @@ function SignYourSelf() {
|
||||
setSignKey(key);
|
||||
};
|
||||
|
||||
//`handleResend` function is used to resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
setOtpLoader(false);
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
try {
|
||||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||
otp: otp,
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
if (resEmail?.message === "Email is verified.") {
|
||||
setIsEmailVerified(true);
|
||||
} else if (resEmail?.message === "Email is already verified.") {
|
||||
setIsEmailVerified(true);
|
||||
}
|
||||
setOtp("");
|
||||
alert(resEmail.message);
|
||||
setIsVerifyModal(false);
|
||||
// handleRecipientSign();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||
async function embedWidgetsData() {
|
||||
let showAlert = false;
|
||||
@@ -574,30 +648,44 @@ function SignYourSelf() {
|
||||
setIsUiLoading(true);
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
const isSignYourSelfFlow = true;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
//embed document's object id to all pages in pdf document
|
||||
if (!HeaderDocId) {
|
||||
await embedDocId(pdfDoc, documentId, allPages);
|
||||
try {
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
const isSignYourSelfFlow = true;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
//embed document's object id to all pages in pdf document
|
||||
if (!HeaderDocId) {
|
||||
await embedDocId(pdfDoc, documentId, allPages);
|
||||
}
|
||||
//embed multi signature in pdf
|
||||
const pdfBytes = await multiSignEmbed(
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
isSignYourSelfFlow,
|
||||
containerWH
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
} catch (err) {
|
||||
setIsUiLoading(false);
|
||||
if (err && err.message.includes("is encrypted.")) {
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: `Currently encrypted pdf files are not supported.`
|
||||
});
|
||||
} else {
|
||||
console.log("err in signing", err);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: `Something went wrong.`
|
||||
});
|
||||
}
|
||||
}
|
||||
//embed multi signature in pdf
|
||||
const pdfBytes = await multiSignEmbed(
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
isSignYourSelfFlow,
|
||||
containerWH
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in embedselfsign ", err );
|
||||
console.log("err in embedselfsign ", err);
|
||||
setIsUiLoading(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
@@ -605,7 +693,6 @@ function SignYourSelf() {
|
||||
});
|
||||
}
|
||||
}
|
||||
// console.log("signyourself", xyPostion);
|
||||
//function for get digital signature
|
||||
const signPdfFun = async (base64Url, documentId) => {
|
||||
let isCustomCompletionMail = false;
|
||||
@@ -622,12 +709,40 @@ function SignYourSelf() {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
// below for loop is used to get first signature of user to send if to signpdf
|
||||
// for adding it in completion certificate
|
||||
let getSignature;
|
||||
for (let item of xyPostion) {
|
||||
const typeExist = item.pos.some((data) => data?.type);
|
||||
if (typeExist) {
|
||||
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||
break;
|
||||
} else {
|
||||
getSignature = item.pos.find((data) => !data.isStamp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let base64Sign = getSignature.SignUrl;
|
||||
//check https type signature (default signature exist) then convert in base64
|
||||
const isUrl = base64Sign.includes("https");
|
||||
if (isUrl) {
|
||||
try {
|
||||
base64Sign = await fetchImageBase64(base64Sign);
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
}
|
||||
}
|
||||
//change image width and height to 104/44 in png base64
|
||||
const getNewse64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64
|
||||
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||
|
||||
let singleSign = {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
isCustomCompletionMail: isCustomCompletionMail,
|
||||
mailProvider: activeMailAdapter
|
||||
mailProvider: activeMailAdapter,
|
||||
signature: suffixbase64
|
||||
};
|
||||
|
||||
await axios
|
||||
@@ -635,7 +750,8 @@ function SignYourSelf() {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
// sessionToken: localStorage.getItem("accesstoken")
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
})
|
||||
.then((Listdata) => {
|
||||
@@ -999,6 +1115,18 @@ function SignYourSelf() {
|
||||
)}
|
||||
|
||||
<div className="signatureContainer" ref={divRef}>
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
isVerifyModal={isVerifyModal}
|
||||
setIsVerifyModal={setIsVerifyModal}
|
||||
handleVerifyEmail={handleVerifyEmail}
|
||||
setOtp={setOtp}
|
||||
otp={otp}
|
||||
otpLoader={otpLoader}
|
||||
handleVerifyBtn={handleVerifyBtn}
|
||||
handleResend={handleResend}
|
||||
/>
|
||||
)}
|
||||
{/* this component used for UI interaction and show their functionality */}
|
||||
{pdfLoadFail && !checkTourStatus && (
|
||||
<Tour
|
||||
|
||||
@@ -8,9 +8,10 @@ import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||
import axios from "axios";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
|
||||
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
|
||||
function UserProfile() {
|
||||
const navigate = useNavigate();
|
||||
@@ -32,12 +33,16 @@ function UserProfile() {
|
||||
const [jobTitle, setJobTitle] = useState(
|
||||
extendUser && extendUser?.[0]?.JobTitle
|
||||
);
|
||||
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(false);
|
||||
useEffect(() => {
|
||||
getUserDetail();
|
||||
}, []);
|
||||
|
||||
const getUserDetail = async () => {
|
||||
setIsLoader(true);
|
||||
const extClass = localStorage.getItem("Extand_Class");
|
||||
const jsonSender = JSON.parse(extClass);
|
||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||
@@ -48,6 +53,26 @@ function UserProfile() {
|
||||
if (HeaderDocId) {
|
||||
setIsDisableDocId(HeaderDocId);
|
||||
}
|
||||
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||
let isEmailVerified = currentUser?.emailVerified || false;
|
||||
if (isEmailVerified) {
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
try {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(currentUser.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
isEmailVerified = user?.get("emailVerified");
|
||||
setIsEmailVerified(isEmailVerified);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (e) {
|
||||
alert("something went wrong!");
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
@@ -177,6 +202,45 @@ function UserProfile() {
|
||||
const handleDisableDocId = () => {
|
||||
setIsDisableDocId((prevChecked) => !prevChecked);
|
||||
};
|
||||
//`handleVerifyBtn` function is used to send otp on user mail
|
||||
const handleVerifyBtn = async () => {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
const handleCloseVerifyModal = async () => {
|
||||
setIsVerifyModal(false);
|
||||
};
|
||||
//`handleVerifyEmail` function is used to verify email with otp
|
||||
const handleVerifyEmail = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
try {
|
||||
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||
otp: otp,
|
||||
email: Parse.User.current().getEmail()
|
||||
});
|
||||
if (resEmail?.message === "Email is verified.") {
|
||||
setIsEmailVerified(true);
|
||||
} else if (resEmail?.message === "Email is already verified.") {
|
||||
setIsEmailVerified(true);
|
||||
}
|
||||
setOtp("");
|
||||
alert(resEmail.message);
|
||||
setIsVerifyModal(false);
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setOtpLoader(false);
|
||||
}
|
||||
};
|
||||
//function to use resend otp for email verification
|
||||
const handleResend = async (e) => {
|
||||
e.preventDefault();
|
||||
setOtpLoader(true);
|
||||
await handleSendOTP();
|
||||
setOtpLoader(false);
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"Profile"} />
|
||||
@@ -310,9 +374,20 @@ function UserProfile() {
|
||||
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold">Is Email verified:</span>{" "}
|
||||
<span>
|
||||
{UserProfile && UserProfile.emailVerified
|
||||
? "Verified"
|
||||
: "Not verified"}
|
||||
{isEmailVerified ? (
|
||||
"Verified"
|
||||
) : (
|
||||
<span>
|
||||
Not verified(
|
||||
<span
|
||||
onClick={() => handleVerifyBtn()}
|
||||
className="hover:underline text-blue-600 cursor-pointer"
|
||||
>
|
||||
verify
|
||||
</span>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
<li className="border-y-[1px] border-gray-300 break-all">
|
||||
@@ -399,6 +474,60 @@ function UserProfile() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isVerifyModal && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"OTP verification"}
|
||||
handleClose={handleCloseVerifyModal}
|
||||
>
|
||||
{otpLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "150px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={(e) => handleVerifyEmail(e)}>
|
||||
<div className="px-6 py-3">
|
||||
<label className="mb-2">Enter OTP</label>
|
||||
<input
|
||||
required
|
||||
type="tel"
|
||||
pattern="[0-9]{4}"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
placeholder="Enter OTP received over email"
|
||||
value={otp}
|
||||
onChange={(e) => setOtp(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="px-6 my-3">
|
||||
<button type="submit" className={submitBtn}>
|
||||
Verify
|
||||
</button>
|
||||
<button
|
||||
className={`${rejectBtn} ml-2`}
|
||||
onClick={(e) => handleResend(e)}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
||||
@@ -10,6 +10,7 @@ import Tooltip from "./Tooltip";
|
||||
import { RWebShare } from "react-web-share";
|
||||
import Tour from "reactour";
|
||||
import Parse from "parse";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
const ReportTable = (props) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -286,13 +287,15 @@ const ReportTable = (props) => {
|
||||
const handleShare = (item) => {
|
||||
setActLoader({ [item.objectId]: true });
|
||||
const host = window.location.origin;
|
||||
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
const baseURL = serverUrl.replace(/\//g, "%2F");
|
||||
const getUrl = (x) => {
|
||||
//encode this url value `${item.objectId}/${x.Email}/${x.objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(`${item.objectId}/${x.Email}/${x.objectId}`);
|
||||
return `${host}/login/${encodeBase64}`;
|
||||
};
|
||||
|
||||
const urls = item.Signers.map((x) => ({
|
||||
email: x.Email,
|
||||
url: `${host}/login/${item.objectId}/${x.Email}/${x.objectId}/${baseURL}%2F&opensign&contracts`
|
||||
url: getUrl(x)
|
||||
}));
|
||||
setShareUrls(urls);
|
||||
setIsShare({ [item.objectId]: true });
|
||||
@@ -420,6 +423,20 @@ const ReportTable = (props) => {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleDownload` is used to get valid doc url available in completed report
|
||||
const handleDownload = async (item) => {
|
||||
const url = item?.SignedUrl || item?.URL || "";
|
||||
if (url) {
|
||||
try {
|
||||
const signedUrl = await Parse.Cloud.run("getsignedurl", { url: url });
|
||||
saveAs(signedUrl);
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
alert("something went wrong, please try again later.");
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
{Object.keys(actLoader)?.length > 0 && (
|
||||
@@ -562,18 +579,14 @@ const ReportTable = (props) => {
|
||||
</td>
|
||||
)}
|
||||
<td className="px-4 py-2">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={item?.URL}
|
||||
href={item?.URL}
|
||||
download={item?.URL}
|
||||
className="text-[blue] hover:text-[blue] hover:underline"
|
||||
<button
|
||||
onClick={() => handleDownload(item)}
|
||||
className="text-[blue] hover:text-[blue] hover:underline focus:outline-none"
|
||||
title={"Download"}
|
||||
>
|
||||
{item?.URL ? "Download" : "-"}
|
||||
</a>
|
||||
</button>
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item?.ExtUserPtr)}
|
||||
</td>
|
||||
|
||||
@@ -217,9 +217,10 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
};
|
||||
|
||||
const objectId = contactMail[i].contactPtr.objectId;
|
||||
|
||||
const hostUrl = baseUrl.origin;
|
||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
||||
//encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
|
||||
const themeBGcolor = '#47a3ad';
|
||||
@@ -266,13 +267,13 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||
} else if (email_body) {
|
||||
replaceVar = replaceMailVaribles(
|
||||
`${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
|
||||
`${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
|
||||
email_body,
|
||||
variables
|
||||
);
|
||||
} else {
|
||||
replaceVar = {
|
||||
subject: `${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
|
||||
subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
|
||||
body: email_html,
|
||||
};
|
||||
}
|
||||
@@ -320,7 +321,9 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
objectId: res.id,
|
||||
signurl: contact.map(x => ({
|
||||
email: x.email,
|
||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
url: `${baseUrl.origin}/login/${btoa(
|
||||
`${res.id}/${x.email}/${x.contactPtr.objectId}`
|
||||
)}`,
|
||||
})),
|
||||
message: 'Document sent successfully!',
|
||||
});
|
||||
|
||||
@@ -278,7 +278,9 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
|
||||
const objectId = contactMail[i].contactPtr.objectId;
|
||||
const hostUrl = baseUrl.origin;
|
||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
||||
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
||||
const encodeBase64 = btoa(`${response.id}/${contactMail[i].email}/${objectId}`);
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
||||
const themeBGcolor = '#47a3ad';
|
||||
@@ -324,13 +326,13 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||
} else if (email_body) {
|
||||
replaceVar = replaceMailVaribles(
|
||||
`${parseExtUser.Name} has requested you to sign ${name}`,
|
||||
`${parseExtUser.Name} has requested you to sign "${name}"`,
|
||||
email_body,
|
||||
variables
|
||||
);
|
||||
} else {
|
||||
replaceVar = {
|
||||
subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`,
|
||||
subject: `${parseExtUser.Name} has requested you to sign "${parseExtUser.Name}"`,
|
||||
body: email_html,
|
||||
};
|
||||
}
|
||||
@@ -363,7 +365,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
objectId: res.id,
|
||||
signurl: contact.map(x => ({
|
||||
email: x.email,
|
||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
||||
url: `${baseUrl.origin}/login/${btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`)}`,
|
||||
})),
|
||||
message: 'Document sent successfully!',
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { customAPIurl } from '../../../../Utils.js';
|
||||
import { customAPIurl, saveFileUsage } from '../../../../Utils.js';
|
||||
|
||||
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
export default async function draftDocument(request, response) {
|
||||
|
||||
@@ -33,7 +33,10 @@ import getInvoices from './parsefunction/getInvoices.js';
|
||||
import getPayments from './parsefunction/getPayments.js';
|
||||
import getSubscriptions from './parsefunction/getSubscriptions.js';
|
||||
import TenantAterFind from './parsefunction/TenantAfterFind.js';
|
||||
import saveSubscriptio from './parsefunction/saveSubscription.js';
|
||||
import saveSubscription from './parsefunction/saveSubscription.js';
|
||||
import VerifyEmail from './parsefunction/VerifyEmail.js';
|
||||
import encryptedpdf from './parsefunction/encryptedPdf.js';
|
||||
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
|
||||
|
||||
Parse.Cloud.define('AddUserToRole', addUserToGroups);
|
||||
Parse.Cloud.define('UserGroups', getUserGroups);
|
||||
@@ -70,4 +73,7 @@ Parse.Cloud.afterFind('contracts_Document', DocumentBeforeFind);
|
||||
Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
|
||||
Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
|
||||
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
|
||||
Parse.Cloud.define('savesubscription', saveSubscriptio);
|
||||
Parse.Cloud.define('savesubscription', saveSubscription);
|
||||
Parse.Cloud.define('verifyemail', VerifyEmail);
|
||||
Parse.Cloud.define('encryptedpdf', encryptedpdf);
|
||||
Parse.Cloud.define('getsignedurl', getSignedUrl);
|
||||
|
||||
@@ -4,6 +4,7 @@ async function DocumentAftersave(request) {
|
||||
console.log('new entry is insert in contracts_Document');
|
||||
const createdAt = request.object.get('createdAt');
|
||||
const Folder = request.object.get('Type');
|
||||
const ip = request?.headers?.['x-real-ip'] || '';
|
||||
if (createdAt && Folder === undefined) {
|
||||
// console.log("IN If condition")
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
||||
@@ -12,6 +13,7 @@ async function DocumentAftersave(request) {
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
updateQuery.set('OriginIp', ip);
|
||||
await updateQuery.save(null, { useMasterKey: true });
|
||||
} else if (createdAt && Folder === 'AIDoc') {
|
||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
||||
@@ -24,6 +26,7 @@ async function DocumentAftersave(request) {
|
||||
const documentQuery = new Parse.Query('contracts_Document');
|
||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||
updateQuery.set('OriginIp', ip);
|
||||
await updateQuery.save(null, { useMasterKey: true });
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ async function DocumentBeforesave(request) {
|
||||
} catch (error) {
|
||||
console.log('Error updating document count in contracts_users: ' + error.message);
|
||||
}
|
||||
if (document?.get('Signers') && document.get('Signers').length > 0) {
|
||||
document.set('DocSentAt', new Date());
|
||||
document.save(null, { useMasterKey: true });
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in document beforesave', err.message);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
export default async function VerifyEmail(request) {
|
||||
try {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
let otpN = request.params.otp;
|
||||
let otp = parseInt(otpN);
|
||||
let email = request.params.email;
|
||||
|
||||
//checking otp is correct or not which already save in defaultdata_Otp class
|
||||
const checkOtp = new Parse.Query('defaultdata_Otp');
|
||||
checkOtp.equalTo('Email', email);
|
||||
checkOtp.equalTo('OTP', otp);
|
||||
|
||||
const res = await checkOtp.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
// Fetch the user by their objectId
|
||||
const isEmailVerified = request?.user?.get('emailVerified');
|
||||
if (isEmailVerified) {
|
||||
return { message: 'Email is already verified.' };
|
||||
} else {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(request?.user.id, {
|
||||
sessionToken: request?.user.getSessionToken(),
|
||||
});
|
||||
|
||||
// Update the emailVerified field to true
|
||||
user.set('emailVerified', true);
|
||||
// Save the user object
|
||||
const res = await user.save(null, { useMasterKey: true });
|
||||
if (res) {
|
||||
return { message: 'Email is verified.' };
|
||||
} else {
|
||||
const error = new Error('Something went wrong, please try again later!');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const error = new Error('OTP is invalid.');
|
||||
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err.code + ' ' + err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import axios from 'axios';
|
||||
export default async function callWebhook(request) {
|
||||
const event = request.params.event;
|
||||
const body = request.params.body;
|
||||
const docId = body.objectId;
|
||||
const contactId = request.params.contactId;
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
@@ -13,6 +15,39 @@ export default async function callWebhook(request) {
|
||||
|
||||
const userId = userRes.data && userRes.data.objectId;
|
||||
if (userId) {
|
||||
if (event === 'viewed' && contactId) {
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
const res = await docQuery.get(docId, { useMasterKey: true });
|
||||
if (res) {
|
||||
const _res = res.toJSON();
|
||||
const userPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const obj = {
|
||||
UserPtr: userPtr,
|
||||
SignedUrl: _res.SignedUrl,
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ViewedOn: date,
|
||||
};
|
||||
const isUserExist = _res?.AuditTrail?.some(
|
||||
x => x.UserPtr.objectId === contactId && x?.ViewedOn
|
||||
);
|
||||
if (!isUserExist) {
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = res.id;
|
||||
if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) {
|
||||
updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]);
|
||||
} else {
|
||||
updateDoc.set('AuditTrail', [obj]);
|
||||
}
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
const extendcls = new Parse.Query('contracts_Users');
|
||||
extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const res = await extendcls.first({ useMasterKey: true });
|
||||
@@ -39,11 +74,11 @@ export default async function callWebhook(request) {
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err);
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Err send data to webhook', err);
|
||||
console.log('Err send data to webhook', err.message);
|
||||
try {
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', err?.status);
|
||||
@@ -54,7 +89,7 @@ export default async function callWebhook(request) {
|
||||
});
|
||||
webhook.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err save in contracts_Webhook', err);
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { PostHog } from 'posthog-node';
|
||||
const ph_project_api_key = process.env.PH_PROJECT_API_KEY;
|
||||
const client = ph_project_api_key ? new PostHog(ph_project_api_key) : '';
|
||||
export default async function encryptedpdf(request) {
|
||||
const email = request.params.email;
|
||||
if (client) {
|
||||
client?.capture({
|
||||
distinctId: email,
|
||||
event: 'encrypted_pdf_error',
|
||||
properties: { response_code: 200 },
|
||||
});
|
||||
}
|
||||
return { message: 'success' };
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import { useLocal } from '../../Utils.js';
|
||||
const credentials = {
|
||||
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
||||
@@ -24,3 +25,25 @@ export default function getPresignedUrl(url) {
|
||||
});
|
||||
return presignedGETURL;
|
||||
}
|
||||
|
||||
export async function getSignedUrl(request) {
|
||||
try {
|
||||
const url = request.params.url;
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('error in getsignedurl', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message;
|
||||
const error = new Parse.Error(code, msg);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,23 +15,44 @@ export default async function GenerateCertificate(docDetails) {
|
||||
const title = 25;
|
||||
const subtitle = 20;
|
||||
const text = 14;
|
||||
const timeText = 11;
|
||||
const textKeyColor = rgb(0.12, 0.12, 0.12);
|
||||
const textValueColor = rgb(0.3, 0.3, 0.3);
|
||||
const completedAt = new Date(docDetails.updatedAt);
|
||||
const completedAt = new Date();
|
||||
const completedUTCtime = completedAt.toUTCString();
|
||||
const signersCount = docDetails?.Signers?.length || 1;
|
||||
const createdAt = new Date();
|
||||
const createdUTCTime = createdAt.toUTCString();
|
||||
const createDate = 'Generated On ' + createdUTCTime;
|
||||
const generateAt = new Date();
|
||||
const generatedUTCTime = generateAt.toUTCString();
|
||||
const generatedOn = 'Generated On ' + generatedUTCTime;
|
||||
const OriginIp = docDetails?.OriginIp || '';
|
||||
const company = docDetails?.ExtUserPtr?.Company || '';
|
||||
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
|
||||
const auditTrail =
|
||||
docDetails.AuditTrail?.length > 1
|
||||
? docDetails.AuditTrail.map(x => {
|
||||
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
|
||||
return { ...data, ipAddress: x.ipAddress };
|
||||
return {
|
||||
...data,
|
||||
ipAddress: x.ipAddress,
|
||||
SignedOn: x?.SignedOn || generatedUTCTime,
|
||||
ViewedOn: x?.ViewedOn || x?.SignedOn || generatedUTCTime,
|
||||
Signature: x?.Signature || '',
|
||||
};
|
||||
})
|
||||
: [{ ...docDetails.ExtUserPtr, ipAddress: docDetails?.AuditTrail[0].ipAddress }];
|
||||
: [
|
||||
{
|
||||
...docDetails.ExtUserPtr,
|
||||
ipAddress: docDetails?.AuditTrail[0].ipAddress,
|
||||
SignedOn: docDetails?.AuditTrail[0]?.SignedOn || generatedUTCTime,
|
||||
ViewedOn:
|
||||
docDetails?.AuditTrail[0]?.ViewedOn ||
|
||||
docDetails?.AuditTrail[0]?.SignedOn ||
|
||||
generatedUTCTime,
|
||||
Signature: docDetails?.AuditTrail[0]?.Signature || '',
|
||||
},
|
||||
];
|
||||
|
||||
const half = width / 2;
|
||||
// Draw a border
|
||||
page.drawRectangle({
|
||||
x: startX,
|
||||
@@ -48,7 +69,7 @@ export default async function GenerateCertificate(docDetails) {
|
||||
height: 25,
|
||||
});
|
||||
|
||||
page.drawText(createDate, {
|
||||
page.drawText(generatedOn, {
|
||||
x: 320,
|
||||
y: 810,
|
||||
size: 12,
|
||||
@@ -127,8 +148,7 @@ export default async function GenerateCertificate(docDetails) {
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Completed on :', {
|
||||
page.drawText('Created on :', {
|
||||
x: 30,
|
||||
y: 625,
|
||||
size: text,
|
||||
@@ -136,15 +156,14 @@ export default async function GenerateCertificate(docDetails) {
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${completedUTCtime}`, {
|
||||
x: 120,
|
||||
page.drawText(`${new Date(createdAt).toUTCString()}`, {
|
||||
x: 105,
|
||||
y: 625,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Signers :', {
|
||||
page.drawText('Completed on :', {
|
||||
x: 30,
|
||||
y: 605,
|
||||
size: text,
|
||||
@@ -152,35 +171,102 @@ export default async function GenerateCertificate(docDetails) {
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${completedUTCtime}`, {
|
||||
x: 125,
|
||||
y: 605,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('Signers :', {
|
||||
x: 30,
|
||||
y: 585,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${signersCount}`, {
|
||||
x: 80,
|
||||
y: 605,
|
||||
y: 585,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('Document originator', {
|
||||
x: 30,
|
||||
y: 565,
|
||||
size: 17,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
page.drawText('Name :', {
|
||||
x: 60,
|
||||
y: 545,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
page.drawText(`${docDetails.ExtUserPtr.Name}`, {
|
||||
x: 105,
|
||||
y: 545,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('Email :', {
|
||||
x: 60,
|
||||
y: 525,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
page.drawText(`${docDetails.ExtUserPtr.Email}`, {
|
||||
x: 105,
|
||||
y: 525,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('IP address :', {
|
||||
x: 60,
|
||||
y: 505,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
page.drawText(`${OriginIp}`, {
|
||||
x: 130,
|
||||
y: 505,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawLine({
|
||||
start: { x: 30, y: 565 },
|
||||
end: { x: width - 30, y: 565 },
|
||||
start: { x: 30, y: 495 },
|
||||
end: { x: width - 30, y: 495 },
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
thickness: 0.5,
|
||||
});
|
||||
page.drawText('Recipients', {
|
||||
x: 30,
|
||||
y: 575,
|
||||
size: subtitle,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
let yPosition1 = 550;
|
||||
let yPosition2 = 530;
|
||||
let yPosition3 = 510;
|
||||
let yPosition4 = 500;
|
||||
auditTrail.forEach(x => {
|
||||
page.drawText('Name :', {
|
||||
let yPosition1 = 475;
|
||||
let yPosition2 = 455;
|
||||
let yPosition3 = 435;
|
||||
let yPosition4 = 415;
|
||||
let yPosition5 = 395;
|
||||
let yPosition6 = 360;
|
||||
auditTrail.slice(0, 3).forEach(async (x, i) => {
|
||||
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
|
||||
page.drawText(`Signer ${i + 1}`, {
|
||||
x: 30,
|
||||
y: yPosition1,
|
||||
size: subtitle,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
page.drawText('Name :', {
|
||||
x: 30,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
@@ -188,15 +274,31 @@ export default async function GenerateCertificate(docDetails) {
|
||||
|
||||
page.drawText(x?.Name, {
|
||||
x: 75,
|
||||
y: yPosition1,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Viewed on :', {
|
||||
x: half +55,
|
||||
y: yPosition2,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
|
||||
x: half + 112,
|
||||
y: yPosition2,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Email :', {
|
||||
x: 30,
|
||||
y: yPosition2,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
@@ -204,41 +306,271 @@ export default async function GenerateCertificate(docDetails) {
|
||||
|
||||
page.drawText(x?.Email, {
|
||||
x: 75,
|
||||
y: yPosition2,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Accessed from :', {
|
||||
page.drawText('Signed on :', {
|
||||
x: half + 55,
|
||||
y: yPosition3 + 5,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
|
||||
x: half + 108,
|
||||
y: yPosition3 + 5,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('IP address :', {
|
||||
x: 30,
|
||||
y: yPosition3,
|
||||
y: yPosition4,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(x?.ipAddress, {
|
||||
x: 125,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
x: 100,
|
||||
y: yPosition4,
|
||||
size: 13,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
page.drawText('Security level :', {
|
||||
x: half + 55,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`Email, OTP Auth`, {
|
||||
x: half + 125,
|
||||
y: yPosition4 + 10,
|
||||
size: timeText,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Signature :', {
|
||||
x: 30,
|
||||
y: yPosition5,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawRectangle({
|
||||
x: 98,
|
||||
y: yPosition5 - 30,
|
||||
width: 104,
|
||||
height: 44,
|
||||
borderColor: rgb(0.22, 0.18, 0.47),
|
||||
borderWidth: 1,
|
||||
});
|
||||
if (embedPng) {
|
||||
page.drawImage(embedPng, {
|
||||
x: 100,
|
||||
y: yPosition5 - 27,
|
||||
width: 100,
|
||||
height: 40,
|
||||
});
|
||||
}
|
||||
page.drawLine({
|
||||
start: { x: 30, y: yPosition4 },
|
||||
end: { x: width - 30, y: yPosition4 },
|
||||
start: { x: 30, y: yPosition6 },
|
||||
end: { x: width - 30, y: yPosition6 },
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
thickness: 0.5,
|
||||
});
|
||||
|
||||
yPosition1 = yPosition4 - 20;
|
||||
yPosition1 = yPosition6 - 20;
|
||||
yPosition2 = yPosition1 - 20;
|
||||
yPosition3 = yPosition2 - 20;
|
||||
yPosition4 = yPosition4 - 70;
|
||||
yPosition4 = yPosition3 - 20;
|
||||
yPosition5 = yPosition4 - 20;
|
||||
yPosition6 = yPosition6 - 140;
|
||||
});
|
||||
|
||||
if (auditTrail.length > 3) {
|
||||
let currentPageIndex = 1;
|
||||
let currentPage = page;
|
||||
auditTrail.slice(3).forEach(async (x, i) => {
|
||||
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
|
||||
|
||||
// Calculate remaining space on current page
|
||||
const remainingSpace = yPosition6;
|
||||
|
||||
// If there's not enough space for the next entry, create a new page
|
||||
if (remainingSpace < 90) {
|
||||
// Adjust the value as needed
|
||||
currentPageIndex++;
|
||||
currentPage = pdfDoc.addPage();
|
||||
currentPage.drawRectangle({
|
||||
x: startX,
|
||||
y: startY,
|
||||
width: width - 2 * startX,
|
||||
height: height - 2 * startY,
|
||||
borderColor: borderColor,
|
||||
borderWidth: 1,
|
||||
});
|
||||
yPosition1 = currentPage.getHeight() - 40;
|
||||
yPosition2 = yPosition1 - 20;
|
||||
yPosition3 = yPosition2 - 20;
|
||||
yPosition4 = yPosition3 - 20;
|
||||
yPosition5 = yPosition4 - 20;
|
||||
yPosition6 = currentPage.getHeight() - 160;
|
||||
}
|
||||
|
||||
currentPage.drawText(`Signer ${4 + i}`, {
|
||||
x: 30,
|
||||
y: yPosition1,
|
||||
size: subtitle,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
currentPage.drawText('Name :', {
|
||||
x: 30,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(x?.Name, {
|
||||
x: 75,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
currentPage.drawText('Viewed on :', {
|
||||
x: half,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
|
||||
x: half + 75,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
currentPage.drawText('Email :', {
|
||||
x: 30,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(x?.Email, {
|
||||
x: 75,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
currentPage.drawText('Signed on :', {
|
||||
x: half,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
|
||||
x: half + 70,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
currentPage.drawText('IP address :', {
|
||||
x: 30,
|
||||
y: yPosition4,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(x?.ipAddress, {
|
||||
x: 100,
|
||||
y: yPosition4,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
currentPage.drawText('Security level :', {
|
||||
x: half,
|
||||
y: yPosition4,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
currentPage.drawText(`Email, OTP Auth`, {
|
||||
x: half + 90,
|
||||
y: yPosition4,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
currentPage.drawText('Signature :', {
|
||||
x: 30,
|
||||
y: yPosition5,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
currentPage.drawRectangle({
|
||||
x: 98,
|
||||
y: yPosition5 - 27,
|
||||
width: 104,
|
||||
height: 44,
|
||||
borderColor: rgb(0.22, 0.18, 0.47),
|
||||
borderWidth: 1,
|
||||
});
|
||||
if (embedPng) {
|
||||
currentPage.drawImage(embedPng, {
|
||||
x: 100,
|
||||
y: yPosition5 - 25,
|
||||
width: 100,
|
||||
height: 40,
|
||||
});
|
||||
}
|
||||
|
||||
currentPage.drawLine({
|
||||
start: { x: 30, y: yPosition6 },
|
||||
end: { x: width - 30, y: yPosition6 },
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
thickness: 0.5,
|
||||
});
|
||||
|
||||
// Update y positions for the next entry
|
||||
yPosition1 = yPosition6 - 20;
|
||||
yPosition2 = yPosition1 - 20;
|
||||
yPosition3 = yPosition2 - 20;
|
||||
yPosition4 = yPosition3 - 20;
|
||||
yPosition5 = yPosition4 - 20;
|
||||
yPosition6 = yPosition6 - 140;
|
||||
});
|
||||
}
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
return pdfBytes;
|
||||
}
|
||||
|
||||
+196
-236
@@ -8,85 +8,96 @@ import GenerateCertificate from './GenerateCertificate.js';
|
||||
const serverUrl = process.env.SERVER_URL,
|
||||
APPID = process.env.APP_ID,
|
||||
masterKEY = process.env.MASTER_KEY;
|
||||
async function uploadFile(e, a) {
|
||||
async function uploadFile(e, t) {
|
||||
try {
|
||||
var t = fs.readFileSync(a),
|
||||
s = new Parse.File(e, [...t], 'application/pdf'),
|
||||
r = (await s.save({ useMasterKey: !0 }), s.url());
|
||||
return { imageUrl: r };
|
||||
var a = fs.readFileSync(t),
|
||||
r = new Parse.File(e, [...a], 'application/pdf'),
|
||||
i = (await r.save({ useMasterKey: !0 }), r.url());
|
||||
return { imageUrl: i };
|
||||
} catch (e) {
|
||||
console.log('Err ', e), fs.unlinkSync(a);
|
||||
console.log('Err ', e), fs.unlinkSync(t);
|
||||
}
|
||||
}
|
||||
async function updateDoc(t, s, r, i, o, n) {
|
||||
async function updateDoc(a, r, i, s, o, n, l) {
|
||||
try {
|
||||
var l = {
|
||||
UserPtr: { __type: 'Pointer', className: n, objectId: r },
|
||||
SignedUrl: s,
|
||||
Activity: 'Signed',
|
||||
ipAddress: i,
|
||||
};
|
||||
var c,
|
||||
d,
|
||||
p = {
|
||||
UserPtr: { __type: 'Pointer', className: n, objectId: i },
|
||||
SignedUrl: r,
|
||||
Activity: 'Signed',
|
||||
ipAddress: s,
|
||||
SignedOn: new Date(),
|
||||
Signature: l,
|
||||
};
|
||||
let e;
|
||||
var d = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, l] : [l]).filter(
|
||||
e => 'Signed' === e.Activity
|
||||
);
|
||||
let a = !1;
|
||||
!((o.Signers && 0 < o.Signers.length && d.length !== o.Signers.length) || !(a = !0));
|
||||
var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, {
|
||||
var m = (e =
|
||||
o.AuditTrail && 0 < o.AuditTrail.length
|
||||
? (-1 !==
|
||||
(d = (c = JSON.parse(JSON.stringify(o.AuditTrail))).findIndex(
|
||||
e => e.UserPtr.objectId === i && 'Created' !== e.Activity
|
||||
))
|
||||
? (c[d] = { ...c[d], ...p })
|
||||
: c.push(p),
|
||||
c)
|
||||
: [p]).filter(e => 'Signed' === e.Activity);
|
||||
let t = !1;
|
||||
!((o.Signers && 0 < o.Signers.length && m.length !== o.Signers.length) || !(t = !0));
|
||||
var g = { SignedUrl: r, AuditTrail: e, IsCompleted: t };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + a, g, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
return { isCompleted: a, message: 'success', AuditTrail: e };
|
||||
return { isCompleted: t, message: 'success', AuditTrail: e };
|
||||
} catch (e) {
|
||||
return console.log('update doc err ', e), 'err';
|
||||
}
|
||||
}
|
||||
async function sendCompletedMail(e) {
|
||||
var a = e.url,
|
||||
t = e.doc,
|
||||
s = e.doc.ExtUserPtr,
|
||||
r = t.Name,
|
||||
i = s.Email;
|
||||
let o = `Document "${r}" has been signed by all parties`,
|
||||
var t = e.url,
|
||||
a = e.doc,
|
||||
r = e.doc.ExtUserPtr,
|
||||
i = a.Name,
|
||||
s = r.Email;
|
||||
let o = `Document "${i}" has been signed by all parties`,
|
||||
n =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document signed successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document " +
|
||||
`<b>"${r}"</b>` +
|
||||
`<b>"${i}"</b>` +
|
||||
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
|
||||
s.Email +
|
||||
r.Email +
|
||||
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>';
|
||||
if (e?.isCustomMail)
|
||||
try {
|
||||
var l,
|
||||
d,
|
||||
c,
|
||||
d,
|
||||
p,
|
||||
m,
|
||||
g = new Parse.Query('partners_Tenant');
|
||||
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: s.UserId.objectId });
|
||||
const u = await g.first();
|
||||
u &&
|
||||
((l = JSON.parse(JSON.stringify(u)))?.CompletionSubject && (o = l?.CompletionSubject),
|
||||
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: r.UserId.objectId });
|
||||
const f = await g.first();
|
||||
f &&
|
||||
((l = JSON.parse(JSON.stringify(f)))?.CompletionSubject && (o = l?.CompletionSubject),
|
||||
l?.CompletionBody && (n = l?.CompletionBody),
|
||||
(d = t.ExpiryDate.iso),
|
||||
(c = new Date(d).toLocaleDateString('en-US', {
|
||||
(c = a.ExpiryDate.iso),
|
||||
(d = new Date(c).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})),
|
||||
(p = {
|
||||
document_title: r,
|
||||
sender_name: s.Name,
|
||||
sender_mail: s.Email,
|
||||
sender_phone: s.Phone,
|
||||
receiver_name: s.Name,
|
||||
receiver_email: s.Email,
|
||||
receiver_phone: s.Phone,
|
||||
expiry_date: c,
|
||||
company_name: s.Company,
|
||||
document_title: i,
|
||||
sender_name: r.Name,
|
||||
sender_mail: r.Email,
|
||||
sender_phone: r.Phone,
|
||||
receiver_name: r.Name,
|
||||
receiver_email: r.Email,
|
||||
receiver_phone: r.Phone,
|
||||
expiry_date: d,
|
||||
company_name: r.Company,
|
||||
}),
|
||||
(m = replaceMailVaribles(o, n, p)),
|
||||
(o = m.subject),
|
||||
@@ -95,12 +106,12 @@ async function sendCompletedMail(e) {
|
||||
console.log('error in fetch tenant in signpdf', e.message);
|
||||
}
|
||||
g = {
|
||||
extUserId: s.objectId,
|
||||
url: a,
|
||||
extUserId: r.objectId,
|
||||
url: t,
|
||||
from: 'OpenSign™',
|
||||
recipient: i,
|
||||
recipient: s,
|
||||
subject: o,
|
||||
pdfName: r,
|
||||
pdfName: i,
|
||||
html: n,
|
||||
mailProvider: e.mailProvider,
|
||||
};
|
||||
@@ -112,44 +123,40 @@ async function sendCompletedMail(e) {
|
||||
},
|
||||
});
|
||||
}
|
||||
async function sendDoctoWebhook(t, e, a, s) {
|
||||
let r = [];
|
||||
(r = s
|
||||
? { name: s?.Name, email: s?.Email, phone: s?.Phone }
|
||||
: t?.data?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
|
||||
{
|
||||
name: t?.data?.ExtUserPtr?.Name,
|
||||
email: t?.data?.ExtUserPtr?.Email,
|
||||
phone: t?.data?.ExtUserPtr?.Phone,
|
||||
},
|
||||
async function sendDoctoWebhook(a, e, t, r) {
|
||||
let i = [];
|
||||
(i = r
|
||||
? { name: r?.Name, email: r?.Email, phone: r?.Phone }
|
||||
: a?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
|
||||
{ name: a?.ExtUserPtr?.Name, email: a?.ExtUserPtr?.Email, phone: a?.ExtUserPtr?.Phone },
|
||||
]),
|
||||
t.data.ExtUserPtr?.Webhook &&
|
||||
((s =
|
||||
'signed' === a
|
||||
? { signer: r, signedAt: new Date() }
|
||||
: { signers: r, completedAt: new Date() }),
|
||||
(a = {
|
||||
event: a,
|
||||
objectId: t?.data?.objectId,
|
||||
a.ExtUserPtr?.Webhook &&
|
||||
((r =
|
||||
'signed' === t
|
||||
? { signer: i, signedAt: new Date() }
|
||||
: { signers: i, completedAt: new Date() }),
|
||||
(t = {
|
||||
event: t,
|
||||
objectId: a?.objectId,
|
||||
file: e || '',
|
||||
name: t?.data?.Name,
|
||||
note: t?.data?.Note || '',
|
||||
description: t?.data?.Description || '',
|
||||
...s,
|
||||
createdAt: t?.data?.createdAt,
|
||||
name: a?.Name,
|
||||
note: a?.Note || '',
|
||||
description: a?.Description || '',
|
||||
...r,
|
||||
createdAt: a?.createdAt,
|
||||
}),
|
||||
await axios
|
||||
.post(t?.data?.ExtUserPtr?.Webhook, a, { headers: { 'Content-Type': 'application/json' } })
|
||||
axios
|
||||
.post(a?.ExtUserPtr?.Webhook, t, { headers: { 'Content-Type': 'application/json' } })
|
||||
.then(e => {
|
||||
try {
|
||||
var a = new Parse.Object('contracts_Webhook');
|
||||
a.set('Log', e?.status),
|
||||
a.set('UserId', {
|
||||
var t = new Parse.Object('contracts_Webhook');
|
||||
t.set('Log', e?.status),
|
||||
t.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: t.data.ExtUserPtr.UserId.objectId,
|
||||
objectId: a.ExtUserPtr.UserId.objectId,
|
||||
}),
|
||||
a.save(null, { useMasterKey: !0 });
|
||||
t.save(null, { useMasterKey: !0 });
|
||||
} catch (e) {
|
||||
console.log('err save in contracts_Webhook', e.message);
|
||||
}
|
||||
@@ -157,109 +164,106 @@ async function sendDoctoWebhook(t, e, a, s) {
|
||||
.catch(e => {
|
||||
console.log('Err send data to webhook', e.message);
|
||||
try {
|
||||
var a = new Parse.Object('contracts_Webhook');
|
||||
a.set('Log', e?.status),
|
||||
a.set('UserId', {
|
||||
var t = new Parse.Object('contracts_Webhook');
|
||||
t.set('Log', e?.status),
|
||||
t.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: t.data.ExtUserPtr.UserId.objectId,
|
||||
objectId: a.ExtUserPtr.UserId.objectId,
|
||||
}),
|
||||
a.save(null, { useMasterKey: !0 });
|
||||
t.save(null, { useMasterKey: !0 });
|
||||
} catch (e) {
|
||||
console.log('err save in contracts_Webhook', e.message);
|
||||
}
|
||||
}));
|
||||
}
|
||||
const sendMailsaveCertifcate = async (e, t, a, r, i, s) => {
|
||||
var o = await GenerateCertificate(e),
|
||||
o = await PDFDocument.load(o),
|
||||
o =
|
||||
(pdflibAddPlaceholder({
|
||||
pdfDoc: o,
|
||||
reason: 'Digitally signed by OpenSign.',
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
await o.save()),
|
||||
o = Buffer.from(o),
|
||||
t = await new SignPDF(o, t).signPDF(),
|
||||
t =
|
||||
(fs.writeFileSync('./exports/certificate.pdf', t),
|
||||
await uploadFile('certificate.pdf', './exports/certificate.pdf')),
|
||||
n = { CertificateUrl: t.imageUrl };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + e.objectId, n, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
}),
|
||||
e.IsSendMail && !1 === e.IsSendMail
|
||||
? console.log("don't send mail")
|
||||
: sendCompletedMail({ url: a, isCustomMail: r, doc: e, mailProvider: i }),
|
||||
saveFileUsage(o.length, t.imageUrl, s),
|
||||
sendDoctoWebhook(e, a, 'completed');
|
||||
};
|
||||
async function PDF(o) {
|
||||
try {
|
||||
var n = o.params.docId,
|
||||
e = o.params.userId,
|
||||
l = o.params.isCustomCompletionMail || !1,
|
||||
d = o.params.mailProvider || '',
|
||||
c = await axios.get(
|
||||
serverUrl + '/classes/contracts_Document/' + n + '?include=ExtUserPtr,Signers',
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
}
|
||||
),
|
||||
p = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
});
|
||||
if (!p.data || !p.data.objectId) return { status: 'error', message: 'This user not allowed!' };
|
||||
if (!o?.user)
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
{
|
||||
var a,
|
||||
t,
|
||||
s,
|
||||
m = JSON.stringify({ objectId: e });
|
||||
let r, i;
|
||||
i = e
|
||||
? (a = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + m, {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
})).data && 0 < a.data.results.length
|
||||
? ((r = a), 'contracts_Contactbook')
|
||||
: ((r = await axios.get(serverUrl + '/classes/contracts_Users?where=' + m, {
|
||||
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
|
||||
})),
|
||||
'contracts_Users')
|
||||
: ((t = JSON.stringify({
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: p.data.objectId },
|
||||
})),
|
||||
(s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + t, {
|
||||
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
|
||||
})).data && 0 < s.data.results.length
|
||||
? ((r = s), 'contracts_Users')
|
||||
: ((r = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + t, {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
})),
|
||||
'contracts_Contactbook'));
|
||||
var g = r.data.results[0].Name,
|
||||
u = r.data.results[0].Email;
|
||||
if (!o.params.pdfFile) return { status: 'error', message: 'Pdf file not present!' };
|
||||
var n = o?.user?.toJSON(),
|
||||
e = o.params.docId;
|
||||
const F = o.params.userId;
|
||||
var l = o.params.isCustomCompletionMail || !1,
|
||||
c = o.params.mailProvider || '',
|
||||
d = o.params.signature || '',
|
||||
t = new Parse.Query('contracts_Document'),
|
||||
a =
|
||||
(t.include('ExtUserPtr,Signers'),
|
||||
t.equalTo('objectId', e),
|
||||
await t.first({ useMasterKey: !0 }));
|
||||
if (!a) throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||
var r,
|
||||
p = a?.toJSON();
|
||||
let i, s;
|
||||
F
|
||||
? ((r = p.Signers.find(e => e.objectId === F)),
|
||||
console.log('_contractUser ', r),
|
||||
r && ((i = r), (s = 'contracts_Contactbook')))
|
||||
: ((s = 'contracts_Users'), (i = p.ExtUserPtr));
|
||||
var m,
|
||||
g = i.Name,
|
||||
f = i.Email;
|
||||
if (!o.params.pdfFile) throw (((m = new Error('Pdf file not present!')).code = 400), m);
|
||||
{
|
||||
let e = Buffer.from(o.params.pdfFile, 'base64');
|
||||
var h = process.env.PFX_BASE64,
|
||||
f = Buffer.from(h, 'base64'),
|
||||
P = {
|
||||
UserPtr: { __type: 'Pointer', className: i, objectId: r.data.results[0].objectId },
|
||||
var u = process.env.PFX_BASE64,
|
||||
h = Buffer.from(u, 'base64'),
|
||||
y = {
|
||||
UserPtr: { __type: 'Pointer', className: s, objectId: i.objectId },
|
||||
SignedUrl: '',
|
||||
Activity: 'Signed',
|
||||
ipAddress: o.headers['x-real-ip'],
|
||||
};
|
||||
let a;
|
||||
var y = (a =
|
||||
c.data.AuditTrail && 0 < c.data.AuditTrail.length
|
||||
? [...c.data.AuditTrail, P]
|
||||
: [P]).filter(e => 'Signed' === e.Activity);
|
||||
let t = !1;
|
||||
!(
|
||||
(c.data.Signers && 0 < c.data.Signers.length && y.length !== c.data.Signers.length) ||
|
||||
!(t = !0)
|
||||
let t;
|
||||
var P = (t = p.AuditTrail && 0 < p.AuditTrail.length ? [...p.AuditTrail, y] : [y]).filter(
|
||||
e => 'Signed' === e.Activity
|
||||
);
|
||||
let a = !1;
|
||||
!((p.Signers && 0 < p.Signers.length && P.length !== p.Signers.length) || !(a = !0));
|
||||
var v,
|
||||
b,
|
||||
U,
|
||||
I,
|
||||
S,
|
||||
w,
|
||||
U,
|
||||
D,
|
||||
S = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
|
||||
_ = './exports/' + S;
|
||||
let s = e.length;
|
||||
s = (
|
||||
t
|
||||
? ((v = c.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
|
||||
I = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
|
||||
_ = './exports/' + I;
|
||||
let r = e.length;
|
||||
r = (
|
||||
a
|
||||
? ((v = p.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
|
||||
(e =
|
||||
v && 0 < v.length
|
||||
? ((b = await PDFDocument.load(e)),
|
||||
@@ -269,97 +273,53 @@ async function PDF(o) {
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(U = await b.save()),
|
||||
Buffer.from(U))
|
||||
: ((I = await PDFDocument.load(e)),
|
||||
(S = await b.save()),
|
||||
Buffer.from(S))
|
||||
: ((w = await PDFDocument.load(e)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: I,
|
||||
reason: 'Digitally signed by OpenSign for ' + g + ' <' + u + '>',
|
||||
pdfDoc: w,
|
||||
reason: 'Digitally signed by OpenSign for ' + g + ' <' + f + '>',
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(w = await I.save()),
|
||||
Buffer.from(w))),
|
||||
(D = await new SignPDF(e, f).signPDF()),
|
||||
(U = await w.save()),
|
||||
Buffer.from(U))),
|
||||
(D = await new SignPDF(e, h).signPDF()),
|
||||
fs.writeFileSync(_, D),
|
||||
D)
|
||||
: (fs.writeFileSync(_, e), e)
|
||||
).length;
|
||||
var A,
|
||||
x,
|
||||
E,
|
||||
j,
|
||||
k,
|
||||
F,
|
||||
C,
|
||||
T,
|
||||
N,
|
||||
M = await uploadFile(S, _);
|
||||
if (M && M.imageUrl)
|
||||
return (
|
||||
(A = await updateDoc(
|
||||
var E = await uploadFile(I, _);
|
||||
if (E && E.imageUrl) {
|
||||
var x,
|
||||
A,
|
||||
j = await updateDoc(
|
||||
o.params.docId,
|
||||
M.imageUrl,
|
||||
r.data.results[0].objectId,
|
||||
E.imageUrl,
|
||||
i.objectId,
|
||||
o.headers['x-real-ip'],
|
||||
c.data,
|
||||
i
|
||||
)),
|
||||
sendDoctoWebhook(c, M.imageUrl, 'signed', r?.data.results?.[0]),
|
||||
saveFileUsage(s, M.imageUrl, p.data.objectId),
|
||||
A &&
|
||||
A.isCompleted &&
|
||||
((x = { ...c.data, AuditTrail: A.AuditTrail }),
|
||||
(E = await GenerateCertificate(x)),
|
||||
(j = await PDFDocument.load(E)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: j,
|
||||
reason: 'Digitally signed by OpenSign.',
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(k = await j.save()),
|
||||
(F = Buffer.from(k)),
|
||||
(C = await new SignPDF(F, f).signPDF()),
|
||||
fs.writeFileSync('./exports/certificate.pdf', C),
|
||||
(N = {
|
||||
CertificateUrl: (T = await uploadFile(
|
||||
'certificate.pdf',
|
||||
'./exports/certificate.pdf'
|
||||
)).imageUrl,
|
||||
}),
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + n, N, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
}),
|
||||
c.data.IsSendMail && !1 === c.data.IsSendMail
|
||||
? console.log("don't send mail")
|
||||
: sendCompletedMail({
|
||||
url: M.imageUrl,
|
||||
isCustomMail: l,
|
||||
doc: c.data,
|
||||
mailProvider: d,
|
||||
}),
|
||||
saveFileUsage(F.length, T.imageUrl, p.data.objectId),
|
||||
sendDoctoWebhook(c, M.imageUrl, 'completed')),
|
||||
p,
|
||||
s,
|
||||
d
|
||||
);
|
||||
if (
|
||||
(sendDoctoWebhook(p, E.imageUrl, 'signed', i),
|
||||
saveFileUsage(r, E.imageUrl, n.objectId),
|
||||
j &&
|
||||
j.isCompleted &&
|
||||
((x = { ...p, AuditTrail: j.AuditTrail }),
|
||||
sendMailsaveCertifcate(x, h, E.imageUrl, l, c, n.objectId)),
|
||||
fs.unlinkSync(_),
|
||||
console.log('New Signed PDF created called: ' + _),
|
||||
'success' === A.message
|
||||
? { status: 'success', data: M.imageUrl }
|
||||
: { status: 'error', message: 'Please provide required parameters!' }
|
||||
);
|
||||
'success' === j.message)
|
||||
)
|
||||
return { status: 'success', data: E.imageUrl };
|
||||
throw (((A = new Error('Please provide required parameters!')).code = 400), A);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return (
|
||||
console.log('Err ', e),
|
||||
'ERR_BAD_REQUEST' === e.code
|
||||
? { status: 'error', message: 'Invalid session token!' }
|
||||
: { status: 'error', message: 'Encrypted files are currently not supported!' }
|
||||
);
|
||||
throw (console.log('Err in signpdf', e), e);
|
||||
}
|
||||
}
|
||||
export default PDF;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
export default async function saveSubscriptio(request) {
|
||||
export default async function saveSubscription(request) {
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const subscription = request.params.subscription;
|
||||
|
||||
@@ -4,6 +4,7 @@ import formData from 'form-data';
|
||||
import Mailgun from 'mailgun.js';
|
||||
import { updateMailCount } from '../../Utils.js';
|
||||
import sendMailGmailProvider from './sendMailGmailProvider.js';
|
||||
import { createTransport } from 'nodemailer';
|
||||
async function sendMailProvider(req) {
|
||||
try {
|
||||
let transporterSMTP;
|
||||
|
||||
@@ -166,11 +166,6 @@ app.use(cors());
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
||||
app.use(function (req, res, next) {
|
||||
// console.log("req ", req.headers);
|
||||
// console.log("x-forwarded-for", req.headers["x-forwarded-for"]);
|
||||
// console.log("req.ip", req.ip);
|
||||
// console.log("req.socket.remoteAddress; ", req.socket.remoteAddress);
|
||||
// console.log("ip", ip.address());
|
||||
req.headers['x-real-ip'] = getUserIP(req);
|
||||
next();
|
||||
});
|
||||
@@ -230,7 +225,7 @@ if (!process.env.TESTING) {
|
||||
// Set the Keep-Alive and headers timeout to 100 seconds
|
||||
httpServer.keepAliveTimeout = 100000; // in milliseconds
|
||||
httpServer.headersTimeout = 100000; // in milliseconds
|
||||
httpServer.listen(port, function () {
|
||||
httpServer.listen(port, '0.0.0.0', function () {
|
||||
console.log('parse-server-example running on port ' + port + '.');
|
||||
const isWindows = process.platform === 'win32';
|
||||
// console.log('isWindows', isWindows);
|
||||
|
||||
Generated
+23
-23
@@ -14,19 +14,19 @@
|
||||
"axios": "^1.6.8",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.3",
|
||||
"express": "^4.19.2",
|
||||
"express-sse": "^0.5.3",
|
||||
"form-data": "^4.0.0",
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^134.0.0",
|
||||
"jsonschema": "^1.4.1",
|
||||
"mailgun.js": "^10.2.1",
|
||||
"mongoose": "^8.2.2",
|
||||
"mongoose": "^8.3.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-signpdf": "^1.5.1",
|
||||
"nodemailer": "^6.9.12",
|
||||
"nodemailer": "^6.9.13",
|
||||
"parse": "^5.0.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^6.5.5",
|
||||
@@ -4123,9 +4123,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bson": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz",
|
||||
"integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==",
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz",
|
||||
"integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==",
|
||||
"engines": {
|
||||
"node": ">=16.20.1"
|
||||
}
|
||||
@@ -7595,9 +7595,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/kareem": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
|
||||
"integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
|
||||
"integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
@@ -7961,12 +7961,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongodb": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz",
|
||||
"integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz",
|
||||
"integrity": "sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==",
|
||||
"dependencies": {
|
||||
"@mongodb-js/saslprep": "^1.1.0",
|
||||
"bson": "^6.2.0",
|
||||
"@mongodb-js/saslprep": "^1.1.5",
|
||||
"bson": "^6.4.0",
|
||||
"mongodb-connection-string-url": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8312,13 +8312,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz",
|
||||
"integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==",
|
||||
"version": "8.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.3.3.tgz",
|
||||
"integrity": "sha512-3kSk0db9DM2tLttCdS6WNRqewPleamFEa4Vz/Qldc0dB4Zow/FiZxb9GExHTJjBZQ9T2xiGleQ3GzRrES3hhsA==",
|
||||
"dependencies": {
|
||||
"bson": "^6.2.0",
|
||||
"kareem": "2.5.1",
|
||||
"mongodb": "6.3.0",
|
||||
"bson": "^6.5.0",
|
||||
"kareem": "2.6.3",
|
||||
"mongodb": "6.5.0",
|
||||
"mpath": "0.9.0",
|
||||
"mquery": "5.0.0",
|
||||
"ms": "2.1.3",
|
||||
@@ -8503,9 +8503,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.9.12",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz",
|
||||
"integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==",
|
||||
"version": "6.9.13",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz",
|
||||
"integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
|
||||
@@ -23,19 +23,19 @@
|
||||
"axios": "^1.6.8",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.3",
|
||||
"express": "^4.19.2",
|
||||
"express-sse": "^0.5.3",
|
||||
"form-data": "^4.0.0",
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^134.0.0",
|
||||
"jsonschema": "^1.4.1",
|
||||
"mailgun.js": "^10.2.1",
|
||||
"mongoose": "^8.2.2",
|
||||
"mongoose": "^8.3.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-signpdf": "^1.5.1",
|
||||
"nodemailer": "^6.9.12",
|
||||
"nodemailer": "^6.9.13",
|
||||
"parse": "^5.0.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^6.5.5",
|
||||
|
||||
Reference in New Issue
Block a user