diff --git a/apps/OpenSign/package-lock.json b/apps/OpenSign/package-lock.json index db904d0d3a..cf882c1383 100644 --- a/apps/OpenSign/package-lock.json +++ b/apps/OpenSign/package-lock.json @@ -12,7 +12,6 @@ "@lottiefiles/dotlottie-react": "^0.13.2", "@pdf-lib/fontkit": "^1.1.1", "@radix-ui/themes": "^3.1.6", - "@react-pdf/renderer": "^4.3.0", "@reduxjs/toolkit": "^2.5.1", "axios": "^1.8.4", "css-minimizer-webpack-plugin": "^7.0.2", @@ -6107,174 +6106,6 @@ "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, - "node_modules/@react-pdf/fns": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz", - "integrity": "sha512-qTKGUf0iAMGg2+OsUcp9ffKnKi41RukM/zYIWMDJ4hRVYSr89Q7e3wSDW/Koqx3ea3Uy/z3h2y3wPX6Bdfxk6g==", - "license": "MIT" - }, - "node_modules/@react-pdf/font": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.2.tgz", - "integrity": "sha512-/dAWu7Y2RD1RxarDZ9SkYPHgBYOhmcDnet4W/qN/m8k+A2Hr3ja54GymSR7GGxWBtxjKtNauVKrTa9LS1n8WUw==", - "license": "MIT", - "dependencies": { - "@react-pdf/pdfkit": "^4.0.3", - "@react-pdf/types": "^2.9.0", - "fontkit": "^2.0.2", - "is-url": "^1.2.4" - } - }, - "node_modules/@react-pdf/image": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.0.3.tgz", - "integrity": "sha512-lvP5ryzYM3wpbO9bvqLZYwEr5XBDX9jcaRICvtnoRqdJOo7PRrMnmB4MMScyb+Xw10mGeIubZAAomNAG5ONQZQ==", - "license": "MIT", - "dependencies": { - "@react-pdf/png-js": "^3.0.0", - "jay-peg": "^1.1.1" - } - }, - "node_modules/@react-pdf/layout": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.4.0.tgz", - "integrity": "sha512-Aq+Cc6JYausWLoks2FvHe3PwK9cTuvksB2uJ0AnkKJEUtQbvCq8eCRb1bjbbwIji9OzFRTTzZij7LzkpKHjIeA==", - "license": "MIT", - "dependencies": { - "@react-pdf/fns": "3.1.2", - "@react-pdf/image": "^3.0.3", - "@react-pdf/primitives": "^4.1.1", - "@react-pdf/stylesheet": "^6.1.0", - "@react-pdf/textkit": "^6.0.0", - "@react-pdf/types": "^2.9.0", - "emoji-regex": "^10.3.0", - "queue": "^6.0.1", - "yoga-layout": "^3.2.1" - } - }, - "node_modules/@react-pdf/pdfkit": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-4.0.3.tgz", - "integrity": "sha512-k+Lsuq8vTwWsCqTp+CCB4+2N+sOTFrzwGA7aw3H9ix/PDWR9QksbmNg0YkzGbLAPI6CeawmiLHcf4trZ5ecLPQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "@react-pdf/png-js": "^3.0.0", - "browserify-zlib": "^0.2.0", - "crypto-js": "^4.2.0", - "fontkit": "^2.0.2", - "jay-peg": "^1.1.1", - "linebreak": "^1.1.0", - "vite-compatible-readable-stream": "^3.6.1" - } - }, - "node_modules/@react-pdf/png-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-pdf/png-js/-/png-js-3.0.0.tgz", - "integrity": "sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==", - "license": "MIT", - "dependencies": { - "browserify-zlib": "^0.2.0" - } - }, - "node_modules/@react-pdf/primitives": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.1.1.tgz", - "integrity": "sha512-IuhxYls1luJb7NUWy6q5avb1XrNaVj9bTNI40U9qGRuS6n7Hje/8H8Qi99Z9UKFV74bBP3DOf3L1wV2qZVgVrQ==", - "license": "MIT" - }, - "node_modules/@react-pdf/reconciler": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-1.1.4.tgz", - "integrity": "sha512-oTQDiR/t4Z/Guxac88IavpU2UgN7eR0RMI9DRKvKnvPz2DUasGjXfChAdMqDNmJJxxV26mMy9xQOUV2UU5/okg==", - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.1", - "scheduler": "0.25.0-rc-603e6108-20241029" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-pdf/render": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.3.0.tgz", - "integrity": "sha512-MdWfWaqO6d7SZD75TZ2z5L35V+cHpyA43YNRlJNG0RJ7/MeVGDQv12y/BXOJgonZKkeEGdzM3EpAt9/g4E22WA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "@react-pdf/fns": "3.1.2", - "@react-pdf/primitives": "^4.1.1", - "@react-pdf/textkit": "^6.0.0", - "@react-pdf/types": "^2.9.0", - "abs-svg-path": "^0.1.1", - "color-string": "^1.9.1", - "normalize-svg-path": "^1.1.0", - "parse-svg-path": "^0.1.2", - "svg-arc-to-cubic-bezier": "^3.2.0" - } - }, - "node_modules/@react-pdf/renderer": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.3.0.tgz", - "integrity": "sha512-28gpA69fU9ZQrDzmd5xMJa1bDf8t0PT3ApUKBl2PUpoE/x4JlvCB5X66nMXrfFrgF2EZrA72zWQAkvbg7TE8zw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.13", - "@react-pdf/fns": "3.1.2", - "@react-pdf/font": "^4.0.2", - "@react-pdf/layout": "^4.4.0", - "@react-pdf/pdfkit": "^4.0.3", - "@react-pdf/primitives": "^4.1.1", - "@react-pdf/reconciler": "^1.1.4", - "@react-pdf/render": "^4.3.0", - "@react-pdf/types": "^2.9.0", - "events": "^3.3.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "queue": "^6.0.1" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@react-pdf/stylesheet": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.1.0.tgz", - "integrity": "sha512-BGZ2sYNUp38VJUegjva/jsri3iiRGnVNjWI+G9dTwAvLNOmwFvSJzqaCsEnqQ/DW5mrTBk/577FhDY7pv6AidA==", - "license": "MIT", - "dependencies": { - "@react-pdf/fns": "3.1.2", - "@react-pdf/types": "^2.9.0", - "color-string": "^1.9.1", - "hsl-to-hex": "^1.0.0", - "media-engine": "^1.0.3", - "postcss-value-parser": "^4.1.0" - } - }, - "node_modules/@react-pdf/textkit": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.0.0.tgz", - "integrity": "sha512-fDt19KWaJRK/n2AaFoVm31hgGmpygmTV7LsHGJNGZkgzXcFyLsx+XUl63DTDPH3iqxj3xUX128t104GtOz8tTw==", - "license": "MIT", - "dependencies": { - "@react-pdf/fns": "3.1.2", - "bidi-js": "^1.0.2", - "hyphen": "^1.6.4", - "unicode-properties": "^1.4.1" - } - }, - "node_modules/@react-pdf/types": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.9.0.tgz", - "integrity": "sha512-ckj80vZLlvl9oYrQ4tovEaqKWP3O06Eb1D48/jQWbdwz1Yh7Y9v1cEmwlP8ET+a1Whp8xfdM0xduMexkuPANCQ==", - "license": "MIT", - "dependencies": { - "@react-pdf/font": "^4.0.2", - "@react-pdf/primitives": "^4.1.1", - "@react-pdf/stylesheet": "^6.1.0" - } - }, "node_modules/@reduxjs/toolkit": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.1.tgz", @@ -7534,12 +7365,6 @@ "optional": true, "peer": true }, - "node_modules/abs-svg-path": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz", - "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==", - "license": "MIT" - }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -8429,6 +8254,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "devOptional": true, "funding": [ { "type": "github", @@ -8464,15 +8290,6 @@ "node": ">= 8.0.0" } }, - "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "license": "MIT", - "dependencies": { - "require-from-string": "^2.0.2" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -8727,29 +8544,11 @@ "node": ">=8" } }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, "node_modules/browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "license": "MIT", - "dependencies": { - "pako": "~1.0.5" - } - }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -9455,15 +9254,6 @@ "node": ">=12" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -9535,16 +9325,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -9952,7 +9732,8 @@ "node_modules/crypto-js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "optional": true }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -11062,12 +10843,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -11360,7 +11135,8 @@ "node_modules/emoji-regex": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true }, "node_modules/emojis-list": { "version": "3.0.0", @@ -12689,23 +12465,6 @@ } } }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", - "license": "MIT", - "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -13559,21 +13318,6 @@ "wbuf": "^1.1.0" } }, - "node_modules/hsl-to-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz", - "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==", - "license": "MIT", - "dependencies": { - "hsl-to-rgb-for-reals": "^1.1.0" - } - }, - "node_modules/hsl-to-rgb-for-reals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz", - "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==", - "license": "ISC" - }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -13790,12 +13534,6 @@ "node": ">=16.17.0" } }, - "node_modules/hyphen": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.10.6.tgz", - "integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==", - "license": "ISC" - }, "node_modules/i18next": { "version": "23.16.8", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", @@ -14717,12 +14455,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-url": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", - "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", - "license": "MIT" - }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -15043,15 +14775,6 @@ "node": ">=8" } }, - "node_modules/jay-peg": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz", - "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==", - "license": "MIT", - "dependencies": { - "restructure": "^3.0.0" - } - }, "node_modules/jest": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", @@ -17517,25 +17240,6 @@ "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/linebreak": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", - "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", - "license": "MIT", - "dependencies": { - "base64-js": "0.0.8", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/linebreak/node_modules/base64-js": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", - "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -18150,12 +17854,6 @@ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" }, - "node_modules/media-engine": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz", - "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==", - "license": "MIT" - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -18624,15 +18322,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-svg-path": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", - "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==", - "license": "MIT", - "dependencies": { - "svg-arc-to-cubic-bezier": "^3.0.0" - } - }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -19193,12 +18882,6 @@ "node": ">=0.10.0" } }, - "node_modules/parse-svg-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz", - "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==", - "license": "MIT" - }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -21186,15 +20869,6 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -24284,12 +23958,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", - "license": "MIT" - }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -24549,12 +24217,6 @@ "node": ">=10" } }, - "node_modules/scheduler": { - "version": "0.25.0-rc-603e6108-20241029", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz", - "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==", - "license": "MIT" - }, "node_modules/schema-utils": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", @@ -25060,21 +24722,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -25871,12 +25518,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-arc-to-cubic-bezier": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz", - "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==", - "license": "ISC" - }, "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", @@ -26307,12 +25948,6 @@ "resolved": "https://registry.npmjs.org/timezone-soft/-/timezone-soft-1.5.2.tgz", "integrity": "sha512-BUr+CfBfeWXJwFAuEzPO9uF+v6sy3pL5SKLkDg4vdEhsyXgbBnpFoBCW8oEKSNTqNq9YHbVOjNb31xE7WyGmrA==" }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -26699,16 +26334,6 @@ "node": ">=4" } }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", @@ -26717,22 +26342,6 @@ "node": ">=4" } }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unicode-trie/node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "license": "MIT" - }, "node_modules/unique-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", @@ -26987,20 +26596,6 @@ "node": ">= 0.8" } }, - "node_modules/vite-compatible-readable-stream": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz", - "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -28136,12 +27731,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoga-layout": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", - "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==", - "license": "MIT" - }, "node_modules/zoom-level": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/zoom-level/-/zoom-level-2.5.0.tgz", diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index e746345a93..c0ec28cd42 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -7,7 +7,6 @@ "@lottiefiles/dotlottie-react": "^0.13.2", "@pdf-lib/fontkit": "^1.1.1", "@radix-ui/themes": "^3.1.6", - "@react-pdf/renderer": "^4.3.0", "@reduxjs/toolkit": "^2.5.1", "axios": "^1.8.4", "css-minimizer-webpack-plugin": "^7.0.2", diff --git a/apps/OpenSign/public/locales/de/translation.json b/apps/OpenSign/public/locales/de/translation.json index 1f32e39afd..019dbd7ed0 100644 --- a/apps/OpenSign/public/locales/de/translation.json +++ b/apps/OpenSign/public/locales/de/translation.json @@ -151,7 +151,8 @@ "extend-expiry-date": "Ablaufdatum verlängern", "Duplicate Template": "Vorlage duplizieren", "Duplicate": "Duplikat", - "daily-mail-quota": "Tägliches E-Mail-Kontingent" + "daily-mail-quota": "Tägliches E-Mail-Kontingent", + "Save as template": "Als Vorlage speichern" }, "report-heading": { "Sr.No": "Nr.", @@ -308,7 +309,7 @@ "revoke-document": "Dokument widerrufen", "revoke-document-alert": "Sind Sie sicher, dass Sie dieses Dokument widerrufen möchten?", "resend-mail": "E-Mail erneut senden", - "resend-mail-help": "Sie können folgende Variablen verwenden, die durch ihre tatsächlichen Werte ersetzt werden: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.", + "resend-mail-help": "Sie können folgende Variablen verwenden, die durch ihre tatsächlichen Werte ersetzt werden: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "subject": "Betreff", "body": "Inhalt", "add-contact": "Kontakt hinzufügen", @@ -522,7 +523,7 @@ "correct-password": "Bitte korrektes Passwort angeben", "decrypting-pdf": "PDF wird entschlüsselt, bitte warten...", "invalid-otp": "Ungültiger OTP", - "user-not-found": "Benutzer nicht gefunden!", + "user-not-found": "Benutzer nicht gefunden", "enter-otp-alert": "Bitte OTP eingeben!", "get-verification-code": "Verifizierungscode erhalten", "get-verification-code-2": "Sie erhalten einen Verifizierungscode per E-Mail", @@ -858,10 +859,19 @@ "indexing-public-profile": "Erlaube die Indexierung des öffentlichen Profils durch Suchmaschinen", "user-created-successfully": "Benutzer erfolgreich erstellt.", "only-15-reminder-allowed": "Sie können bis zu 15 automatische Erinnerungen festlegen. Wenn zum Beispiel 'TimeToComplete' auf 15 Tage und 'RemindOnceInEvery' auf 1 Tag eingestellt ist, erreichen Sie das maximale Limit von 15 Erinnerungen. Passen Sie Ihre Einstellungen entsprechend an.", - "rate-your-experience": "👋 Bitte bewerten Sie Ihre Erfahrung mit Opensign", + "rate-your-experience": "Wie war Ihre Erfahrung mit {{appName}}?", "thanks-for-feedback": "Danke für Ihr Feedback 🙏", "share-your-feedback": "Teilen Sie Ihr Feedback", "share-your-review": "Teilen Sie Ihre Bewertung", "date-format": "Datumsformat", - "document-deleted": "Das Dokument wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender." + "document-deleted": "Das Dokument wurde gelöscht oder Sie haben keinen Zugriff. Bitte kontaktieren Sie den Absender.", + "save-as-template-?": "Sind Sie sicher, dass Sie dieses Dokument als Vorlage speichern möchten?", + "go-to-manage-templates": "Zu 'Vorlagen verwalten' gehen", + "template-created": "Vorlage erstellt", + "how-would-you-like-to-proceed?": "Wie möchten Sie fortfahren?", + "failed-to-load-refresh-page": "Fehler beim Laden des Dokuments. Bitte versuchen Sie, diese Seite zu aktualisieren.", + "document-has-been-signed": "Das Dokument wurde erfolgreich unterschrieben!", + "document-has-been-signed-by-you": "Das Dokument wurde erfolgreich von Ihnen unterschrieben!", + "participant-completed-signing": "Alle Teilnehmer haben den Signaturprozess abgeschlossen.", + "you-will-receive-email-shortly": "✅ Das war's! Sie erhalten in Kürze eine Bestätigungs-E-Mail." } diff --git a/apps/OpenSign/public/locales/en/translation.json b/apps/OpenSign/public/locales/en/translation.json index fe851284e1..1140b25b98 100644 --- a/apps/OpenSign/public/locales/en/translation.json +++ b/apps/OpenSign/public/locales/en/translation.json @@ -151,7 +151,8 @@ "extend-expiry-date": "Extend expiry date", "Duplicate Template": "Duplicate template", "Duplicate": "Duplicate", - "daily-mail-quota": "Daily Email Quota" + "daily-mail-quota": "Daily Email Quota", + "Save as template": "Save as template" }, "report-heading": { "Sr.No": "Sr.No", @@ -308,7 +309,7 @@ "revoke-document": "Revoke document", "revoke-document-alert": "Are you sure you want to revoke this document?", "resend-mail": "Resend mail", - "resend-mail-help": "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.", + "resend-mail-help": "You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "subject": "Subject", "body": "Body", "add-contact": "Add contact", @@ -522,7 +523,7 @@ "correct-password": "Please provide correct password", "decrypting-pdf": " Decrypting pdf please wait...", "invalid-otp": "Invalid otp", - "user-not-found": "User not found!", + "user-not-found": "User not found", "enter-otp-alert": "Please enter OTP!", "get-verification-code": "Get verification code", "get-verification-code-2": "You will get a verification code via Email", @@ -858,10 +859,19 @@ "indexing-public-profile": "Allow indexing of public profile by search engines", "user-created-successfully": "user created successfully.", "only-15-reminder-allowed": "You can set up to 15 automatic reminders. For example, if 'TimeToComplete' is 15 days and 'RemindOnceInEvery' is 1 day, you'll reach the maximum limit of 15 reminders. Adjust your settings accordingly.", - "rate-your-experience": "👋 Please rate your experience with Opensign", + "rate-your-experience": "How was your experience with {{appName}}?", "thanks-for-feedback": "Thanks for your feedback 🙏", "share-your-feedback": "Share your feedback", "share-your-review": "Share your review", "date-format": "Date format", - "document-deleted": "The document has been deleted or you don't have access. Please contact the sender." + "document-deleted": "The document has been deleted or you don't have access. Please contact the sender.", + "save-as-template-?": "Are you sure you want to save this document as template?", + "go-to-manage-templates": "go to 'Manage templates'", + "template-created": "Template Created", + "how-would-you-like-to-proceed?": "How would you like to proceed?", + "failed-to-load-refresh-page": "Failed to load the document. Please try refreshing this page.", + "document-has-been-signed": "The document has been signed successfully!", + "document-has-been-signed-by-you": "The document has been successfully signed by you!", + "participant-completed-signing": "All participants have completed the signing process.", + "you-will-receive-email-shortly": "✅ That's it! You'll receive a confirmation email shortly." } diff --git a/apps/OpenSign/public/locales/es/translation.json b/apps/OpenSign/public/locales/es/translation.json index 3c3c8b3b1c..93cd2bcfef 100644 --- a/apps/OpenSign/public/locales/es/translation.json +++ b/apps/OpenSign/public/locales/es/translation.json @@ -151,7 +151,8 @@ "extend-expiry-date": "Date d'expiration", "Duplicate Template": "Plantilla duplicada", "Duplicate": "Duplicada", - "daily-mail-quota": "Cuota diaria de correos electrónicos" + "daily-mail-quota": "Cuota diaria de correos electrónicos", + "Save as template": "Guardar como plantilla" }, "report-heading": { "Sr.No": "Nº", @@ -309,7 +310,7 @@ "revoke-document": "Revocar documento", "revoke-document-alert": "¿En definitiva quieres revocar este documento?", "resend-mail": "Reenviar correo", - "resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.", + "resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "subject": "Asunto", "body": "Cuerpo", "add-contact": "Agregar contacto", @@ -522,7 +523,7 @@ "correct-password": "Por favor, proporciona la contraseña correcta", "decrypting-pdf": " Desencriptando PDF, por favor, espera...", "invalid-otp": "OTP inválido", - "user-not-found": "¡Usuario no encontrado!", + "user-not-found": "Usuario no encontrado", "enter-otp-alert": "¡Por favor, ingresa el OTP!", "get-verification-code": "Obtener código de verificación", "get-verification-code-2": "Obtendrás un código de verificación por correo", @@ -858,10 +859,19 @@ "indexing-public-profile": "Permitir la indexación del perfil público por los motores de búsqueda", "user-created-successfully": "Usuario creado con éxito.", "only-15-reminder-allowed": "Puede configurar hasta 15 recordatorios automáticos. Por ejemplo, si 'TimeToComplete' es de 15 días y 'RemindOnceInEvery' es de 1 día, alcanzará el límite máximo de 15 recordatorios. Ajuste su configuración en consecuencia.", - "rate-your-experience": "👋 Por favor, califique su experiencia con Opensign", + "rate-your-experience": "¿Cómo fue su experiencia con {{appName}}?", "thanks-for-feedback": "Gracias por su comentario 🙏", "share-your-feedback": "Comparta sus comentarios", "share-your-review": "Comparta su reseña", "date-format": "Formato de fecha", - "document-deleted": "El documento ha sido eliminado o no tiene acceso. Por favor, contacte al remitente." + "document-deleted": "El documento ha sido eliminado o no tiene acceso. Por favor, contacte al remitente.", + "save-as-template-?": "¿Está seguro de que desea guardar este documento como plantilla?", + "go-to-manage-templates": "Ir a 'Gestionar plantillas'", + "template-created": "Plantilla creada", + "how-would-you-like-to-proceed?": "¿Cómo le gustaría proceder?", + "failed-to-load-refresh-page": "Error al cargar el documento. Intente actualizar esta página.", + "document-has-been-signed": "¡El documento ha sido firmado con éxito!", + "document-has-been-signed-by-you": "¡El documento ha sido firmado con éxito por usted!", + "participant-completed-signing": "Todos los participantes han completado el proceso de firma.", + "you-will-receive-email-shortly": "✅ ¡Eso es todo! Recibirá un correo de confirmación en breve." } diff --git a/apps/OpenSign/public/locales/fr/translation.json b/apps/OpenSign/public/locales/fr/translation.json index c12464d501..54ef4ab4b1 100644 --- a/apps/OpenSign/public/locales/fr/translation.json +++ b/apps/OpenSign/public/locales/fr/translation.json @@ -172,7 +172,8 @@ "extend-expiry-date": "Prolonger la date d'expiration", "Duplicate Template": "dupliquer le modèle", "Duplicate": "Double", - "daily-mail-quota": "Quota d'e-mails quotidien" + "daily-mail-quota": "Quota d'e-mails quotidien", + "Save as template": "Enregistrer comme modèle" }, "report-help": { "Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.", @@ -308,7 +309,7 @@ "revoke-document": "Révoquer le document", "revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?", "resend-mail": "Renvoyer le courrier", - "resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.", + "resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "subject": "Sujet", "body": "Corps", "add-contact": "Ajouter le contact", @@ -522,7 +523,7 @@ "correct-password": "Veuillez fournir un mot de passe correct", "decrypting-pdf": "Décryptage du pdf, veuillez patienter...", "invalid-otp": "OTP invalide", - "user-not-found": "Utilisateur non trouvé!", + "user-not-found": "Utilisateur non trouvé", "enter-otp-alert": "Veuillez saisir OTP !", "get-verification-code": "Obtenir le code de vérification", "get-verification-code-2": "Vous recevrez un code de vérification par e-mail", @@ -858,10 +859,19 @@ "indexing-public-profile": "Autoriser l'indexation du profil public par les moteurs de recherche", "user-created-successfully": "Utilisateur créé avec succès.", "only-15-reminder-allowed": "Vous pouvez définir jusqu'à 15 rappels automatiques. Par exemple, si 'TimeToComplete' est de 15 jours et 'RemindOnceInEvery' est de 1 jour, vous atteindrez la limite maximale de 15 rappels. Ajustez vos paramètres en conséquence.", - "rate-your-experience": "👋 Veuillez évaluer votre expérience avec Opensign", + "rate-your-experience": "Comment s'est passée votre expérience avec {{appName}} ?", "thanks-for-feedback": "Merci pour votre retour 🙏", "share-your-feedback": "Partagez votre avis", "share-your-review": "Partagez votre avis", "date-format": "Format de date", - "document-deleted": "Le document a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur." + "document-deleted": "Le document a été supprimé ou vous n'y avez pas accès. Veuillez contacter l'expéditeur.", + "save-as-template-?": "Êtes-vous sûr de vouloir enregistrer ce document comme modèle ?", + "go-to-manage-templates": "Aller à 'Gérer les modèles'", + "template-created": "Modèle créé", + "how-would-you-like-to-proceed?": "Comment souhaitez-vous procéder ?", + "failed-to-load-refresh-page": "Échec du chargement du document. Veuillez essayer d'actualiser cette page.", + "document-has-been-signed": "Le document a été signé avec succès !", + "document-has-been-signed-by-you": "Le document a été signé avec succès par vous !", + "participant-completed-signing": "Tous les participants ont terminé le processus de signature.", + "you-will-receive-email-shortly": "✅ Voilà, c'est fait ! Vous recevrez un e-mail de confirmation sous peu." } diff --git a/apps/OpenSign/public/locales/it/translation.json b/apps/OpenSign/public/locales/it/translation.json index 5e6be06139..4e124db7e9 100644 --- a/apps/OpenSign/public/locales/it/translation.json +++ b/apps/OpenSign/public/locales/it/translation.json @@ -151,7 +151,8 @@ "extend-expiry-date": "Estendi data di scadenza", "Duplicate Template": "Duplica modello", "Duplicate": "Duplica", - "daily-mail-quota": "Quota e-mail giornaliera" + "daily-mail-quota": "Quota e-mail giornaliera", + "Save as template": "Salva come modello" }, "report-heading": { "Sr.No": "Nr.", @@ -308,7 +309,7 @@ "revoke-document": "Revoca documento", "revoke-document-alert": "Sei sicuro di voler revocare questo documento?", "resend-mail": "Reinvia email", - "resend-mail-help": "Puoi usare le seguenti variabili che verranno sostituite con i loro valori effettivi: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.", + "resend-mail-help": "Puoi usare le seguenti variabili che verranno sostituite con i loro valori effettivi: {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}.", "subject": "Oggetto", "body": "Corpo del messaggio", "add-contact": "Aggiungi contatto", @@ -522,7 +523,7 @@ "correct-password": "Fornisci la password corretta", "decrypting-pdf": "Decrittazione PDF, attendi...", "invalid-otp": "OTP non valido", - "user-not-found": "Utente non trovato!", + "user-not-found": "Utente non trovato", "enter-otp-alert": "Inserisci l'OTP!", "get-verification-code": "Ottieni codice di verifica", "get-verification-code-2": "Riceverai un codice di verifica tramite Email", @@ -858,10 +859,19 @@ "indexing-public-profile": "Consenti l'indicizzazione del profilo pubblico dai motori di ricerca", "user-created-successfully": "Utente creato con successo.", "only-15-reminder-allowed": "Puoi impostare fino a 15 promemoria automatici. Ad esempio, se 'TimeToComplete' è di 15 giorni e 'RemindOnceInEvery' è di 1 giorno, raggiungerai il limite massimo di 15 promemoria. Regola le tue impostazioni di conseguenza.", - "rate-your-experience": "👋 Valuta la tua esperienza con Opensign", + "rate-your-experience": "Com'è stata la sua esperienza con {{appName}}?", "thanks-for-feedback": "Grazie per il tuo feedback 🙏", "share-your-feedback": "Condividi il tuo feedback", "share-your-review": "Condividi la tua recensione", "date-format": "Formato data", - "document-deleted": "Il documento è stato eliminato o non hai accesso. Si prega di contattare il mittente." + "document-deleted": "Il documento è stato eliminato o non hai accesso. Si prega di contattare il mittente.", + "save-as-template-?": "Sei sicuro di voler salvare questo documento come modello?", + "go-to-manage-templates": "Vai a 'Gestisci modelli'", + "template-created": "Modello creato", + "how-would-you-like-to-proceed?": "Come desideri procedere?", + "failed-to-load-refresh-page": "Impossibile caricare il documento. Prova ad aggiornare questa pagina.", + "document-has-been-signed": "Il documento è stato firmato con successo!", + "document-has-been-signed-by-you": "Il documento è stato firmato con successo da lei!", + "participant-completed-signing": "Tutti i partecipanti hanno completato il processo di firma.", + "you-will-receive-email-shortly": "✅ È tutto! Riceverà a breve un'e-mail di conferma." } diff --git a/apps/OpenSign/src/components/RenderDebugPdf.js b/apps/OpenSign/src/components/RenderDebugPdf.js index f33935e4ae..f9915ea07b 100644 --- a/apps/OpenSign/src/components/RenderDebugPdf.js +++ b/apps/OpenSign/src/components/RenderDebugPdf.js @@ -1,7 +1,9 @@ import React from "react"; import { Document, Page } from "react-pdf"; import { Stage, Layer, Rect, Text } from "react-konva"; +import { useTranslation } from "react-i18next"; const RenderDebugPdf = (props) => { + const { t } = useTranslation(); return (
@@ -12,10 +14,9 @@ const RenderDebugPdf = (props) => { onMouseMove={props.handleMouseMoveDiv} > { - props.setPdfLoadFail(false); - }} - loading={"Loading Document.."} + onLoadError={() => props.setPdfLoadFail(false)} + loading={t("loading-doc")} + error={

{t("failed-to-load-refresh-page")}

} onLoadSuccess={props.pageDetails} ref={props.pdfRef} file={props.pdfUrl} diff --git a/apps/OpenSign/src/components/pdf/AddRoleModal.js b/apps/OpenSign/src/components/pdf/AddRoleModal.js index 5edfd2e0e1..6696306dd7 100644 --- a/apps/OpenSign/src/components/pdf/AddRoleModal.js +++ b/apps/OpenSign/src/components/pdf/AddRoleModal.js @@ -17,8 +17,8 @@ const AddRoleModal = (props) => { onChange={(e) => props.setRoleName(e.target.value)} placeholder={ props.signersdata.length > 0 - ? "User " + (props.signersdata.length + 1) - : "User 1" + ? "Role " + (props.signersdata.length + 1) + : "Role 1" } className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1" /> diff --git a/apps/OpenSign/src/components/pdf/Certificate.js b/apps/OpenSign/src/components/pdf/Certificate.js deleted file mode 100644 index 2051c26cd0..0000000000 --- a/apps/OpenSign/src/components/pdf/Certificate.js +++ /dev/null @@ -1,276 +0,0 @@ -import React, { useEffect, useState } from "react"; -import opensignLogo from "../../assets/images/logo.png"; -import { - Page, - Text, - View, - Document, - StyleSheet, - Image -} from "@react-pdf/renderer"; - -function Certificate({ pdfData }) { - const [isMultiSigners, setIsMultiSigners] = useState(); - const [multiSigner, setMultiSigners] = useState([]); - const [isLoad, setIsLoad] = useState(false); - - useEffect(() => { - handleSignerData(); - // eslint-disable-next-line - }, []); - - const handleSignerData = () => { - const checkSigners = pdfData.filter((data) => data.Signers); - if (checkSigners && checkSigners.length > 0) { - setIsMultiSigners(true); - - const checkSignSigners = - pdfData[0].AuditTrail && - pdfData[0].AuditTrail.length > 0 && - pdfData[0].AuditTrail.filter((data) => data.Activity === "Signed"); - - setMultiSigners(checkSignSigners); - } else { - setIsMultiSigners(false); - } - setIsLoad(true); - }; - - const styles = StyleSheet.create({ - page: { - borderRadius: "5px", - padding: "10px", - backgroundColor: "white" - }, - section1: { - border: "1px solid rgb(177, 174, 174)", - padding: "20px" - }, - textStyle: { - fontWeight: "bold", - fontSize: "11px", - marginBottom: "10px" - }, - textStyle2: { - fontWeight: "600", - fontSize: "11px", - marginBottom: "10px", - color: "gray" - }, - image: { - width: "71px", - height: "17px" - } - }); - - const generatedDate = () => { - const newDate = new Date(); - const utcTime = newDate.toUTCString(); - - return ( - - Generated On {utcTime} - - ); - }; - const changeCompletedDate = () => { - const completedOn = pdfData[0].updatedAt; - const newDate = new Date(completedOn); - const utcTime = newDate.toUTCString(); - - return {utcTime}; - }; - - const signerName = (data) => { - const getSignerName = pdfData[0].Signers.filter( - (sign) => sign.objectId === data.UserPtr.objectId - ); - - return ( - getSignerName[0] && - getSignerName.length > 0 && ( - <> - - Name :   - {getSignerName[0].Name} - - - Email :   - {getSignerName[0].Email} - - - ) - ); - }; - - return ( - isLoad && ( - - {/** Page defines a single page of content. */} - - - - - {generatedDate()} - - - - - {" "} - Certificate of Completion - - - - - - Summary - - - - - Document ID :   - {pdfData[0].objectId} - - - Document Name :   - {pdfData[0].Name} - - - Organization :   - - {pdfData[0].ExtUserPtr.Company} - - - - Completed on :  {changeCompletedDate()} - - {multiSigner && multiSigner.length > 0 && ( - - Signers :   - - {multiSigner.length} - - - )} - - {isMultiSigners ? ( - - - Recipients - - - - {multiSigner && - multiSigner.map((data, ind) => { - return ( - - - {signerName(data)} - - - Accessed from :   - - {data.ipAddress} - - - - ); - })} - - - ) : ( - - - Recipients - - - Signers :  1 - - - Name :   - - {pdfData[0].ExtUserPtr.Name} - - - - Email :   - - {pdfData[0].ExtUserPtr.Email} - - - - Accessed from :   - - {pdfData[0].AuditTrail && - pdfData[0].AuditTrail[0].ipAddress} - - - - - Signed on :  {changeCompletedDate()} - - - )} - - - - - - ) - ); -} - -export default Certificate; diff --git a/apps/OpenSign/src/components/pdf/PdfHeader.js b/apps/OpenSign/src/components/pdf/PdfHeader.js index 25ae08606c..e20c1ff7f6 100644 --- a/apps/OpenSign/src/components/pdf/PdfHeader.js +++ b/apps/OpenSign/src/components/pdf/PdfHeader.js @@ -14,6 +14,7 @@ import ModalUi from "../../primitives/ModalUi"; import Loader from "../../primitives/Loader"; import { useTranslation } from "react-i18next"; import { PDFDocument } from "pdf-lib"; +import { maxFileSize } from "../../constant/const"; function Header(props) { const { t } = useTranslation(); @@ -50,6 +51,13 @@ function Header(props) { } }; + // `removeFile` is used to remove file if exists + const removeFile = (e) => { + if (e) { + e.target.value = ""; + } + }; + const handleFileUpload = async (e) => { const file = e.target.files[0]; if (!file) { @@ -60,6 +68,13 @@ function Header(props) { alert("Only PDF files are allowed."); return; } + + const mb = Math.round(file?.size / Math.pow(1024, 2)); + if (mb > maxFileSize) { + alert(`${t("file-alert-1")} ${maxFileSize} MB`); + removeFile(e); + return; + } try { const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { diff --git a/apps/OpenSign/src/components/pdf/PdfZoom.js b/apps/OpenSign/src/components/pdf/PdfZoom.js index 746139ba4d..dc74ffd0a4 100644 --- a/apps/OpenSign/src/components/pdf/PdfZoom.js +++ b/apps/OpenSign/src/components/pdf/PdfZoom.js @@ -7,6 +7,7 @@ import { } from "../../constant/Utils"; import ModalUi from "../../primitives/ModalUi"; import { PDFDocument } from "pdf-lib"; +import { maxFileSize } from "../../constant/const"; function PdfZoom(props) { const { t } = useTranslation(); @@ -41,6 +42,14 @@ function PdfZoom(props) { console.log("error in delete pdf page", e); } }; + + // `removeFile` is used to remove file if exists + const removeFile = (e) => { + if (e) { + e.target.value = ""; + } + }; + const handleFileUpload = async (e) => { const file = e.target.files[0]; if (!file) { @@ -51,6 +60,12 @@ function PdfZoom(props) { alert("Only PDF files are allowed."); return; } + const mb = Math.round(file?.size / Math.pow(1024, 2)); + if (mb > maxFileSize) { + alert(`${t("file-alert-1")} ${maxFileSize} MB`); + removeFile(e); + return; + } try { const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { diff --git a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js index 1c41850ee5..58372a396d 100644 --- a/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js +++ b/apps/OpenSign/src/components/pdf/RenderAllPdfPage.js @@ -4,6 +4,7 @@ import { Document, Page } from "react-pdf"; import { useSelector } from "react-redux"; import { PDFDocument } from "pdf-lib"; import { base64ToArrayBuffer } from "../../constant/Utils"; +import { maxFileSize } from "../../constant/const"; function RenderAllPdfPage(props) { const { t } = useTranslation(); @@ -62,6 +63,12 @@ function RenderAllPdfPage(props) { }; const pdfDataBase64 = `data:application/pdf;base64,${props?.pdfBase64Url}`; + // `removeFile` is used to remove file if exists + const removeFile = (e) => { + if (e) { + e.target.value = ""; + } + }; // `handleFileUpload` is trigger when user click on add pages btn and is used to merge multiple pdf const handleFileUpload = async (e) => { const file = e.target.files[0]; @@ -73,6 +80,12 @@ function RenderAllPdfPage(props) { alert("Only PDF files are allowed."); return; } + const mb = Math.round(file?.size / Math.pow(1024, 2)); + if (mb > maxFileSize) { + alert(`${t("file-alert-1")} ${maxFileSize} MB`); + removeFile(e); + return; + } try { const uploadedPdfBytes = await file.arrayBuffer(); const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, { @@ -110,6 +123,7 @@ function RenderAllPdfPage(props) { autoSignScroll hide-scrollbar max-h-[100vh] `} > ); }))} - + {/* Mobile */} {t("failed-to-load-refresh-page")}

} onLoadError={() => props.setPdfLoad(false)} loading={t("loading-doc")} onLoadSuccess={props.pageDetails} - // ref={pdfRef}' - onClick={() => { - if (props.setSelectWidgetId) { - props.setSelectWidgetId(""); - } - }} + onClick={() => + props.setSelectWidgetId && props.setSelectWidgetId("") + } file={pdfDataBase64} > ); }))} - + {/* large device */} {/* this component for render pdf document is in middle of the component */} {t("failed-to-load-refresh-page")}

} onLoadError={() => props.setPdfLoad(false)} loading={t("loading-doc")} onLoadSuccess={props.pageDetails} - onClick={() => { - if (props.setSelectWidgetId) { - props.setSelectWidgetId(""); - } - }} + onClick={() => + props.setSelectWidgetId && props.setSelectWidgetId("") + } file={pdfDataBase64} > { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId") }; - const body = { email: email }; + const body = { + email: email + }; await axios.post(url, body, { headers: headers }); } catch (error) { alert(error.message); @@ -2960,8 +2962,10 @@ export const mailTemplate = (param) => { param.senderMail + "Organization " + param.organization + - "Expire on" + + "Expires on" + param.localExpireDate + + "Note" + + param.note + "

This is an automated email from " + diff --git a/apps/OpenSign/src/constant/const.js b/apps/OpenSign/src/constant/const.js index 30ed762975..f6b6198b78 100644 --- a/apps/OpenSign/src/constant/const.js +++ b/apps/OpenSign/src/constant/const.js @@ -4,3 +4,4 @@ export const documentCls = "contracts_Document"; export const themeColor = "#47a3ad"; export const iconColor = "#686968"; export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; +export const maxFileSize = 10; // 10MB diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js index f27819bd06..ac9990ef72 100644 --- a/apps/OpenSign/src/json/ReportJson.js +++ b/apps/OpenSign/src/json/ReportJson.js @@ -1,4 +1,3 @@ - export default function reportJson(id) { // console.log("json ", json); const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"]; @@ -29,6 +28,25 @@ export default function reportJson(id) { btnIcon: "fa-light fa-trash", redirectUrl: "", action: "delete" + }, + { + btnId: "22534", + hoverLabel: "option", + btnColor: "", + restrictBtn: true, + textColor: "black", + btnIcon: "fa-light fa-ellipsis-vertical fa-lg", + action: "option", + subaction: [ + { + btnId: "1630", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + } + ] } ], helpMsg: @@ -107,6 +125,14 @@ export default function reportJson(id) { redirectUrl: "", action: "revoke" }, + { + btnId: "0630", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + }, { btnId: "1488", btnLabel: "Delete", @@ -143,6 +169,25 @@ export default function reportJson(id) { restrictBtn: true, redirectUrl: "", action: "delete" + }, + { + btnId: "33534", + hoverLabel: "option", + btnColor: "", + restrictBtn: true, + textColor: "black", + btnIcon: "fa-light fa-ellipsis-vertical fa-lg", + action: "option", + subaction: [ + { + btnId: "0930", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + } + ] } ], helpMsg: @@ -169,6 +214,25 @@ export default function reportJson(id) { btnIcon: "fa-light fa-trash", redirectUrl: "", action: "delete" + }, + { + btnId: "44534", + hoverLabel: "option", + btnColor: "", + restrictBtn: true, + textColor: "black", + btnIcon: "fa-light fa-ellipsis-vertical fa-lg", + action: "option", + subaction: [ + { + btnId: "0940", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + } + ] } ], helpMsg: @@ -212,6 +276,14 @@ export default function reportJson(id) { btnIcon: "fa-light fa-hourglass-end", redirectUrl: "", action: "extendexpiry" + }, + { + btnId: "5530", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" } ] } @@ -273,6 +345,14 @@ export default function reportJson(id) { redirectUrl: "", action: "revoke" }, + { + btnId: "7730", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + }, { btnId: "2000", btnLabel: "Delete", @@ -323,6 +403,25 @@ export default function reportJson(id) { btnIcon: "fa-light fa-trash", redirectUrl: "", action: "delete" + }, + { + btnId: "55534", + hoverLabel: "option", + btnColor: "", + restrictBtn: true, + textColor: "black", + btnIcon: "fa-light fa-ellipsis-vertical fa-lg", + action: "option", + subaction: [ + { + btnId: "6630", + btnLabel: "Save as template", + hoverLabel: "Save as template", + btnIcon: "fa-light fa-envelope", + redirectUrl: "", + action: "saveastemplate" + } + ] } ] }; diff --git a/apps/OpenSign/src/layout/HomeLayout.js b/apps/OpenSign/src/layout/HomeLayout.js index eb1b03eb83..9297aa9583 100644 --- a/apps/OpenSign/src/layout/HomeLayout.js +++ b/apps/OpenSign/src/layout/HomeLayout.js @@ -15,8 +15,7 @@ import { showHeader } from "../redux/reducers/showHeader"; import { useTranslation } from "react-i18next"; const HomeLayout = () => { - const appName = - "OpenSign™"; + const appName = "OpenSign™"; const { t, i18n } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); @@ -54,8 +53,8 @@ const HomeLayout = () => { }); if (user) { localStorage.setItem("profileImg", user.get("ProfilePic") || ""); - setIsUserValid(true); - setIsLoader(false); + setIsUserValid(true); + setIsLoader(false); } else { setIsUserValid(false); } @@ -72,7 +71,7 @@ const HomeLayout = () => { //function to use save data in cookies storage const saveCookies = () => { const main_Domain = window.location.origin; - const domainName = window.location.hostname; //app.opensignlabs.com + const domainName = window.location.hostname; // Find the index of the first dot in the string const indexOfFirstDot = domainName.indexOf("."); // Remove the first dot and get the substring starting from the next character diff --git a/apps/OpenSign/src/pages/DocSuccessPage.js b/apps/OpenSign/src/pages/DocSuccessPage.js index a957f86552..e394d411db 100644 --- a/apps/OpenSign/src/pages/DocSuccessPage.js +++ b/apps/OpenSign/src/pages/DocSuccessPage.js @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; +import Confetti from "react-confetti"; // Import the confetti library import { getBase64FromUrl, handleDownloadCertificate, @@ -10,19 +11,25 @@ import ModalUi from "../primitives/ModalUi"; import Loader from "../primitives/Loader"; import DownloadPdfZip from "../primitives/DownloadPdfZip"; import Title from "../components/Title"; +import CheckCircle from "../primitives/CheckCircle"; const DocSuccessPage = () => { + const { t } = useTranslation(); const signed = window.location?.search?.includes("docid"); const sent = window.location?.search?.includes("message"); const [isDownloading, setIsDownloading] = useState(false); const [isDownloadModal, setIsDownloadModal] = useState(false); const [pdfDetails, setPdfDetails] = useState([]); const [pdfBase64Url, setPdfBase64Url] = useState(""); - const { t } = useTranslation(); + const [showConfetti, setShowConfetti] = useState(true); // State to control confetti useEffect(() => { initialsetup(); + // Stop confetti after 5 seconds + const timer = setTimeout(() => setShowConfetti(false), 5000); + return () => clearTimeout(timer); }, []); + const initialsetup = async () => { const search = window.location.search.split("?")[1]; if (search) { @@ -44,6 +51,7 @@ const DocSuccessPage = () => { } } }; + const handleDownload = () => { if (pdfDetails?.[0]?.IsCompleted) { setIsDownloadModal(true); @@ -51,50 +59,79 @@ const DocSuccessPage = () => { handleDownloadPdf(pdfDetails, setIsDownloading, pdfBase64Url); } }; + return ( -

+ <> + {/* Confetti Effect */} + {showConfetti && ( + <Confetti width={window.innerWidth} height={window.innerHeight} /> + )} {sent ? ( - <div>{t("doc-sent")}</div> + <div className="min-h-screen flex flex-col items-center justify-center p-3 md:p-8 text-center"> + <div className="max-w-lg md:max-w-2xl bg-white rounded-lg shadow-lg p-3 md:p-10"> + {t("doc-sent")} + </div> + </div> ) : signed ? ( - <div className="text-center"> - <p> - {pdfDetails?.[0]?.IsCompleted - ? t("document-signed-alert-4") - : t("document-signed-alert")} - </p> - <div className="m-2"> - <button - onClick={(e) => handleToPrint(e, setIsDownloading, pdfDetails)} - type="button" - className="font-[500] text-[13px] mr-[5px] op-btn op-btn-neutral" - > - <i className="fa-light fa-print" aria-hidden="true"></i> - <span className="hidden lg:block">{t("print")}</span> - </button> - {pdfDetails?.[0]?.IsCompleted && ( - <button - type="button" - onClick={() => - handleDownloadCertificate(pdfDetails, setIsDownloading) - } - className="font-[500] text-[13px] mr-[5px] op-btn op-btn-secondary" - > - <i - className="fa-light fa-award mx-[3px] lg:mx-0" - aria-hidden="true" - ></i> - <span className="hidden lg:block">{t("certificate")}</span> - </button> - )} - <button - type="button" - className="font-[500] text-[13px] mr-[5px] op-btn op-btn-primary" - onClick={() => handleDownload()} - > - <i className="fa-light fa-download" aria-hidden="true"></i> - <span className="hidden lg:block">{t("download")}</span> - </button> + <> + <div className="min-h-screen flex flex-col items-center justify-center p-3 md:p-8 text-center"> + <div className="max-w-lg md:max-w-2xl bg-white rounded-lg shadow-lg p-3 md:p-10"> + <div className="flex flex-col items-center space-y-4 "> + <CheckCircle className="text-green-500 w-12 h-12 md:w-14 md:h-14" /> + <h1 className="text-xl md:text-2xl font-semibold text-gray-800"> + {pdfDetails?.[0]?.IsCompleted + ? t("document-has-been-signed") + : t("document-has-been-signed-by-you")} + </h1> + {pdfDetails?.[0]?.IsCompleted && ( + <p className="text-sm md:text-base text-gray-600"> + {t("participant-completed-signing")} + </p> + )} + </div> + {/* Action Buttons */} + <div className="mt-6 flex flex-wrap justify-center gap-2"> + <button + type="button" + className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-primary" + onClick={() => handleDownload()} + > + <i className="fa-light fa-download" aria-hidden="true"></i> + <span>{t("download")}</span> + </button> + + {pdfDetails?.[0]?.IsCompleted && ( + <button + type="button" + onClick={() => + handleDownloadCertificate(pdfDetails, setIsDownloading) + } + className="font-medium text-sm md:text-[13px] md:px-4 py-2 op-btn op-btn-secondary" + > + <i + className="fa-light fa-award mx-[3px] md:mx-0" + aria-hidden="true" + ></i> + <span>{t("certificate")}</span> + </button> + )} + <button + onClick={(e) => + handleToPrint(e, setIsDownloading, pdfDetails) + } + type="button" + className="font-medium text-sm md:text-[13px] px-4 py-2 op-btn op-btn-neutral" + > + <i className="fa-light fa-print" aria-hidden="true"></i> + <span>{t("print")}</span> + </button> + </div> + {/* Footer Message */} + <p className="mt-4 md:mt-6 text-xs md:text-sm text-gray-500"> + {t("you-will-receive-email-shortly")} + </p> + </div> </div> {isDownloading === "pdf" && ( <div className="fixed z-[1000] inset-0 flex justify-center items-center bg-black bg-opacity-30"> @@ -114,7 +151,7 @@ const DocSuccessPage = () => { } handleClose={() => setIsDownloading("")} > - <div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content"> + <div className="p-3 md:p-5 text-sm md:text-base text-center text-base-content"> {isDownloading === "certificate" ? ( <p>{t("generate-certificate-alert")}</p> ) : ( @@ -129,11 +166,11 @@ const DocSuccessPage = () => { isDocId={true} pdfBase64={pdfBase64Url} /> - </div> + </> ) : ( <></> )} - </div> + </> ); }; diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index dfffee88e9..c34b51d757 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -18,6 +18,7 @@ import { } from "../constant/Utils"; import { PDFDocument } from "pdf-lib"; import axios from "axios"; +import { maxFileSize } from "../constant/const"; import ModalUi from "../primitives/ModalUi"; import { Tooltip } from "react-tooltip"; import Loader from "../primitives/Loader"; @@ -38,7 +39,6 @@ function Form() { const Forms = (props) => { const appName = "OpenSign™"; const { t } = useTranslation(); - const maxFileSize = 20; const abortController = new AbortController(); const inputFileRef = useRef(null); const navigate = useNavigate(); diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index d5c6e0fbbf..37469e5074 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -1,13 +1,8 @@ import React, { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router"; import axios from "axios"; -import { - emailRegex, -} from "../constant/const"; -import { - contractUsers, - saveLanguageInLocal -} from "../constant/Utils"; +import { emailRegex } from "../constant/const"; +import { contractUsers, saveLanguageInLocal } from "../constant/Utils"; import logo from "../assets/images/logo.png"; import { appInfo } from "../constant/appinfo"; import Parse from "parse"; @@ -63,13 +58,10 @@ function GuestLogin() { //function generate serverUrl and parseAppId from url and save it in local storage const handleServerUrl = async () => { - setAppLogo(logo); + setAppLogo(logo); localStorage.clear(); // Clears everything - localStorage.setItem( - "appname", - "OpenSign™" - ); + localStorage.setItem("appname", "OpenSign™"); //save isGuestSigner true in local to handle login flow header in mobile view localStorage.setItem("isGuestSigner", true); saveLanguageInLocal(i18n); @@ -113,7 +105,10 @@ function GuestLogin() { setLoading(true); setEmail(email); try { - const params = { email: email.toString(), docId: documentId }; + const params = { + email: email.toString(), + docId: documentId + }; const Otp = await Parse.Cloud.run("SendOTPMailV1", params); if (Otp) { setLoading(false); @@ -216,7 +211,14 @@ function GuestLogin() { } }; const handleInputChange = (e) => { - setContact((prev) => ({ ...prev, [e.target.name]: e.target.value })); + if (e.target.name === "email") { + setContact((prev) => ({ + ...prev, + [e.target.name]: e.target.value?.toLowerCase()?.replace(/\s/g, "") + })); + } else { + setContact((prev) => ({ ...prev, [e.target.name]: e.target.value })); + } }; return ( <div> diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index ef8f49b24d..4a3df40717 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -54,6 +54,10 @@ function Login() { // eslint-disable-next-line }, []); + const showToast = (type, msg) => { + setState({ ...state, loading: false, alertType: type, alertMsg: msg }); + setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); + }; const checkUserExt = async () => { const app = await getAppLogo(); if (app?.error === "invalid_json") { @@ -156,60 +160,25 @@ function Login() { setIsModal(true); } } else { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: - "You don't have access, please contact the admin." - }); + showToast("danger", t("do-not-access-contact-admin")); logOutUser(); } } else { - setState({ ...state, loading: false }); - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: "User not found." - }); + showToast("danger", t("user-not-found")); logOutUser(); } }) .catch((error) => { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: t("something-went-wrong-mssg") - }); - setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); + showToast("danger", t("something-went-wrong-mssg")); console.error("Error while fetching Follow", error); }); } catch (error) { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: `${error.message}` - }); - console.log(error); - setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); + showToast("danger", `${error.message}`); } } } catch (error) { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: "Invalid username/password or region" - }); console.error("Error while logging in user", error); - } finally { - setTimeout( - () => setState((prev) => ({ ...prev, alertMsg: "" })), - 2000 - ); + showToast("danger", "Invalid username/password or region"); } } } @@ -279,53 +248,29 @@ function Login() { localStorage.setItem("pageType", menu.pageType); navigate(redirectUrl); } else { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: "Role not found." - }); + showToast("danger", t("role-not-found")); logOutUser(); } } else { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: "You don't have access, please contact the admin." - }); + showToast("danger", t("do-not-access-contact-admin")); logOutUser(); } } else { - setState({ - ...state, - alertType: "danger", - alertMsg: "User not found." - }); + showToast("danger", t("user-not-found")); logOutUser(); } }) .catch((err) => { console.error("err in fetching extUser", err); - setState({ - ...state, - alertType: "danger", - alertMsg: `${err.message}` - }); + showToast("danger", `${err.message}`); const payload = { sessionToken: sessionToken }; handleSubmitbtn(payload); }); } catch (error) { - setState({ - ...state, - alertType: "danger", - alertMsg: `${error.message}` - }); + showToast("danger", `${error.message}`); console.log(error); } finally { setThirdpartyLoader(false); - setState({ ...state, loading: false }); - setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); } } }; @@ -382,33 +327,17 @@ function Login() { logOutUser(); } } else { - setState({ - ...state, - loading: false, - alertType: "danger", - alertMsg: "You don't have access, please contact the admin." - }); + showToast("danger", t("do-not-access-contact-admin")); logOutUser(); } } else { - setState({ - ...state, - alertType: "danger", - alertMsg: "User not found." - }); + showToast("danger", t("user-not-found")); logOutUser(); } }); } catch (error) { - setState({ - ...state, - alertType: "danger", - alertMsg: t("something-went-wrong-mssg") - }); + showToast("danger", t("something-went-wrong-mssg")); console.log("err", error); - } finally { - setState({ ...state, loading: false }); - setTimeout(() => setState({ ...state, alertMsg: "" }), 2000); } }; @@ -420,8 +349,6 @@ function Login() { e.preventDefault(); if (userDetails.Destination && userDetails.Company) { setThirdpartyLoader(true); - // console.log("handelSubmit", userDetails); - // const payload = await Parse.User.logIn(state.email, state.password); const payload = { sessionToken: localStorage.getItem("accesstoken") }; const userInformation = JSON.parse( localStorage.getItem("UserInformation") @@ -461,13 +388,7 @@ function Login() { alert(t("server-error")); } } else { - setState({ - ...state, - loading: false, - alertType: "warning", - alertMsg: "Please fill required details." - }); - setTimeout(() => setState((prev) => ({ ...prev, alertMsg: "" })), 2000); + showToast("warning", t("fill-required-details!")); } }; @@ -516,7 +437,7 @@ function Login() { <div aria-labelledby="loginHeading" role="region" - className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16" + className="pb-1 md:pb-4 pt-10 md:px-10 lg:px-16 h-screen" > <div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card"> <div className="w-[250px] h-[66px] inline-block overflow-hidden"> diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index f80bd9e53c..290fe0b3ee 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -521,6 +521,14 @@ function PdfRequestFiles() { } ); const contact = resContact?.data?.result; + localStorage.setItem( + "signer", + JSON.stringify({ + Name: contact?.Name, + Email: contact?.Email, + UserId: contact?.UserId?.objectId + }) + ); setContractName("_Contactbook"); setSignerUserId(contact?.objectId); const tourData = contact?.TourStatus && contact?.TourStatus; @@ -877,6 +885,7 @@ function PdfRequestFiles() { const variables = { document_title: documentName, + note: pdfDetails?.[0]?.Note, sender_name: senderName, sender_mail: senderEmail, sender_phone: senderPhone, @@ -894,6 +903,7 @@ function PdfRequestFiles() { ); } const mailparam = { + note: pdfDetails?.[0]?.Note || "", senderName: senderName, senderMail: senderEmail, title: documentName, diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index e08c0a9358..fa8ba5d115 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -1182,6 +1182,7 @@ function PlaceHolderSign() { const variables = { document_title: documentName, + note: pdfDetails?.[0]?.Note, sender_name: senderName, sender_mail: senderEmail, sender_phone: senderPhone || "", @@ -1207,6 +1208,7 @@ function PlaceHolderSign() { "</body> </html>"; const variables = { document_title: documentName, + note: pdfDetails?.[0]?.Note, sender_name: senderName, sender_mail: senderEmail, sender_phone: senderPhone || "", @@ -1221,6 +1223,7 @@ function PlaceHolderSign() { } const mailparam = { senderName: senderName, + note: pdfDetails?.[0]?.Note || "", senderMail: senderEmail, title: documentName, organization: orgName, diff --git a/apps/OpenSign/src/pages/Preferences.js b/apps/OpenSign/src/pages/Preferences.js index 692bfd0154..d7b0ae4821 100644 --- a/apps/OpenSign/src/pages/Preferences.js +++ b/apps/OpenSign/src/pages/Preferences.js @@ -244,13 +244,15 @@ const Preferences = () => { setIsLoader(true); const replacedHtmlBody = completionBody.replace(/"/g, "'"); const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`; - const tenantQuery = new Parse.Query("partners_Tenant"); - const updateTenantObj = await tenantQuery.get(tenantId); - updateTenantObj.set("CompletionBody", htmlBody); - updateTenantObj.set("CompletionSubject", completionsubject); - const res = await updateTenantObj.save(); - if (res) { - const updateRes = JSON.parse(JSON.stringify(res)); + const updateTenant = await Parse.Cloud.run("updatetenant", { + tenantId: tenantId, + details: { + CompletionBody: htmlBody, + CompletionSubject: completionsubject + } + }); + if (updateTenant) { + const updateRes = JSON.parse(JSON.stringify(updateTenant)); SetCompletionBody(updateRes?.CompletionBody); setCompletionSubject(updateRes?.CompletionSubject); setIsAlert({ type: "success", msg: t("saved-successfully") }); @@ -271,13 +273,15 @@ const Preferences = () => { setIsLoader(true); const replacedHtmlBody = requestBody.replace(/"/g, "'"); const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`; - const tenantQuery = new Parse.Query("partners_Tenant"); - const updateTenantObj = await tenantQuery.get(tenantId); - updateTenantObj.set("RequestBody", htmlBody); - updateTenantObj.set("RequestSubject", requestSubject); - const res = await updateTenantObj.save(); - if (res) { - const updateRes = JSON.parse(JSON.stringify(res)); + const updateTenant = await Parse.Cloud.run("updatetenant", { + tenantId: tenantId, + details: { + RequestBody: htmlBody, + RequestSubject: requestSubject + } + }); + if (updateTenant) { + const updateRes = JSON.parse(JSON.stringify(updateTenant)); setRequestBody(updateRes?.RequestBody); setRequestSubject(updateRes?.RequestSubject); setIsAlert({ type: "success", msg: t("saved-successfully") }); diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 5b04badc5b..9b1aefd09f 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -1212,7 +1212,7 @@ function SignYourSelf() { setPdfBase64Url={setPdfBase64Url} setIsUploadPdf={setIsUploadPdf} pdfArrayBuffer={pdfArrayBuffer} - isMergePdfBtn={true} + isMergePdfBtn={!pdfDetails?.[0]?.IsCompleted} /> <div className=" w-full md:w-[57%] flex mr-4"> <PdfZoom diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index 857792bde3..3786f239ab 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -284,14 +284,14 @@ const TemplatePlaceholder = () => { const updatedSigners = documentData[0].Signers.map((x, index) => ({ ...x, Id: randomId(), - Role: "User " + (index + 1) + Role: "Role " + (index + 1) })); setSignersData(updatedSigners); setUniqueId(updatedSigners[0].Id); setBlockColor(updatedSigners[0].blockColor); } } else { - setRoleName("User 1"); + setRoleName("Role 1"); if ( documentData[0].Placeholders && documentData[0].Placeholders.length > 0 @@ -1101,7 +1101,7 @@ const TemplatePlaceholder = () => { const Id = randomId(); const index = signersdata.length; const obj = { - Role: roleName || "User " + count, + Role: roleName || "Role " + count, Id: Id, blockColor: color[index] }; @@ -1110,7 +1110,7 @@ const TemplatePlaceholder = () => { signerPtr: {}, signerObjId: "", blockColor: color[index], - Role: roleName || "User " + count, + Role: roleName || "Role " + count, Id: Id }; diff --git a/apps/OpenSign/src/primitives/Alert.js b/apps/OpenSign/src/primitives/Alert.js index 1d84d9b075..6b5950851a 100644 --- a/apps/OpenSign/src/primitives/Alert.js +++ b/apps/OpenSign/src/primitives/Alert.js @@ -11,7 +11,7 @@ const Alert = ({ children, type, className }) => { case "danger": return "op-alert-error"; case "warning": - return "op-alert-warning"; + return "op-alert-warning text-black"; default: return ""; } diff --git a/apps/OpenSign/src/primitives/CheckCircle.js b/apps/OpenSign/src/primitives/CheckCircle.js new file mode 100644 index 0000000000..91e02fb026 --- /dev/null +++ b/apps/OpenSign/src/primitives/CheckCircle.js @@ -0,0 +1,34 @@ +import React from "react"; + +const CheckCircle = ({ size = 56, color = "text-green-500" }) => { + return ( + <div className={`flex items-center justify-center ${color}`}> + <svg + xmlns="http://www.w3.org/2000/svg" + className={`w-${size / 4} h-${size / 4}`} + fill="none" + viewBox="0 0 24 24" + stroke="currentColor" + strokeWidth={2} + > + <circle + cx="12" + cy="12" + r="10" + stroke="currentColor" + strokeWidth="2" + fill="none" + /> + <path + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth="2" + d="M9 12l2 2l4-4" + /> + </svg> + </div> + ); +}; + +export default CheckCircle; diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index ddf20f3779..6ad5356f99 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useRef } from "react"; import pad from "../assets/images/pad.svg"; -import { useLocation, useNavigate } from "react-router"; +import { Link, useLocation, useNavigate } from "react-router"; import axios from "axios"; import ModalUi from "./ModalUi"; import AddSigner from "../components/AddSigner"; @@ -46,7 +46,6 @@ const ReportTable = (props) => { location?.pathname === "/dashboard/35KBoSgoAK" ? true : false; const [currentPage, setCurrentPage] = useState(1); const [actLoader, setActLoader] = useState({}); - const [isAlert, setIsAlert] = useState(false); const [isContactform, setIsContactform] = useState(false); const [isDeleteModal, setIsDeleteModal] = useState({}); const [isRevoke, setIsRevoke] = useState({}); @@ -81,6 +80,8 @@ const ReportTable = (props) => { const [invalidRecords, setInvalidRecords] = useState(0); const [renameDoc, setRenameDoc] = useState(""); const [contact, setContact] = useState({ Name: "", Email: "", Phone: "" }); + const [isSuccess, setIsSuccess] = useState({}); + const [templateId, setTemplateId] = useState(""); const recordsPerPage = 5; const startIndex = (currentPage - 1) * props.docPerPage; const { isMoreDocs, setIsNextRecord } = props; @@ -136,6 +137,10 @@ const ReportTable = (props) => { return pages; }; + const showAlert = (type, message, time = 1500) => { + setAlertMsg({ type: type, message: message }); + setTimeout(() => setAlertMsg({ type: "", message: "" }), time); + }; const pageNumbers = getPaginationRange(); // below useEffect reset currenpage to 1 if user change route useEffect(() => { @@ -225,164 +230,143 @@ const ReportTable = (props) => { navigate(`/${act.redirectUrl}/${item.objectId}`); } else { setActLoader({ [`${item.objectId}_${act.btnId}`]: true }); - try { - const params = { templateId: item.objectId }; - const templateDeatils = await axios.post( - `${localStorage.getItem("baseUrl")}functions/getTemplate`, - params, - { - headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") - } - } - ); - const templateData = - templateDeatils.data && templateDeatils.data.result; - if (!templateData.error) { - const Doc = templateData; + handleUseTemplate(item.objectId, act.redirectUrl); + } + } else { + navigate(`/${act.redirectUrl}?docId=${item?.objectId}`); + } + }; - let signers = []; - if (Doc.Signers?.length > 0) { - Doc.Signers?.forEach((x) => { - if (x.objectId) { - const obj = { - __type: "Pointer", - className: "contracts_Contactbook", - objectId: x.objectId - }; - signers.push(obj); - } + const handleUseTemplate = async (templateId, redirectUrl) => { + try { + const params = { templateId: templateId }; + const templateDeatils = await axios.post( + `${localStorage.getItem("baseUrl")}functions/getTemplate`, + params, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + sessionToken: localStorage.getItem("accesstoken") + } + } + ); + const templateData = templateDeatils.data && templateDeatils.data.result; + if (!templateData.error) { + const Doc = templateData; + + let signers = []; + if (Doc.Signers?.length > 0) { + Doc.Signers?.forEach((x) => { + if (x.objectId) { + signers.push({ + __type: "Pointer", + className: "contracts_Contactbook", + objectId: x.objectId }); } + }); + } - let extUserId = Doc.ExtUserPtr.objectId; - let creatorId = Doc.CreatedBy.objectId; + let extUserId = Doc.ExtUserPtr.objectId; + let creatorId = Doc.CreatedBy.objectId; + if (extClass && extClass.length > 0) { + if (Doc.ExtUserPtr?.objectId !== extClass[0].objectId) { + const Extand_Class = localStorage.getItem("Extand_Class"); + const extClass = Extand_Class && JSON.parse(Extand_Class); if (extClass && extClass.length > 0) { - if (Doc.ExtUserPtr?.objectId !== extClass[0].objectId) { - const Extand_Class = localStorage.getItem("Extand_Class"); - const extClass = Extand_Class && JSON.parse(Extand_Class); - if (extClass && extClass.length > 0) { - extUserId = extClass[0].objectId; - creatorId = extClass[0]?.UserId.objectId; - } - } + extUserId = extClass[0].objectId; + creatorId = extClass[0]?.UserId.objectId; } - const tenantSignTypes = await fetchTenantDetails(); - const docSignTypes = Doc?.SignatureType || signatureTypes; - const updatedSignatureType = await handleSignatureType( - tenantSignTypes, - docSignTypes - ); - const SignatureType = - updatedSignatureType.length > 0 - ? { SignatureType: updatedSignatureType } - : {}; - const NotifyOnSignatures = - Doc?.NotifyOnSignatures !== undefined - ? { NotifyOnSignatures: Doc.NotifyOnSignatures } - : {}; - const Bcc = Doc?.Bcc?.length > 0 ? { Bcc: Doc?.Bcc } : {}; - const RedirectUrl = Doc?.RedirectUrl - ? { RedirectUrl: Doc?.RedirectUrl } - : {}; - let placeholdersArr = []; - if (Doc.Placeholders?.length > 0) { - placeholdersArr = Doc.Placeholders; - const data = { - Name: Doc.Name, - URL: Doc.URL, - SignedUrl: Doc.SignedUrl, - SentToOthers: Doc?.SentToOthers || false, - Description: Doc.Description, - Note: Doc.Note, - Placeholders: placeholdersArr, - ExtUserPtr: { - __type: "Pointer", - className: "contracts_Users", - objectId: extUserId - }, - CreatedBy: { - __type: "Pointer", - className: "_User", - objectId: creatorId - }, - Signers: signers, - SendinOrder: Doc?.SendinOrder || false, - AutomaticReminders: Doc?.AutomaticReminders || false, - RemindOnceInEvery: Doc?.RemindOnceInEvery || 5, - IsEnableOTP: Doc?.IsEnableOTP || false, - TimeToCompleteDays: parseInt(Doc?.TimeToCompleteDays) || 15, - AllowModifications: Doc?.AllowModifications || false, - ...SignatureType, - ...NotifyOnSignatures, - ...Bcc, - ...RedirectUrl - }; - try { - const res = await axios.post( - `${localStorage.getItem( - "baseUrl" - )}classes/contracts_Document`, - data, - { - headers: { - "Content-Type": "application/json", - "X-Parse-Application-Id": - localStorage.getItem("parseAppId"), - "X-Parse-Session-Token": - localStorage.getItem("accesstoken") - } - } - ); - - if (res.data && res.data.objectId) { - setActLoader({}); - setIsAlert(true); - setTimeout(() => setIsAlert(false), 1500); - navigate(`/${act.redirectUrl}/${res.data.objectId}`, { - state: { title: "Use Template" } - }); + } + } + const tenantSignTypes = await fetchTenantDetails(); + const docSignTypes = Doc?.SignatureType || signatureTypes; + const updatedSignatureType = await handleSignatureType( + tenantSignTypes, + docSignTypes + ); + const SignatureType = + updatedSignatureType.length > 0 + ? { SignatureType: updatedSignatureType } + : {}; + const NotifyOnSignatures = + Doc?.NotifyOnSignatures !== undefined + ? { NotifyOnSignatures: Doc.NotifyOnSignatures } + : {}; + const Bcc = Doc?.Bcc?.length > 0 ? { Bcc: Doc?.Bcc } : {}; + const RedirectUrl = Doc?.RedirectUrl + ? { RedirectUrl: Doc?.RedirectUrl } + : {}; + let placeholdersArr = []; + if (Doc.Placeholders?.length > 0) { + placeholdersArr = Doc.Placeholders; + const data = { + Name: Doc.Name, + URL: Doc.URL, + SignedUrl: Doc.SignedUrl, + SentToOthers: Doc?.SentToOthers || false, + Description: Doc.Description, + Note: Doc.Note, + Placeholders: placeholdersArr, + ExtUserPtr: { + __type: "Pointer", + className: "contracts_Users", + objectId: extUserId + }, + CreatedBy: { + __type: "Pointer", + className: "_User", + objectId: creatorId + }, + Signers: signers, + SendinOrder: Doc?.SendinOrder || false, + AutomaticReminders: Doc?.AutomaticReminders || false, + RemindOnceInEvery: Doc?.RemindOnceInEvery || 5, + IsEnableOTP: Doc?.IsEnableOTP || false, + TimeToCompleteDays: parseInt(Doc?.TimeToCompleteDays) || 15, + AllowModifications: Doc?.AllowModifications || false, + ...SignatureType, + ...NotifyOnSignatures, + ...Bcc, + ...RedirectUrl + }; + try { + const res = await axios.post( + `${localStorage.getItem("baseUrl")}classes/contracts_Document`, + data, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } - } catch (err) { - console.log("Err", err); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); - setActLoader({}); } - } else { + ); + if (res.data && res.data.objectId) { setActLoader({}); + navigate(`/${redirectUrl}/${res.data.objectId}`, { + state: { title: "Use Template" } + }); } - } else { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); + } catch (err) { + console.log("Err", err); + showAlert("danger", t("something-went-wrong-mssg")); setActLoader({}); } - } catch (err) { - console.log("err", err); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); + } else { setActLoader({}); } + } else { + showAlert("danger", t("something-went-wrong-mssg")); + setActLoader({}); } - } else { - navigate(`/${act.redirectUrl}?docId=${item?.objectId}`); + } catch (err) { + console.log("err", err); + showAlert("danger", t("something-went-wrong-mssg")); + setActLoader({}); } }; - const handleActionBtn = async (act, item) => { if (act.action === "redirect") { handleURL(item, act); @@ -415,6 +399,8 @@ const ReportTable = (props) => { } else if (act.action === "edit") { setContact(item); setIsModal({ [`edit_${item.objectId}`]: true }); + } else if (act.action === "saveastemplate") { + setIsModal({ [`saveastemplate_${item.objectId}`]: true }); } else if (act.action) { setIsModal({ [`extendexpiry_${item.objectId}`]: true }); } @@ -470,12 +456,7 @@ const ReportTable = (props) => { }); if (res.data && res.data.updatedAt) { setActLoader({}); - setIsAlert(true); - setAlertMsg({ - type: "success", - message: t("record-delete-alert") - }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("success", t("record-delete-alert")); const upldatedList = props.List.filter( (x) => x.objectId !== item.objectId ); @@ -483,12 +464,7 @@ const ReportTable = (props) => { } } catch (err) { console.log("err", err); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("danger", t("something-went-wrong-mssg")); setActLoader({}); } }; @@ -563,12 +539,7 @@ const ReportTable = (props) => { const res = result.data; if (res) { setActLoader({}); - setIsAlert(true); - setAlertMsg({ - type: "success", - message: t("record-revoke-alert") - }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("success", t("record-revoke-alert")); const upldatedList = props.List.filter( (x) => x.objectId !== item.objectId ); @@ -579,12 +550,7 @@ const ReportTable = (props) => { .catch((err) => { console.log("err", err); setReason(""); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("danger", t("something-went-wrong-mssg")); setActLoader({}); }); }; @@ -678,6 +644,7 @@ const ReportTable = (props) => { const signPdf = `${window.location.origin}/login/${encodeBase64}`; const variables = { document_title: doc.Name, + note: doc?.Note || "", sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", @@ -707,6 +674,7 @@ const ReportTable = (props) => { const signPdf = `${window.location.origin}/login/${encodeBase64}`; const variables = { document_title: doc.Name, + note: doc?.Note || "", sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", @@ -748,6 +716,7 @@ const ReportTable = (props) => { const signPdf = `${window.location.origin}/login/${encodeBase64}`; const variables = { document_title: doc.Name, + note: doc?.Note || "", sender_name: doc.ExtUserPtr.Name, sender_mail: doc.ExtUserPtr.Email, sender_phone: doc.ExtUserPtr?.Phone || "", @@ -789,25 +758,15 @@ const ReportTable = (props) => { try { const res = await axios.post(url, params, { headers: headers }); if (res?.data?.result?.status === "success") { - setIsAlert(true); - setAlertMsg({ type: "success", message: t("mail-sent-alert") }); + showAlert("success", t("mail-sent-alert")); setIsResendMail({}); } else { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg")); } } catch (err) { console.log("err in sendmail", err); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg")); } finally { - setTimeout(() => setIsAlert(false), 1500); setIsNextStep({}); setUserDetails({}); setActLoader({}); @@ -840,27 +799,10 @@ const ReportTable = (props) => { // `handleQuickSendClose` is trigger when bulk send component trigger close event const handleQuickSendClose = (status, count) => { setIsBulkSend({}); - setIsAlert(true); if (status === "success") { - if (count > 1) { - setAlertMsg({ - type: "success", - message: count + " " + t("document-sent-alert") - }); - setTimeout(() => setIsAlert(false), 1500); - } else { - setAlertMsg({ - type: "success", - message: count + " " + t("document-sent-alert") - }); - setTimeout(() => setIsAlert(false), 1500); - } + showAlert("success", count + " " + t("document-sent-alert")); } else { - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("danger", t("something-went-wrong-mssg")); } }; @@ -899,9 +841,7 @@ const ReportTable = (props) => { } catch (err) { console.log("err in fetch template in bulk modal", err); setIsBulkSend({}); - setIsAlert(true); - setAlertMsg({ type: "danger", message: t("something-went-wrong-mssg") }); - setTimeout(() => setIsAlert(false), 1500); + showAlert("danger", t("something-went-wrong-mssg")); } }; @@ -925,21 +865,12 @@ const ReportTable = (props) => { templateCls.set("SharedWith", teamArr); const res = await templateCls.save(); if (res) { - setIsAlert(true); - setAlertMsg({ - type: "success", - message: t("template-share-alert") - }); + showAlert("success", t("template-share-alert")); } } catch (err) { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg")); } finally { setActLoader({}); - setTimeout(() => setIsAlert(false), 1500); } }; @@ -966,13 +897,13 @@ const ReportTable = (props) => { } }); if (res.data && res.data.updatedAt) { - setIsAlert(true); - setAlertMsg({ - type: "success", - message: t("expiry-date-updated", { + showAlert( + "success", + t("expiry-date-updated", { newexpirydate: new Date(expiryDate)?.toLocaleDateString() - }) - }); + }), + 2000 + ); if (props.ReportName === "Expired Documents") { const upldatedList = props.List.filter( (x) => x.objectId !== item.objectId @@ -982,70 +913,39 @@ const ReportTable = (props) => { } } catch (err) { console.log("err", err); - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg"), 2000); } finally { setActLoader({}); setExpiryDate(); - setTimeout(() => setIsAlert(false), 2000); setIsModal({}); } } else { - setIsAlert(true); - setAlertMsg({ type: "danger", message: t("expiry-date-error") }); - setTimeout(() => setIsAlert(false), 2000); + showAlert("danger", t("expiry-date-error"), 2000); } } else { - setIsAlert(true); - setAlertMsg({ type: "danger", message: t("expiry-date-error") }); - setTimeout(() => setIsAlert(false), 2000); + showAlert("danger", t("expiry-date-error"), 2000); } }; // `formatStatusRow` is used to format status row const formatStatusRow = (item) => { + const timezone = extClass?.[0]?.Timezone || ""; + const DateFormat = extClass?.[0]?.DateFormat || "MM/DD/YYYY"; + const Is12Hr = extClass?.[0]?.Is12HourTime || false; const signers = item?.Placeholders?.map((x, i) => { - const matchSigner = item?.AuditTrail?.find( + const audit = item?.AuditTrail?.find( (audit) => audit?.UserPtr?.objectId === x.signerObjId ); - if (matchSigner) { - const timezone = extClass?.[0]?.Timezone || ""; - const DateFormat = extClass?.[0]?.DateFormat || "MM/DD/YYYY"; - const Is12Hr = extClass?.[0]?.Is12HourTime || false; - const signedon = matchSigner?.SignedOn - ? formatDateTime( - new Date(matchSigner?.SignedOn), - DateFormat, - timezone, - Is12Hr - ) - : "-"; - const viewedon = matchSigner?.ViewedOn - ? formatDateTime( - new Date(matchSigner?.ViewedOn), - DateFormat, - timezone, - Is12Hr - ) + const format = (date) => + date + ? formatDateTime(new Date(date), DateFormat, timezone, Is12Hr) : "-"; - return { - id: i, - Email: matchSigner.UserPtr.Email, - Activity: matchSigner?.Activity?.toUpperCase() || "-", - SignedOn: signedon, - ViewedOn: viewedon - }; - } else { - return { - id: i, - Email: x?.signerPtr?.Email || x?.email, - Activity: "SENT", - SignedOn: "-", - ViewedOn: "-" - }; - } + return { + id: i, + Email: x?.signerPtr?.Email || x?.email, + Activity: audit?.Activity?.toUpperCase() || "SENT", + SignedOn: format(audit?.SignedOn), + ViewedOn: format(audit?.ViewedOn) + }; }); // Decide how many signers to display based on `showAllSignes` state const displaySigners = isShowAllSigners[item.objectId] @@ -1075,7 +975,7 @@ const ReportTable = (props) => { <ModalUi isOpen title={t("document-logs")} - handleClose={() => setIsModal({})} + handleClose={handleCloseModal} > <div className="pl-3 first:mt-2 border-t-[1px] border-gray-600 text-[12px] py-2"> <p className="font-bold"> {x?.Email}</p> @@ -1289,28 +1189,25 @@ const ReportTable = (props) => { const contacts = JSON.stringify(filterdata); const res = await Parse.Cloud.run("createbatchcontact", { contacts }); if (res) { - setIsAlert(true); - setAlertMsg({ - type: "info", - message: t("contact-imported", { + showAlert( + "info", + t("contact-imported", { imported: res?.success || 0, failed: res?.failed || 0 }) - }); + ); if (res?.success > 0) { - setTimeout(() => window.location.reload(), 2000); + setTimeout(() => window.location.reload(), 1500); } } } catch (err) { console.log("err while creating batch contact", err); - setIsAlert(true); - setAlertMsg({ type: "danger", message: t("something-went-wrong-mssg") }); + showAlert("danger", t("something-went-wrong-mssg")); } finally { setActLoader({}); setIsModal({}); setImportedData([]); setInvalidRecords(0); - setTimeout(() => setIsAlert(false), 2000); } }; @@ -1325,22 +1222,13 @@ const ReportTable = (props) => { if (duplicateRes) { const newTemplate = JSON.parse(JSON.stringify(duplicateRes)); props.setList((prevData) => [newTemplate, ...prevData]); - setIsAlert(true); - setAlertMsg({ - type: "success", - message: t("duplicate-template-created") - }); + showAlert("success", t("duplicate-template-created")); } } catch (err) { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg")); console.log("Err while create duplicate template", err); } finally { setActLoader({}); - setTimeout(() => setIsAlert(false), 2000); } }; // `handleRenameDoc` is used to update document name @@ -1361,21 +1249,11 @@ const ReportTable = (props) => { x.objectId === item.objectId ? { ...x, Name: renameDoc } : x ); props.setList(updateList); - setIsAlert(true); - setAlertMsg({ - type: "success", - message: "Document updated" - }); setActLoader({}); - setTimeout(() => setIsAlert(false), 2000); + showAlert("success", "Document updated", 2000); } catch (err) { - setIsAlert(true); - setAlertMsg({ - type: "danger", - message: t("something-went-wrong-mssg") - }); + showAlert("danger", t("something-went-wrong-mssg"), 2000); setActLoader({}); - setTimeout(() => setIsAlert(false), 2000); } }; const handleBtnVisibility = (act, item) => { @@ -1399,6 +1277,65 @@ const ReportTable = (props) => { const handleCloseModal = () => { setIsModal({}); }; + const handleSaveAsTemplate = async (doc) => { + try { + const params = { docId: doc?.objectId }; + const templateRes = await Parse.Cloud.run("saveastemplate", params); + // console.log("templateRes ", templateRes); + setTemplateId(templateRes?.id); + setIsSuccess({ [doc.objectId]: true }); + } catch (err) { + console.log("Err in saveastemplate", err); + } finally { + setActLoader({}); + } + }; + const handleCloseTemplate = () => { + setTemplateId(""); + setIsSuccess({}); + handleCloseModal(); + setActLoader({}); + handleClose(); + }; + + // `handleBulkSend` is used to open modal as well as fetch template + // and show Ui on the basis template response + const handleBulkSendTemplate = async (templateId, docId) => { + setIsBulkSend({ [docId]: true }); + setIsLoader({ [docId]: true }); + try { + const params = { + templateId: templateId, + include: ["Placeholders.signerPtr"] + }; + const axiosRes = await axios.post( + `${localStorage.getItem("baseUrl")}functions/getTemplate`, + params, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + sessionToken: localStorage.getItem("accesstoken") + } + } + ); + const templateRes = axiosRes.data && axiosRes.data.result; + const tenantSignTypes = await fetchTenantDetails(); + const docSignTypes = templateRes?.SignatureType || signatureTypes; + const updatedSignatureType = await handleSignatureType( + tenantSignTypes, + docSignTypes + ); + setSignatureType(updatedSignatureType); + setPlaceholders(templateRes?.Placeholders); + setTemplateDetails(templateRes); + setIsLoader({}); + } catch (err) { + console.log("err in fetch template in bulk modal", err); + setIsBulkSend({}); + showAlert("danger", t("something-went-wrong-mssg")); + } + }; return ( <div className="relative"> {Object.keys(actLoader)?.length > 0 && ( @@ -1407,7 +1344,9 @@ const ReportTable = (props) => { </div> )} <div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg"> - {isAlert && <Alert type={alertMsg.type}>{alertMsg.message}</Alert>} + {alertMsg.message && ( + <Alert type={alertMsg.type}>{alertMsg.message}</Alert> + )} {props.tourData && props.ReportName === "Templates" && ( <> <Tour @@ -1781,7 +1720,7 @@ const ReportTable = (props) => { isOpen={ isModal["duplicate_" + item.objectId] } - handleClose={() => setIsModal({})} + handleClose={handleCloseModal} > <div className=" flex flex-col px-4 pb-3 pt-2 "> <p className="text-base"> @@ -1798,7 +1737,7 @@ const ReportTable = (props) => { </button> <button className="w-[100px] op-btn op-btn-secondary op-btn-md" - onClick={() => setIsModal({})} + onClick={handleCloseModal} > {t("no")} </button> @@ -1869,12 +1808,93 @@ const ReportTable = (props) => { ) )} </div> + {isModal["saveastemplate_" + item.objectId] && ( + <ModalUi + isOpen + title={ + isSuccess[item.objectId] + ? t("template-created") + : t("btnLabel.Save as template") + } + handleClose={handleCloseTemplate} + > + {isSuccess[item.objectId] ? ( + <div className="mx-[10px] my-[15px]"> + <p className="text-base text-center"> + {t("how-would-you-like-to-proceed?")} + </p> + <div className="flex flex-wrap gap-1 items-center justify-center mt-2"> + <button + className="op-btn-primary op-btn op-btn-sm focus:outline-none text-sm relative" + onClick={() => + handleUseTemplate( + templateId, + "placeHolderSign" + ) + } + > + <i className="fa-light fa-plus"></i>{" "} + {t("btnLabel.Use")} + </button> + <button + className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative" + onClick={() => + handleBulkSendTemplate( + templateId, + item.objectId + ) + } + > + <i className="fa-light fa-plus"></i>{" "} + {`${t(`btnLabel.Quick send`)}`} + </button> + <button + className="op-btn-secondary op-btn op-btn-sm focus:outline-none text-sm relative" + onClick={() => + navigate(`/template/${templateId}`) + } + > + <i className="fa-light fa-pen"></i>{" "} + {t(`btnLabel.Edit`)} + </button> + </div> + <Link + to="/report/6TeaPr321t" + className="cursor-pointer underline text-sm w-full flex justify-center mt-2" + > + {t("go-to-manage-templates")} + </Link> + </div> + ) : ( + <div className="m-[20px]"> + <div className="text-lg font-normal text-black"> + {t("save-as-template-?")} + </div> + <hr className="bg-[#ccc] mt-3" /> + <div className="flex items-center mt-3 gap-2 text-white"> + <button + onClick={() => handleSaveAsTemplate(item)} + className="op-btn op-btn-primary w-[100px]" + > + {t("yes")} + </button> + <button + onClick={handleCloseTemplate} + className="op-btn op-btn-secondary w-[100px]" + > + {t("no")} + </button> + </div> + </div> + )} + </ModalUi> + )} {isModal["extendexpiry_" + item.objectId] && ( <ModalUi isOpen title={t("btnLabel.extend-expiry-date")} reduceWidth={"md:max-w-[450px]"} - handleClose={() => setIsModal({})} + handleClose={handleCloseModal} > <form className="px-4 py-2 flex flex-col" @@ -2233,7 +2253,7 @@ const ReportTable = (props) => { <ModalUi title={t("btnLabel.Rename")} isOpen={isModal["rename_" + item.objectId]} - handleClose={() => setIsModal({})} + handleClose={handleCloseModal} > <div className=" flex flex-col px-4 pb-3 pt-2 "> <div className="flex flex-col gap-2"> @@ -2255,7 +2275,7 @@ const ReportTable = (props) => { </button> <button className="w-[100px] op-btn op-btn-secondary op-btn-md" - onClick={() => setIsModal({})} + onClick={handleCloseModal} > {t("cancel")} </button> diff --git a/apps/OpenSign/src/primitives/sanitizeFileName.js b/apps/OpenSign/src/primitives/sanitizeFileName.js index 7c1126e357..bf796913fb 100644 --- a/apps/OpenSign/src/primitives/sanitizeFileName.js +++ b/apps/OpenSign/src/primitives/sanitizeFileName.js @@ -1,7 +1,7 @@ function sanitizeFileName(fileName) { // Remove spaces and invalid characters - const file = fileName.replace(/[^a-zA-Z0-9._-]/g, ""); - const removedot = file.replace(/\.(?=.*\.)/g, ""); - return removedot.replace(/[^a-zA-Z0-9._-]/g, ""); + const file = fileName?.replace(/[^a-zA-Z0-9._-]/g, ""); + const removedot = file?.replace(/\.(?=.*\.)/g, ""); + return removedot?.replace(/[^a-zA-Z0-9._-]/g, ""); } export default sanitizeFileName; diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 8b9eb4f682..af86dd93e2 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -49,7 +49,7 @@ export const saveFileUsage = async (size, fileUrl, userId) => { className: '_User', objectId: userId, }); - const tenant = await tenantQuery.first(); + const tenant = await tenantQuery.first({ useMasterKey: true }); if (tenant) { const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id }; try { @@ -225,6 +225,7 @@ export const flattenPdf = async pdfFile => { export const mailTemplate = param => { const themeColor = '#47a3ad'; const subject = `${param.senderName} has requested you to sign "${param.title}"`; + const AppName = appName; const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' />`; const opurl = ` <a href='www.opensignlabs.com' target=_blank>here</a>`; @@ -240,15 +241,17 @@ export const mailTemplate = param => { param.senderMail + "</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'> " + param.organization + - "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td></td><td style='color:#626363;font-weight:bold'>" + + "</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'>" + param.localExpireDate + + "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" + + param.note + "</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" + param.sigingUrl + "><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;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 " + - appName + + AppName + '. For any queries regarding this email, please contact the sender ' + param.senderMail + - ` directly. If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`; + ` directly. If you think this email is inappropriate or spam, you may file a complaint with ${AppName}${opurl}.</p></div></div></body></html>`; return { subject, body }; }; diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index b2017c56d6..f96167d1c2 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -12,7 +12,6 @@ import getDrive from './parsefunction/getDrive.js'; import getReport from './parsefunction/getReport.js'; import TemplateAfterSave from './parsefunction/TemplateAfterSave.js'; import GetTemplate from './parsefunction/GetTemplate.js'; -import callWebhook from './parsefunction/callWebhook.js'; import DocumentBeforesave from './parsefunction/DocumentBeforesave.js'; import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js'; import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js'; @@ -50,6 +49,8 @@ import fileUpload from './parsefunction/fileUpload.js'; import getUserListByOrg from './parsefunction/getUserListByOrg.js'; import editContact from './parsefunction/editContact.js'; import forwardDoc from './parsefunction/ForwardDoc.js'; +import saveAsTemplate from './parsefunction/saveAsTemplate.js'; +import updateTenant from './parsefunction/updateTenant.js'; // This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic. Parse.Cloud.afterSave('contracts_Document', DocumentAftersave); @@ -80,7 +81,6 @@ Parse.Cloud.define('getDocument', getDocument); Parse.Cloud.define('getDrive', getDrive); Parse.Cloud.define('getReport', getReport); Parse.Cloud.define('getTemplate', GetTemplate); -Parse.Cloud.define('callwebhook', callWebhook); Parse.Cloud.define('verifyemail', VerifyEmail); Parse.Cloud.define('getsignedurl', getSignedUrl); Parse.Cloud.define('batchdocuments', createBatchDocs); @@ -110,3 +110,5 @@ Parse.Cloud.define('fileupload', fileUpload); Parse.Cloud.define('getuserlistbyorg', getUserListByOrg); Parse.Cloud.define('editcontact', editContact); Parse.Cloud.define('forwarddoc', forwardDoc); +Parse.Cloud.define('saveastemplate', saveAsTemplate); +Parse.Cloud.define('updatetenant', updateTenant); diff --git a/apps/OpenSignServer/cloud/parsefunction/AddAdmin.js b/apps/OpenSignServer/cloud/parsefunction/AddAdmin.js index 8e239fa9d0..a9e77dbdb4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/AddAdmin.js +++ b/apps/OpenSignServer/cloud/parsefunction/AddAdmin.js @@ -61,7 +61,7 @@ async function addTeamAndOrg(extUser) { async function saveUser(userDetails) { const userQuery = new Parse.Query(Parse.User); - userQuery.equalTo('username', userDetails.email); + userQuery.equalTo('username', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); const userRes = await userQuery.first({ useMasterKey: true }); if (userRes) { @@ -83,9 +83,9 @@ async function saveUser(userDetails) { return { id: login.objectId, sessionToken: login.sessionToken }; } else { const user = new Parse.User(); - user.set('username', userDetails.email); + user.set('username', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); user.set('password', userDetails.password); - user.set('email', userDetails.email); + user.set('email', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); if (userDetails?.phone) { user.set('phone', userDetails.phone); } @@ -111,7 +111,6 @@ export default async function AddAdmin(request) { if (extUser) { return { message: 'User already exist' }; } else { - // console.log("role ", role); const partnerQuery = new Parse.Object('partners_Tenant'); partnerQuery.set('UserId', { __type: 'Pointer', @@ -123,7 +122,7 @@ export default async function AddAdmin(request) { partnerQuery.set('ContactNumber', userDetails.phone); } partnerQuery.set('TenantName', userDetails.company); - partnerQuery.set('EmailAddress', userDetails.email); + partnerQuery.set('EmailAddress', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); partnerQuery.set('IsActive', true); partnerQuery.set('CreatedBy', { __type: 'Pointer', @@ -154,7 +153,7 @@ export default async function AddAdmin(request) { objectId: user.id, }); newObj.set('UserRole', userDetails.role); - newObj.set('Email', userDetails.email); + newObj.set('Email', userDetails.email?.toLowerCase()?.replace(/\s/g, '')); newObj.set('Name', userDetails.name); if (userDetails?.phone) { newObj.set('Phone', userDetails?.phone); @@ -177,7 +176,7 @@ export default async function AddAdmin(request) { const extUser = { objectId: extRes.id, Name: userDetails.name, - Email: userDetails.email, + Email: userDetails.email?.toLowerCase()?.replace(/\s/g, ''), Phone: userDetails?.phone ? userDetails.phone : '', TenantId: { objectId: tenantRes.id }, UserId: { objectId: user.id }, diff --git a/apps/OpenSignServer/cloud/parsefunction/ForwardDoc.js b/apps/OpenSignServer/cloud/parsefunction/ForwardDoc.js index 803ead8f64..226aeec0f9 100644 --- a/apps/OpenSignServer/cloud/parsefunction/ForwardDoc.js +++ b/apps/OpenSignServer/cloud/parsefunction/ForwardDoc.js @@ -28,6 +28,7 @@ export default async function forwardDoc(request) { const docName = _docRes.Name; const fileAdapterId = _docRes?.FileAdapterId || ''; const extUserId = _docRes?.ExtUserPtr?.objectId; + const TenantAppName = appName; const from = _docRes?.ExtUserPtr?.Email; const replyTo = _docRes?.ExtUserPtr?.Email; const senderName = _docRes?.ExtUserPtr?.Name; @@ -51,8 +52,8 @@ export default async function forwardDoc(request) { `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'><div>` + `${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` + `<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${docName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` + - `</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` + - `If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`, + `</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` + + `If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`, }; mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, { headers: { diff --git a/apps/OpenSignServer/cloud/parsefunction/GetLogoByDomain.js b/apps/OpenSignServer/cloud/parsefunction/GetLogoByDomain.js index 0e081297c9..11cb4b1495 100644 --- a/apps/OpenSignServer/cloud/parsefunction/GetLogoByDomain.js +++ b/apps/OpenSignServer/cloud/parsefunction/GetLogoByDomain.js @@ -6,13 +6,17 @@ export default async function GetLogoByDomain(request) { try { const tenantCreditsQuery = new Parse.Query('partners_Tenant'); tenantCreditsQuery.equalTo('Domain', domain); - const res = await tenantCreditsQuery.first(); + const res = await tenantCreditsQuery.first({ useMasterKey: true }); if (res) { const updateRes = JSON.parse(JSON.stringify(res)); - return { logo: updateRes?.Logo, appname: appName, user: 'exist' }; + return { + logo: updateRes?.Logo, + appname: appName, + user: 'exist', + }; } else { const tenantCreditsQuery = new Parse.Query('partners_Tenant'); - const tenantRes = await tenantCreditsQuery.first(); + const tenantRes = await tenantCreditsQuery.first({ useMasterKey: true }); if (tenantRes) { return { logo: '', appname: appName, user: 'exist' }; } else { diff --git a/apps/OpenSignServer/cloud/parsefunction/Newsletter.js b/apps/OpenSignServer/cloud/parsefunction/Newsletter.js index d02b9b3ff7..e26c8d97ea 100644 --- a/apps/OpenSignServer/cloud/parsefunction/Newsletter.js +++ b/apps/OpenSignServer/cloud/parsefunction/Newsletter.js @@ -1,7 +1,7 @@ import axios from 'axios'; export default async function Newsletter(request) { const name = request.params.name; - const email = request.params.email; + const email = request.params?.email?.toLowerCase()?.replace(/\s/g, ''); const domain = request.params.domain; try { const envAppId = process.env.REACT_APP_APPID || 'opensign'; diff --git a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js index a66b69c3bd..e60dbafa0a 100644 --- a/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js +++ b/apps/OpenSignServer/cloud/parsefunction/SendMailOTPv1.js @@ -22,19 +22,20 @@ async function sendMailOTPv1(request) { //--for elearning app side let code = Math.floor(1000 + Math.random() * 9000); let email = request.params.email; - var TenantId = request.params.TenantId ? request.params.TenantId : undefined; + let TenantId = request.params.TenantId ? request.params.TenantId : undefined; + const AppName = appName; if (email) { const recipient = request.params.email; const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER; try { await Parse.Cloud.sendEmail({ - from: appName + ' <' + mailsender + '>', + from: AppName + ' <' + mailsender + '>', recipient: recipient, - subject: `Your ${appName} OTP`, + subject: `Your ${AppName} OTP`, text: 'This email is a test.', html: - `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for ${appName} verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` + + `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for ${AppName} verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` + code + '</p></div> </div> </div></body></html>', }); diff --git a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js b/apps/OpenSignServer/cloud/parsefunction/callWebhook.js deleted file mode 100644 index e888beb9a9..0000000000 --- a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js +++ /dev/null @@ -1,108 +0,0 @@ -import axios from 'axios'; -import { cloudServerUrl } from '../../Utils.js'; -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 = cloudServerUrl; //process.env.SERVER_URL; - const appId = process.env.APP_ID; - try { - const docQuery = new Parse.Query('contracts_Document'); - docQuery.include('ExtUserPtr.TenantId'); - const docRes = await docQuery.get(docId, { useMasterKey: true }); - const isEnableOTP = docRes?.get('IsEnableOTP') || false; - let userId; - if (isEnableOTP) { - const userRes = await axios.get(serverUrl + '/users/me', { - headers: { - 'X-Parse-Application-Id': appId, - 'X-Parse-Session-Token': request.headers['sessiontoken'], - }, - }); - userId = userRes.data && userRes.data.objectId; - } - if (!isEnableOTP || userId) { - if (event === 'viewed' && contactId) { - if (docRes) { - const _docRes = docRes.toJSON(); - const userPtr = { - __type: 'Pointer', - className: 'contracts_Contactbook', - objectId: contactId, - }; - const date = new Date().toISOString(); - const obj = { - UserPtr: userPtr, - SignedUrl: _docRes.SignedUrl, - Activity: 'Viewed', - ipAddress: request.headers['x-real-ip'], - ViewedOn: date, - }; - const isUserExist = _docRes?.AuditTrail?.some( - x => x.UserPtr.objectId === contactId && x?.ViewedOn - ); - if (!isUserExist) { - const updateDoc = new Parse.Object('contracts_Document'); - updateDoc.id = docRes.id; - if (_docRes?.AuditTrail && _docRes?.AuditTrail?.length > 0) { - updateDoc.set('AuditTrail', [..._docRes?.AuditTrail, obj]); - } else { - updateDoc.set('AuditTrail', [obj]); - } - await updateDoc.save(null, { useMasterKey: true }); - } - } - } - const extendcls = new Parse.Query('contracts_Users'); - extendcls.equalTo('objectId', docRes.get('ExtUserPtr')?.id); - // extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId }); - const resExt = await extendcls.first({ useMasterKey: true }); - if (resExt) { - const extUser = JSON.parse(JSON.stringify(resExt)); - if (extUser?.Webhook) { - const params = { event: event, ...body }; - await axios - .post(extUser?.Webhook, params, { - headers: { 'Content-Type': 'application/json' }, - }) - .then(res => { - try { - const webhook = new Parse.Object('contracts_Webhook'); - webhook.set('Log', res?.status); - webhook.set('UserId', { - __type: 'Pointer', - className: '_User', - objectId: userId, - }); - webhook.save(null, { useMasterKey: true }); - } catch (err) { - console.log('err save in contracts_Webhook', err.message); - } - }) - .catch(err => { - console.log('Err send data to webhook', err.message); - try { - const webhook = new Parse.Object('contracts_Webhook'); - webhook.set('Log', err?.status); - webhook.set('UserId', { - __type: 'Pointer', - className: '_User', - objectId: userId, - }); - webhook.save(null, { useMasterKey: true }); - } catch (err) { - console.log('err save in contracts_Webhook', err.message); - } - }); - } - return { message: 'webhook called!' }; - } - } else { - return { message: 'User not found!' }; - } - } catch (err) { - console.log('Err in callwebhook', err); - return { message: 'Something went wrong!' }; - } -} diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchContact.js b/apps/OpenSignServer/cloud/parsefunction/createBatchContact.js index b364c97ceb..51ce40a620 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchContact.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchContact.js @@ -21,7 +21,7 @@ export default async function createBatchContact(req) { TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId }, CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id }, Name: x.Name, - Email: x.Email, + Email: x.Email?.toLowerCase()?.replace(/\s/g, ''), IsDeleted: false, IsImported: true, ...(x?.Phone ? { Phone: `${x?.Phone}` } : {}), diff --git a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js index 440bc8330a..ab280c0e53 100644 --- a/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js +++ b/apps/OpenSignServer/cloud/parsefunction/createBatchDocs.js @@ -1,14 +1,8 @@ import axios from 'axios'; -import { - cloudServerUrl, - replaceMailVaribles, -} from '../../Utils.js'; +import { cloudServerUrl, replaceMailVaribles } from '../../Utils.js'; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const appId = process.env.APP_ID; -async function deductcount( - docsCount, - extUserId, -) { +async function deductcount(docsCount, extUserId) { try { const extCls = new Parse.Object('contracts_Users'); extCls.id = extUserId; @@ -18,9 +12,9 @@ async function deductcount( console.log('Err in deduct in quick send', err); } } -async function sendMail(document) { +async function sendMail(document, publicUrl) { //sessionToken - const baseUrl = new URL(process.env.PUBLIC_URL); + const baseUrl = new URL(publicUrl); //process.env.PUBLIC_URL // console.log("pdfDetails", pdfDetails); const timeToCompleteDays = document?.TimeToCompleteDays || 15; @@ -33,10 +27,8 @@ async function sendMail(document) { year: 'numeric', }); let signerMail = document.Placeholders; - const senderName = - document.ExtUserPtr.Name; - const senderEmail = - document.ExtUserPtr.Email; + const senderName = document.ExtUserPtr.Name; + const senderEmail = document.ExtUserPtr.Email; if (document.SendinOrder) { signerMail = signerMail.slice(); @@ -70,6 +62,7 @@ async function sendMail(document) { '</body></html>'; const variables = { document_title: document?.Name, + note: document?.Note || '', sender_name: senderName, sender_mail: senderEmail, sender_phone: senderObj?.Phone || '', @@ -83,6 +76,7 @@ async function sendMail(document) { replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables); } const mailparam = { + note: document?.Note || '', senderName: senderName, senderMail: senderEmail, title: document.Name, @@ -94,8 +88,7 @@ async function sendMail(document) { extUserId: document.ExtUserPtr.objectId, recipient: objectId ? existSigner?.Email : signerMail[i].email, subject: replaceVar?.subject ? replaceVar?.subject : mailTemplate(mailparam).subject, - from: - document.ExtUserPtr.Email, + from: document.ExtUserPtr.Email, replyto: senderEmail || '', html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body, }; @@ -108,13 +101,7 @@ async function sendMail(document) { } } } -async function batchQuery( - userId, - Documents, - Ip, - parseConfig, - type -) { +async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) { const extCls = new Parse.Query('contracts_Users'); extCls.equalTo('UserId', { __type: 'Pointer', @@ -199,22 +186,22 @@ async function batchQuery( }; }); // console.log('requests ', requests); - if (requests?.length > 0) { - const newrequests = [requests?.[0]]; - const response = await axios.post('batch', { requests: newrequests }, parseConfig); - // Handle the batch query response - // console.log('Batch query response:', response.data); - if (response.data && response.data.length > 0) { - const document = Documents?.[0]; - const updateDocuments = { - ...document, - objectId: response.data[0]?.success?.objectId, - createdAt: response.data[0]?.success?.createdAt, - }; - deductcount(response.data.length, resExt.id); - sendMail(updateDocuments); //sessionToken - return 'success'; - } + if (requests?.length > 0) { + const newrequests = [requests?.[0]]; + const response = await axios.post('batch', { requests: newrequests }, parseConfig); + // Handle the batch query response + // console.log('Batch query response:', response.data); + if (response.data && response.data.length > 0) { + const document = Documents?.[0]; + const updateDocuments = { + ...document, + objectId: response.data[0]?.success?.objectId, + createdAt: response.data[0]?.success?.createdAt, + }; + deductcount(response.data.length, resExt.id); + sendMail(updateDocuments, publicUrl); //sessionToken + return 'success'; + } } } catch (error) { const code = error?.response?.data?.code || error?.response?.status || error?.code || 400; @@ -236,6 +223,8 @@ export default async function createBatchDocs(request) { const type = request.headers?.type || 'quicksend'; const Documents = JSON.parse(strDocuments); const Ip = request?.headers?.['x-real-ip'] || ''; + // Access the host from the headers + const publicUrl = request.headers.public_url; const parseConfig = { baseURL: serverUrl, headers: { @@ -246,9 +235,8 @@ export default async function createBatchDocs(request) { }; try { if (request?.user) { - return await batchQuery(request.user.id, Documents, Ip, parseConfig, '', type); - } - else { + return await batchQuery(request.user.id, Documents, Ip, parseConfig, '', type, publicUrl); + } else { throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); } } catch (err) { diff --git a/apps/OpenSignServer/cloud/parsefunction/editContact.js b/apps/OpenSignServer/cloud/parsefunction/editContact.js index dbe3b33748..2bd396d0d9 100644 --- a/apps/OpenSignServer/cloud/parsefunction/editContact.js +++ b/apps/OpenSignServer/cloud/parsefunction/editContact.js @@ -15,7 +15,7 @@ export default async function editContact(request) { const query = new Parse.Query('contracts_Contactbook'); query.equalTo('CreatedBy', createdBy); query.notEqualTo('IsDeleted', true); - query.equalTo('Email', email); + query.equalTo('Email', email?.toLowerCase()?.replace(/\s/g, '')); const isContactExist = await query.first({ useMasterKey: true }); if (isContactExist) { throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.'); @@ -25,7 +25,7 @@ export default async function editContact(request) { if (phone) { contactQuery.set('Phone', phone); } - contactQuery.set('Email', email); + contactQuery.set('Email', email?.toLowerCase()?.replace(/\s/g, '')); contactQuery.set('UserRole', 'contracts_Guest'); contactQuery.set('IsDeleted', false); contactQuery.set('TenantId', { @@ -37,9 +37,9 @@ export default async function editContact(request) { const _users = Parse.Object.extend('User'); const _user = new _users(); _user.set('name', name); - _user.set('username', email); - _user.set('email', email); - _user.set('password', email); + _user.set('username', email?.toLowerCase()?.replace(/\s/g, '')); + _user.set('email', email?.toLowerCase()?.replace(/\s/g, '')); + _user.set('password', email?.toLowerCase()?.replace(/\s/g, '')); if (phone) { _user.set('phone', phone); } diff --git a/apps/OpenSignServer/cloud/parsefunction/linkContactToDoc.js b/apps/OpenSignServer/cloud/parsefunction/linkContactToDoc.js index f46bfc5da9..15cc200d49 100644 --- a/apps/OpenSignServer/cloud/parsefunction/linkContactToDoc.js +++ b/apps/OpenSignServer/cloud/parsefunction/linkContactToDoc.js @@ -52,7 +52,8 @@ const saveRoleContact = async contact => { // `linkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and // save contact pointer in placeholder, signers and ACL of Document export default async function linkContactToDoc(req) { - const email = req.params.email; + const requestemail = req.params?.email; + const email = requestemail?.toLowerCase()?.replace(/\s/g, ''); const docId = req.params.docId; const name = req.params.name; const phone = req.params.phone; diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.js index 2008f49534..dd9ff23e3f 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.js @@ -19,9 +19,6 @@ const APPID = process.env.APP_ID; const masterKEY = process.env.MASTER_KEY; const eSignName = 'OpenSign'; const eSigncontact = 'hello@opensignlabs.com'; -const logo = - "<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>"; -const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`; // `updateDoc` is used to create url in from pdfFile async function uploadFile(pdfName, filepath) { @@ -93,8 +90,12 @@ async function updateDoc(docId, url, userId, ipAddress, data, className, sign) { } // `sendNotifyMail` is used to send notification mail of signer signed the document -async function sendNotifyMail(doc, signUser, mailProvider) { +async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) { try { + const TenantAppName = appName; + const logo = + "<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>"; + const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`; const auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0; const signersCount = doc?.Placeholders?.length; const remaingsign = signersCount - auditTrailCount; @@ -105,18 +106,18 @@ async function sendNotifyMail(doc, signUser, mailProvider) { const creatorEmail = doc.ExtUserPtr.Email; const signerName = signUser.Name; const signerEmail = signUser.Email; - const viewDocUrl = `${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`; + const viewDocUrl = `${publicUrl}/recipientSignPdf/${doc.objectId}`; // ` ${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`; const subject = `Document "${pdfName}" has been signed by ${signerName}`; const 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='background-color:white'>" + `<div>${logo}</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 by ${signerName}</p>` + `</div><div style='padding:20px;font-family:system-ui;font-size:14px'><p>Dear ${creatorName},</p><p>${pdfName} has been signed by ${signerName} "${signerEmail}" successfully</p>` + - `<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, ` + - `please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`; + `<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, ` + + `please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`; const params = { extUserId: sender.objectId, - from: appName, + from: TenantAppName, recipient: creatorEmail, subject: subject, pdfName: pdfName, @@ -142,6 +143,10 @@ async function sendCompletedMail(obj) { const doc = obj.doc; const sender = obj.doc.ExtUserPtr; const pdfName = doc.Name; + const TenantAppName = appName; + const logo = + "<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>"; + const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`; let signersMail; if (doc?.Signers?.length > 0) { const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email); @@ -157,8 +162,8 @@ async function sendCompletedMail(obj) { "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'>" + `<div>${logo}</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>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` + - `</div></div><div><p>This is an automated email from ${appName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` + - `If you think this email is inappropriate or spam, you may file a complaint with ${appName}${opurl}.</p></div></div></body></html>`; + `</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` + + `If you think this email is inappropriate or spam, you may file a complaint with ${TenantAppName}${opurl}.</p></div></div></body></html>`; if (obj?.isCustomMail) { const tenant = sender?.TenantId; @@ -175,7 +180,7 @@ async function sendCompletedMail(obj) { className: '_User', objectId: userId, }); - const tenantRes = await tenantQuery.first(); + const tenantRes = await tenantQuery.first({ useMasterKey: true }); if (tenantRes) { const _tenantRes = JSON.parse(JSON.stringify(tenantRes)); subject = _tenantRes?.CompletionSubject || ''; @@ -214,7 +219,7 @@ async function sendCompletedMail(obj) { const params = { extUserId: sender.objectId, url: url, - from: appName, + from: TenantAppName, replyto: doc?.ExtUserPtr?.Email || '', recipient: recipient, subject: subject, @@ -301,6 +306,7 @@ async function PDF(req) { const isCustomMail = req.params.isCustomCompletionMail || false; const mailProvider = req.params.mailProvider || ''; const sign = req.params.signature || ''; + const publicUrl = req.headers.public_url; // below bode is used to get info of docId const docQuery = new Parse.Query('contracts_Document'); docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId,Bcc'); @@ -426,7 +432,7 @@ async function PDF(req) { className, // className based on flow sign // sign base64 ); - sendNotifyMail(_resDoc, signUser, mailProvider); + sendNotifyMail(_resDoc, signUser, mailProvider, publicUrl); saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId); if (updatedDoc && updatedDoc.isCompleted) { const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl }; diff --git a/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js b/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js new file mode 100644 index 0000000000..85433c476a --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/saveAsTemplate.js @@ -0,0 +1,87 @@ +const randomId = () => Math.floor(1000 + Math.random() * 9000); +export default async function saveAsTemplate(request) { + const docId = request.params.docId; + const Ip = request?.headers?.['x-real-ip'] || ''; + + if (!request.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'user is not authenticated.'); + } + try { + const docQuery = new Parse.Query('contracts_Document'); + docQuery.equalTo('objectId', docId); + docQuery.equalTo('CreatedBy', request.user); + docQuery.include('ExtUserPtr'); + docQuery.include('ExtUserPtr.TenantId'); + docQuery.notEqualTo('IsArchive', true); + const docRes = await docQuery.first({ useMasterKey: true }); + if (docRes) { + const _docRes = docRes?.toJSON(); + const templateCls = new Parse.Object('contracts_Template'); + templateCls.set('URL', _docRes?.URL); + templateCls.set('Name', _docRes?.Name); + templateCls.set('Note', _docRes?.Note); + templateCls.set('Description', _docRes?.Description); + templateCls.set('OriginIp', Ip); + templateCls.set('SendinOrder', _docRes?.SendinOrder || false); + templateCls.set('AutomaticReminders', _docRes?.AutomaticReminders || false); + templateCls.set('ExtUserPtr', _docRes?.ExtUserPtr); + templateCls.set('CreatedBy', _docRes?.CreatedBy); + templateCls.set('IsEnableOTP', _docRes?.IsEnableOTP === true ? true : false); + templateCls.set('IsTourEnabled', _docRes?.IsTourEnabled === true ? true : false); + templateCls.set('AllowModifications', _docRes?.AllowModifications || false); + templateCls.set('EmailSenderName', _docRes?.EmailSenderName); + templateCls.set('SenderName', _docRes?.SenderName); + templateCls.set('SenderMail', _docRes?.SenderMail); + templateCls.set('FileAdapterId', _docRes?.FileAdapterId); + templateCls.set('RequestBody', _docRes?.RequestBody); + templateCls.set('RequestSubject', _docRes?.RequestSubject); + templateCls.set('NextReminderDate', _docRes?.NextReminderDate); + templateCls.set('RedirectUrl', _docRes?.RedirectUrl); + templateCls.set( + 'NotifyOnSignatures', + _docRes?.NotifyOnSignatures !== undefined ? _docRes?.NotifyOnSignatures : false + ); + templateCls.set( + 'TimeToCompleteDays', + _docRes?.TimeToCompleteDays ? parseInt(_docRes?.TimeToCompleteDays) : 15 + ); + if (_docRes?.RemindOnceInEvery) { + templateCls.set('RemindOnceInEvery', parseInt(_docRes?.RemindOnceInEvery)); + } + + if (_docRes?.Placeholders?.length > 0) { + if (_docRes?.IsSignyourself) { + const placeHolders = { + signerObjId: '', + signerPtr: {}, + Id: randomId(), + blockColor: '#93a3db', + Role: 'Role 1', + email: '', + placeHolder: _docRes?.Placeholders, + }; + templateCls.set('Placeholders', [placeHolders]); + } else { + const placeHolders = _docRes?.Placeholders?.map((x, i) => { + const email = x.email ? { email: '' } : {}; + return { ...x, signerObjId: '', signerPtr: {}, Role: 'Role ' + (i + 1), ...email }; + }); + templateCls.set('Placeholders', placeHolders); + } + } + if (_docRes?.SignatureType?.length > 0) { + templateCls.set('SignatureType', _docRes?.SignatureType); + } + if (_docRes?.Bcc?.length > 0) { + templateCls.set('Bcc', _docRes?.Bcc); + } + const res = await templateCls.save(null, { useMasterKey: true }); + return res; + } else { + throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'document not found.'); + } + } catch (err) { + console.log('Err in save as template', err); + throw err; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/savecontact.js b/apps/OpenSignServer/cloud/parsefunction/savecontact.js index fdd8e462fb..6cbfaac637 100644 --- a/apps/OpenSignServer/cloud/parsefunction/savecontact.js +++ b/apps/OpenSignServer/cloud/parsefunction/savecontact.js @@ -1,7 +1,8 @@ export default async function savecontact(request) { const name = request.params.name; const phone = request.params.phone; - const email = request.params.email; + const requestemail = request.params?.email; + const email = requestemail?.toLowerCase()?.replace(/\s/g, ''); const tenantId = request.params.tenantId; if (request.user) { diff --git a/apps/OpenSignServer/cloud/parsefunction/updateTenant.js b/apps/OpenSignServer/cloud/parsefunction/updateTenant.js new file mode 100644 index 0000000000..9bcf7eccb1 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/updateTenant.js @@ -0,0 +1,27 @@ +export default async function updateTenant(request) { + const { tenantId, details } = request.params; + + if (!tenantId || !details) { + throw new Parse.Error(400, 'Missing tenantId or details.'); + } + + if (!request.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized'); + } + try { + const tenant = new Parse.Object('partners_Tenant'); + tenant.id = tenantId; + // Update tenant details + Object.keys(details).forEach(key => { + tenant.set(key, details?.[key]); + }); + + const tenantRes = await tenant.save(null, { useMasterKey: true }); + if (tenantRes) { + const res = JSON.parse(JSON.stringify(tenantRes)); + return res; + } + } catch (error) { + throw error; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/usersignup.js b/apps/OpenSignServer/cloud/parsefunction/usersignup.js index de561ccdf8..506051fa49 100644 --- a/apps/OpenSignServer/cloud/parsefunction/usersignup.js +++ b/apps/OpenSignServer/cloud/parsefunction/usersignup.js @@ -1,7 +1,5 @@ import axios from 'axios'; -import { - cloudServerUrl, -} from '../../Utils.js'; +import { cloudServerUrl } from '../../Utils.js'; const serverUrl = cloudServerUrl; //process.env.SERVER_URL; const APPID = process.env.APP_ID; const masterKEY = process.env.MASTER_KEY; @@ -32,7 +30,7 @@ async function saveUser(userDetails) { const user = new Parse.User(); user.set('username', userDetails.email); user.set('password', userDetails.password); - user.set('email', userDetails.email); + user.set('email', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); if (userDetails?.phone) { user.set('phone', userDetails.phone); } @@ -73,7 +71,7 @@ export default async function usersignup(request) { partnerQuery.set('ContactNumber', userDetails.phone); } partnerQuery.set('TenantName', userDetails.company); - partnerQuery.set('EmailAddress', userDetails.email); + partnerQuery.set('EmailAddress', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); partnerQuery.set('IsActive', true); partnerQuery.set('CreatedBy', { __type: 'Pointer', @@ -105,7 +103,7 @@ export default async function usersignup(request) { objectId: user.id, }); newObj.set('UserRole', userDetails.role); - newObj.set('Email', userDetails.email); + newObj.set('Email', userDetails?.email?.toLowerCase()?.replace(/\s/g, '')); newObj.set('Name', userDetails.name); if (userDetails?.phone) { newObj.set('Phone', userDetails?.phone); @@ -131,4 +129,3 @@ export default async function usersignup(request) { console.log('Err ', err); } } - diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 42650c50d6..0fdd1d8f59 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -160,6 +160,8 @@ app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(function (req, res, next) { req.headers['x-real-ip'] = getUserIP(req); + const publicUrl = req.protocol + '://' + req.get('host'); + req.headers['public_url'] = publicUrl; // process.env.PUBLIC_URL next(); }); function getUserIP(request) {