mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
v2.38.0
This commit is contained in:
Generated
+181
-187
@@ -12,7 +12,7 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@imgly/background-removal": "^1.7.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.18.3",
|
||||
"@lottiefiles/dotlottie-react": "^0.18.4",
|
||||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
@@ -31,12 +31,12 @@
|
||||
"@usewaypoint/block-text": "^0.0.7",
|
||||
"@usewaypoint/document-core": "^0.0.6",
|
||||
"@usewaypoint/email-builder": "^0.0.9",
|
||||
"axios": "^1.13.5",
|
||||
"axios": "^1.13.6",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dompurify": "^3.3.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlight.js": "^11.9.0",
|
||||
"i18next": "^25.8.13",
|
||||
"i18next": "^25.8.17",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -49,6 +49,7 @@
|
||||
"prettier": "^3.8.1",
|
||||
"print-js": "^1.6.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"quill-html-edit-button": "^3.0.0",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^19.2.4",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
@@ -60,11 +61,12 @@
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-konva": "^19.2.2",
|
||||
"react-i18next": "^16.5.6",
|
||||
"react-konva": "^19.2.3",
|
||||
"react-pdf": "^10.4.1",
|
||||
"react-quill-new": "^3.8.3",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-rnd": "^10.5.3",
|
||||
"react-router": "^7.13.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.2",
|
||||
@@ -74,7 +76,7 @@
|
||||
"react-tooltip": "^5.30.0",
|
||||
"redux": "^5.0.1",
|
||||
"regex-parser": "^2.3.1",
|
||||
"serve": "^14.2.5",
|
||||
"serve": "^14.2.6",
|
||||
"web-vitals": "^5.1.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"zod": "^3.22.4",
|
||||
@@ -97,12 +99,12 @@
|
||||
"concurrently": "^9.2.1",
|
||||
"css-loader": "^7.1.4",
|
||||
"daisyui": "^4.12.24",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"jsdom": "^28.1.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"postcss": "^8.5.6",
|
||||
"lint-staged": "^16.3.3",
|
||||
"postcss": "^8.5.8",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"tailwindcss": "^3.4.19",
|
||||
@@ -2799,15 +2801,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
|
||||
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
|
||||
"integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/object-schema": "^2.1.7",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.1.2"
|
||||
"minimatch": "^3.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2840,9 +2842,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz",
|
||||
"integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==",
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
|
||||
"integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2853,7 +2855,7 @@
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"js-yaml": "^4.1.1",
|
||||
"minimatch": "^3.1.3",
|
||||
"minimatch": "^3.1.5",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2888,9 +2890,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.39.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz",
|
||||
"integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==",
|
||||
"version": "9.39.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz",
|
||||
"integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -3126,21 +3128,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lottiefiles/dotlottie-react": {
|
||||
"version": "0.18.3",
|
||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.18.3.tgz",
|
||||
"integrity": "sha512-/6iVC4y9KCyPKS2gDUFp+omYndo3KPombAsZXB0h5sSyEO/+ydVppd5fIoHg2ogGAPOCPK97Y5Eos65JnyTjww==",
|
||||
"version": "0.18.4",
|
||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.18.4.tgz",
|
||||
"integrity": "sha512-tuMjFM968TQOqkoaK7QbkQGxxBRQaH4VMxH43guJFwuPkH8q2MJbcglBizbQbAqWLM9c9adaHuUKocsS99MhBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@lottiefiles/dotlottie-web": "0.65.0"
|
||||
"@lottiefiles/dotlottie-web": "0.66.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@lottiefiles/dotlottie-web": {
|
||||
"version": "0.65.0",
|
||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.65.0.tgz",
|
||||
"integrity": "sha512-D5ALEwZerjy+MkOv9mMjY7bxRfe7rQPWMtDl8hlNYLnrDKj4AzTaRztZ2u5hBHHiSfSc7TnA4MJgq+ktNZPpIg==",
|
||||
"version": "0.66.0",
|
||||
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.66.0.tgz",
|
||||
"integrity": "sha512-aFTwmvP3iN4Lu4VyuKMqHlc6mMDdg0wtekY6FEzcv+Miv7inFW6wz0ekY5xHqhDctVOrQIWvJX+ShmevFZUjDg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
@@ -6426,9 +6428,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-reconciler": {
|
||||
"version": "0.32.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.32.3.tgz",
|
||||
"integrity": "sha512-cMi5ZrLG7UtbL7LTK6hq9w/EZIRk4Mf1Z5qHoI+qBh7/WkYkFXQ7gOto2yfUvPzF5ERMAhaXS5eTQ2SAnHjLzA==",
|
||||
"version": "0.33.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.33.0.tgz",
|
||||
"integrity": "sha512-HZOXsKT0tGI9LlUw2LuedXsVeB88wFa536vVL0M6vE8zN63nI+sSr1ByxmPToP5K5bukaVscyeCJcF9guVNJ1g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
@@ -7039,7 +7041,6 @@
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
@@ -7478,9 +7479,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
|
||||
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
"integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
@@ -9335,9 +9336,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz",
|
||||
"integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
@@ -9434,9 +9435,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.19.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
|
||||
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
|
||||
"version": "5.20.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
|
||||
"integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -9750,25 +9751,25 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.39.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz",
|
||||
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
|
||||
"version": "9.39.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz",
|
||||
"integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.21.1",
|
||||
"@eslint/config-array": "^0.21.2",
|
||||
"@eslint/config-helpers": "^0.4.2",
|
||||
"@eslint/core": "^0.17.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.39.3",
|
||||
"@eslint/eslintrc": "^3.3.5",
|
||||
"@eslint/js": "9.39.4",
|
||||
"@eslint/plugin-kit": "^0.4.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@humanwhocodes/retry": "^0.4.2",
|
||||
"@types/estree": "^1.0.6",
|
||||
"ajv": "^6.12.4",
|
||||
"ajv": "^6.14.0",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"debug": "^4.3.2",
|
||||
@@ -9787,7 +9788,7 @@
|
||||
"is-glob": "^4.0.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"minimatch": "^3.1.2",
|
||||
"minimatch": "^3.1.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.3"
|
||||
},
|
||||
@@ -10026,7 +10027,6 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/events": {
|
||||
@@ -10111,7 +10111,6 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
||||
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
@@ -10162,7 +10161,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
|
||||
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -11000,9 +10998,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.8.13",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.13.tgz",
|
||||
"integrity": "sha512-E0vzjBY1yM+nsFrtgkjLhST2NBkirkvOVoQa0MSldhsuZ3jUge7ZNpuwG0Cfc74zwo5ZwRzg3uOgT+McBn32iA==",
|
||||
"version": "25.8.17",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.17.tgz",
|
||||
"integrity": "sha512-vWtCttyn5bpOK4hWbRAe1ZXkA+Yzcn2OcACT+WJavtfGMcxzkfvXTLMeOU8MUhRmAySKjU4VVuKlo0sSGeBokA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -11019,7 +11017,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
"@babel/runtime": "^7.28.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
@@ -12156,19 +12154,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "16.2.7",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz",
|
||||
"integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==",
|
||||
"version": "16.3.3",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.3.3.tgz",
|
||||
"integrity": "sha512-RLq2koZ5fGWrx7tcqx2tSTMQj4lRkfNJaebO/li/uunhCJbtZqwTuwPHpgIimAHHi/2nZIiGrkCHDCOeR1onxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"commander": "^14.0.2",
|
||||
"commander": "^14.0.3",
|
||||
"listr2": "^9.0.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"nano-spawn": "^2.0.0",
|
||||
"pidtree": "^0.6.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"yaml": "^2.8.1"
|
||||
"tinyexec": "^1.0.2",
|
||||
"yaml": "^2.8.2"
|
||||
},
|
||||
"bin": {
|
||||
"lint-staged": "bin/lint-staged.js"
|
||||
@@ -12190,6 +12187,16 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/tinyexec": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
"integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz",
|
||||
@@ -12337,6 +12344,12 @@
|
||||
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
@@ -12344,6 +12357,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.map": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz",
|
||||
@@ -12838,7 +12858,6 @@
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@@ -12910,19 +12929,6 @@
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nano-spawn": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz",
|
||||
"integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/nano-spawn?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
@@ -13378,6 +13384,12 @@
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
|
||||
"integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -13640,19 +13652,6 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pidtree": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"pidtree": "bin/pidtree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@@ -13726,9 +13725,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"version": "8.5.8",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
|
||||
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -14148,6 +14147,7 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -14198,6 +14198,44 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
|
||||
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"eventemitter3": "^5.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"parchment": "^3.0.0",
|
||||
"quill-delta": "^5.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=8.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
||||
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-diff": "^1.3.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-html-edit-button": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quill-html-edit-button/-/quill-html-edit-button-3.0.0.tgz",
|
||||
"integrity": "sha512-ZUV3rYjEvXz+v/7f8N0Mkp2+0D8VGDj6yCYPfagSLmyshczeMawS7vZJzaJ18dJp35IfjiPnzqyVk3KCI8qN1w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"quill": "^2.x"
|
||||
}
|
||||
},
|
||||
"node_modules/radix-ui": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.4.3.tgz",
|
||||
@@ -14275,17 +14313,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
||||
@@ -14507,12 +14534,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-draggable": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz",
|
||||
"integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz",
|
||||
"integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^1.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -14520,15 +14547,6 @@
|
||||
"react-dom": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-draggable/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/react-gtm-module": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/react-gtm-module/-/react-gtm-module-2.0.11.tgz",
|
||||
@@ -14536,9 +14554,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "16.5.4",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz",
|
||||
"integrity": "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g==",
|
||||
"version": "16.5.6",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.6.tgz",
|
||||
"integrity": "sha512-Ua7V2/efA88ido7KyK51fb8Ki8M/sRfW8LR/rZ/9ZKr2luhuTI7kwYZN5agT1rWG7aYm5G0RYE/6JR8KJoCMDw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
@@ -14569,9 +14587,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-konva": {
|
||||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-konva/-/react-konva-19.2.2.tgz",
|
||||
"integrity": "sha512-fgIevJSiZ6mYxouR0XLq7oKgQkfHHySHyNpyc1f/3in0xXq8BV7M7Y82obyoBPOAj35TBUPuZ568qZcelYm3ag==",
|
||||
"version": "19.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-konva/-/react-konva-19.2.3.tgz",
|
||||
"integrity": "sha512-VsO5CJZwUo12xFa33UEIDOQn6ZZBeE6jlkStGFvpR/3NiDA/9RPQTzw6Ri++C0Pnh3Arco1AehB8qJNv9YCRwg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "patreon",
|
||||
@@ -14588,7 +14606,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/react-reconciler": "^0.32.3",
|
||||
"@types/react-reconciler": "^0.33.0",
|
||||
"its-fine": "^2.0.0",
|
||||
"react-reconciler": "0.33.0",
|
||||
"scheduler": "0.27.0"
|
||||
@@ -14640,6 +14658,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-quill-new": {
|
||||
"version": "3.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-quill-new/-/react-quill-new-3.8.3.tgz",
|
||||
"integrity": "sha512-c96PYqFTo0pI4R3e79B3rH9LUIce1kIQbmTBu/imJQZk8305ogyLyBqKKjG2UoInDlquXqePSzmBo2aVia3ttw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash-es": "^4.17.21",
|
||||
"quill": "~2.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"quill-delta": "^5.1.0",
|
||||
"react": "^16 || ^17 || ^18 || ^19",
|
||||
"react-dom": "^16 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-reconciler": {
|
||||
"version": "0.33.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
|
||||
@@ -14748,13 +14781,13 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/react-rnd": {
|
||||
"version": "10.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.2.tgz",
|
||||
"integrity": "sha512-0Tm4x7k7pfHf2snewJA8x7Nwgt3LV+58MVEWOVsFjk51eYruFEa6Wy7BNdxt4/lH0wIRsu7Gm3KjSXY2w7YaNw==",
|
||||
"version": "10.5.3",
|
||||
"resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.5.3.tgz",
|
||||
"integrity": "sha512-s/sIT3pGZnQ+57egijkTp9mizjIWrJz68Pq6yd+F/wniFY3IriML18dUXnQe/HP9uMiJ+9MAp44hljG99fZu6Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"re-resizable": "6.11.2",
|
||||
"react-draggable": "4.4.6",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react-draggable": "^4.5.0",
|
||||
"tslib": "2.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -15506,25 +15539,14 @@
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/serialize-javascript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"randombytes": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz",
|
||||
"integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==",
|
||||
"version": "14.2.6",
|
||||
"resolved": "https://registry.npmjs.org/serve/-/serve-14.2.6.tgz",
|
||||
"integrity": "sha512-QEjUSA+sD4Rotm1znR8s50YqA3kYpRGPmtd5GlFxbaL9n/FdUNbqMhxClqdditSk0LlZyA/dhud6XNRTOC9x2Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zeit/schemas": "2.36.0",
|
||||
"ajv": "8.12.0",
|
||||
"ajv": "8.18.0",
|
||||
"arg": "5.0.2",
|
||||
"boxen": "7.0.0",
|
||||
"chalk": "5.0.1",
|
||||
@@ -15532,7 +15554,7 @@
|
||||
"clipboardy": "3.0.0",
|
||||
"compression": "1.8.1",
|
||||
"is-port-reachable": "4.0.0",
|
||||
"serve-handler": "6.1.6",
|
||||
"serve-handler": "6.1.7",
|
||||
"update-check": "1.5.4"
|
||||
},
|
||||
"bin": {
|
||||
@@ -15543,15 +15565,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler": {
|
||||
"version": "6.1.6",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz",
|
||||
"integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==",
|
||||
"version": "6.1.7",
|
||||
"resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz",
|
||||
"integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.0.0",
|
||||
"content-disposition": "0.5.2",
|
||||
"mime-types": "2.1.18",
|
||||
"minimatch": "3.1.2",
|
||||
"minimatch": "3.1.5",
|
||||
"path-is-inside": "1.0.2",
|
||||
"path-to-regexp": "3.3.0",
|
||||
"range-parser": "1.2.0"
|
||||
@@ -15587,34 +15609,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-handler/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/chalk": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
|
||||
@@ -16404,9 +16398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.16",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
|
||||
"integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz",
|
||||
"integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -16414,7 +16408,6 @@
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^4.3.0",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"terser": "^5.31.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -16965,6 +16958,7 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
@@ -17408,9 +17402,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.105.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.3.tgz",
|
||||
"integrity": "sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==",
|
||||
"version": "5.105.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz",
|
||||
"integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -17425,7 +17419,7 @@
|
||||
"acorn-import-phases": "^1.0.3",
|
||||
"browserslist": "^4.28.1",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.19.0",
|
||||
"enhanced-resolve": "^5.20.0",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
@@ -17437,7 +17431,7 @@
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^4.3.3",
|
||||
"tapable": "^2.3.0",
|
||||
"terser-webpack-plugin": "^5.3.16",
|
||||
"terser-webpack-plugin": "^5.3.17",
|
||||
"watchpack": "^2.5.1",
|
||||
"webpack-sources": "^3.3.4"
|
||||
},
|
||||
|
||||
+13
-11
@@ -7,7 +7,7 @@
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@formkit/auto-animate": "^0.9.0",
|
||||
"@imgly/background-removal": "^1.7.0",
|
||||
"@lottiefiles/dotlottie-react": "^0.18.3",
|
||||
"@lottiefiles/dotlottie-react": "^0.18.4",
|
||||
"@mui/icons-material": "^5.15.10",
|
||||
"@mui/material": "^5.15.10",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
@@ -26,12 +26,12 @@
|
||||
"@usewaypoint/block-text": "^0.0.7",
|
||||
"@usewaypoint/document-core": "^0.0.6",
|
||||
"@usewaypoint/email-builder": "^0.0.9",
|
||||
"axios": "^1.13.5",
|
||||
"axios": "^1.13.6",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dompurify": "^3.3.3",
|
||||
"file-saver": "^2.0.5",
|
||||
"highlight.js": "^11.9.0",
|
||||
"i18next": "^25.8.13",
|
||||
"i18next": "^25.8.17",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -44,6 +44,7 @@
|
||||
"prettier": "^3.8.1",
|
||||
"print-js": "^1.6.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"quill-html-edit-button": "^3.0.0",
|
||||
"radix-ui": "^1.4.3",
|
||||
"react": "^19.2.4",
|
||||
"react-bootstrap": "^2.10.10",
|
||||
@@ -55,11 +56,12 @@
|
||||
"react-dnd-multi-backend": "^9.0.0",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-i18next": "^16.5.4",
|
||||
"react-konva": "^19.2.2",
|
||||
"react-i18next": "^16.5.6",
|
||||
"react-konva": "^19.2.3",
|
||||
"react-pdf": "^10.4.1",
|
||||
"react-quill-new": "^3.8.3",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-rnd": "^10.5.3",
|
||||
"react-router": "^7.13.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.10.2",
|
||||
@@ -69,7 +71,7 @@
|
||||
"react-tooltip": "^5.30.0",
|
||||
"redux": "^5.0.1",
|
||||
"regex-parser": "^2.3.1",
|
||||
"serve": "^14.2.5",
|
||||
"serve": "^14.2.6",
|
||||
"web-vitals": "^5.1.0",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"zod": "^3.22.4",
|
||||
@@ -129,12 +131,12 @@
|
||||
"concurrently": "^9.2.1",
|
||||
"css-loader": "^7.1.4",
|
||||
"daisyui": "^4.12.24",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-prettier": "^5.5.5",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"jsdom": "^28.1.0",
|
||||
"lint-staged": "^16.2.7",
|
||||
"postcss": "^8.5.6",
|
||||
"lint-staged": "^16.3.3",
|
||||
"postcss": "^8.5.8",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"tailwindcss": "^3.4.19",
|
||||
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "Jede öffnende {{ muss eine passende }} haben.",
|
||||
"formula-error-operators": "Verwenden Sie bei Operatoren oder Klammern mindestens zwei Werte.",
|
||||
"formula-error-leading-operator": "Formeln dürfen nicht mit Operatoren beginnen.",
|
||||
"formula-error-trailing-operator": "Formeln dürfen nicht mit einem Operator enden.",
|
||||
"formula-error-adjacent-placeholders": "Zwei Platzhalter dürfen nicht ohne einen Operator dazwischen direkt nebeneinander stehen.",
|
||||
"formula-error-continuous-operators": "Zwei Operatoren dürfen nicht direkt hintereinander verwendet werden.",
|
||||
"formula-error-empty-parentheses": "Klammern dürfen nicht leer sein.",
|
||||
"formula-error-invalid-parentheses-content": "Klammern dürfen nicht mit einem Operator beginnen oder enden.",
|
||||
"formula-error-before-open-parenthesis": "Fehlender Operator vor '('. Fügen Sie vor der öffnenden Klammer einen Operator hinzu.",
|
||||
"formula-error-after-close-parenthesis": "Fehlender Operator nach ')'. Fügen Sie nach der schließenden Klammer einen Operator wie '+' oder '*' hinzu.",
|
||||
"formula-error-between-parentheses": "Fehlender Operator zwischen ')' und '('. Fügen Sie zwischen den beiden Gruppen einen Operator wie '*' hinzu.",
|
||||
"formula-error-division-by-zero": "Division durch Null ist nicht zulässig. Ersetzen Sie '0' durch eine gültige, von Null verschiedene Zahl oder Variable.",
|
||||
"formula-error-invalid-number-format": "Ungültiges Zahlenformat gefunden. Zahlen müssen in einem gültigen Format wie '1' oder '3.14' vorliegen, nicht '.5', '1.' oder '1.2.3'.",
|
||||
"formula-error-unclosed-parenthesis": "Nicht geschlossene '(' gefunden. Jede öffnende Klammer '(' muss eine passende schließende Klammer ')' haben.",
|
||||
"formula-error-unexpected-closing-parenthesis": "Unerwartete schließende Klammer ')' gefunden, ohne passende öffnende Klammer '('.",
|
||||
"formula-error-empty-placeholder": "Leerer Platzhalter '{{}}' ist nicht zulässig. Geben Sie einen Variablennamen innerhalb der Klammern an.",
|
||||
"decimal-places": "Dezimalstellen",
|
||||
"number-of-decimal-places": "Anzahl der Dezimalstellen",
|
||||
"decimal-places-helper": "Auf 0 setzen für ganze Zahlen",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "Hinweis: Diese Funktion verbraucht Premium-Credits.",
|
||||
"note-consume-premium-credits-on-document-send": "Hinweis: Diese Funktion verbraucht Premium-Credits. Sie werden jedes Mal verbraucht, wenn ein Dokument versendet wird – unabhängig davon, ob es unterschrieben wird oder nicht.",
|
||||
"webhook-authentication-help": "{{appName}} sichert jeden Webhook, indem eine HMAC-SHA256-Signatur mit Ihrem Webhook-Sicherheitsschlüssel und dem unveränderten Request-Payload generiert wird. Diese Signatur wird im Header x-webhook-signature gesendet. Ihr Server muss dieselbe Signatur mit dem empfangenen Payload und Ihrem geheimen Schlüssel generieren. Stimmen beide Signaturen überein, ist die Webhook-Anfrage authentisch und wurde nicht verändert.",
|
||||
"invalid-widget-found-in-row": "Ungültiger Wert in Zeile {{row}} für Widget {{widget}} erkannt."
|
||||
"invalid-widget-found-in-row": "Ungültiger Wert in Zeile {{row}} für Widget {{widget}} erkannt.",
|
||||
"all-signer-status": "Alle Unterzeichner-Status",
|
||||
"viewed": "Angesehen",
|
||||
"signed": "Unterzeichnet",
|
||||
"switch-to-advanced": "Zum erweiterten Modus wechseln",
|
||||
"switch-to-basic": "Zum Basis-Modus wechseln",
|
||||
"use-name-as-sender": "Profilnamen als Absender verwenden",
|
||||
"use-name-as-sender-help": "Wenn aktiviert, verwendet {{appName}} Ihren Profilnamen als Absender für Signaturanfragen und Abschluss-E-Mails."
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "Each opening {{ must have a matching }}.",
|
||||
"formula-error-operators": "Add at least two values when using operators or parentheses.",
|
||||
"formula-error-leading-operator": "Formulas cannot start with operators.",
|
||||
"formula-error-trailing-operator": "Formulas cannot end with an operator.",
|
||||
"formula-error-adjacent-placeholders": "Two placeholders cannot be written together without an operator between them.",
|
||||
"formula-error-continuous-operators": "Two operators cannot be used together.",
|
||||
"formula-error-empty-parentheses": "Parentheses cannot be empty.",
|
||||
"formula-error-invalid-parentheses-content": "Parentheses cannot start or end with an operator.",
|
||||
"formula-error-before-open-parenthesis": "Missing operator before '('. Add an operator before the opening parenthesis.",
|
||||
"formula-error-after-close-parenthesis": "Missing operator after ')'. Add an operator like '+' or '*' after the closing parenthesis.",
|
||||
"formula-error-between-parentheses": "Missing operator between ')' and '('. Add an operator like '*' between the two groups.",
|
||||
"formula-error-division-by-zero": "Division by zero is not allowed. Replace '0' with a valid non-zero number or variable.",
|
||||
"formula-error-invalid-number-format": "Invalid number format found. Numbers must be in a valid format like '1', '3.14', not '.5', '1.', or '1.2.3'.",
|
||||
"formula-error-unclosed-parenthesis": "Unclosed '(' found. Every opening '(' must have a matching closing ')'.",
|
||||
"formula-error-unexpected-closing-parenthesis": "Unexpected closing parenthesis ')' found without a matching opening '('.",
|
||||
"formula-error-empty-placeholder": "Empty placeholder '{{}}' is not allowed. Provide a variable name inside the braces.",
|
||||
"decimal-places": "Decimal Places",
|
||||
"number-of-decimal-places": "Number of decimal places",
|
||||
"decimal-places-helper": "Set to 0 for whole numbers",
|
||||
@@ -525,7 +538,7 @@
|
||||
"email-body": "email body",
|
||||
"email-placeholder": "add body of email",
|
||||
"reset-to-default": "Reset to default",
|
||||
"customize-email": "Customize email",
|
||||
"customize-email": "Customize email",
|
||||
"upgrade-to-customize-email": "Upgrade to customize Email",
|
||||
"sign-url": "Sign url",
|
||||
"mails-sent": "Mails Sent",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "Note: This feature consumes premium credits.",
|
||||
"note-consume-premium-credits-on-document-send": "Note: This feature consumes premium credits. It is consumed any time a document is sent out, regardless of whether it is signed or not.",
|
||||
"webhook-authentication-help": "{{appName}} secures every webhook by generating an HMAC-SHA256 signature using your webhook security key and the raw request payload. This signature is sent in the x-webhook-signature header. Your server must generate the same signature using the received payload and your secret key. If both signatures match, the webhook request is authentic and has not been modified.",
|
||||
"invalid-widget-found-in-row": "Invalid value detected in row {{row}} for widget {{widget}}."
|
||||
"invalid-widget-found-in-row": "Invalid value detected in row {{row}} for widget {{widget}}.",
|
||||
"all-signer-status": "All signer status",
|
||||
"viewed": "Viewed",
|
||||
"signed": "Signed",
|
||||
"switch-to-advanced": "Switch to advanced",
|
||||
"switch-to-basic": "Switch to basic",
|
||||
"use-name-as-sender": "Use profile name as sender",
|
||||
"use-name-as-sender-help": "When enabled, {{appName}} uses your profile name as sender for signature request and completion mail."
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "Cada {{ de apertura debe tener una }} correspondiente.",
|
||||
"formula-error-operators": "Cuando uses operadores o paréntesis, agrega al menos dos valores.",
|
||||
"formula-error-leading-operator": "Las fórmulas no pueden comenzar con operadores.",
|
||||
"formula-error-trailing-operator": "Las fórmulas no pueden terminar con un operador.",
|
||||
"formula-error-adjacent-placeholders": "No se pueden escribir dos marcadores de posición seguidos sin un operador entre ellos.",
|
||||
"formula-error-continuous-operators": "No se pueden usar dos operadores seguidos.",
|
||||
"formula-error-empty-parentheses": "Los paréntesis no pueden estar vacíos.",
|
||||
"formula-error-invalid-parentheses-content": "Los paréntesis no pueden empezar ni terminar con un operador.",
|
||||
"formula-error-before-open-parenthesis": "Falta un operador antes de '('. Añada un operador antes del paréntesis de apertura.",
|
||||
"formula-error-after-close-parenthesis": "Falta un operador después de ')'. Añada un operador como '+' o '*' después del paréntesis de cierre.",
|
||||
"formula-error-between-parentheses": "Falta un operador entre ')' y '('. Añada un operador como '*' entre los dos grupos.",
|
||||
"formula-error-division-by-zero": "No se permite la división entre cero. Reemplace '0' por un número o variable válido distinto de cero.",
|
||||
"formula-error-invalid-number-format": "Se encontró un formato de número no válido. Los números deben estar en un formato válido como '1' o '3.14', no '.5', '1.' ni '1.2.3'.",
|
||||
"formula-error-unclosed-parenthesis": "Se encontró un '(' sin cerrar. Cada paréntesis de apertura '(' debe tener un paréntesis de cierre ')' correspondiente.",
|
||||
"formula-error-unexpected-closing-parenthesis": "Se encontró un paréntesis de cierre ')' inesperado sin un paréntesis de apertura '(' correspondiente.",
|
||||
"formula-error-empty-placeholder": "No se permite un marcador de posición vacío '{{}}'. Proporcione un nombre de variable dentro de las llaves.",
|
||||
"decimal-places": "Decimales",
|
||||
"number-of-decimal-places": "Número de decimales",
|
||||
"decimal-places-helper": "Establecer en 0 para números enteros",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "Nota: Esta función consume créditos premium.",
|
||||
"note-consume-premium-credits-on-document-send": "Nota: Esta función consume créditos premium. Se consumen cada vez que se envía un documento, independientemente de si se firma o no.",
|
||||
"webhook-authentication-help": "{{appName}} protege cada webhook generando una firma HMAC-SHA256 utilizando su clave de seguridad del webhook y el payload original de la solicitud. Esta firma se envía en el encabezado x-webhook-signature. Su servidor debe generar la misma firma utilizando el payload recibido y su clave secreta. Si ambas firmas coinciden, la solicitud del webhook es auténtica y no ha sido modificada.",
|
||||
"invalid-widget-found-in-row": "Se detectó un valor no válido en la fila {{row}} para el widget {{widget}}."
|
||||
"invalid-widget-found-in-row": "Se detectó un valor no válido en la fila {{row}} para el widget {{widget}}.",
|
||||
"all-signer-status": "Estado de todos los firmantes",
|
||||
"viewed": "Visto",
|
||||
"signed": "Firmado",
|
||||
"switch-to-advanced": "Cambiar a avanzado",
|
||||
"switch-to-basic": "Cambiar a básico",
|
||||
"use-name-as-sender": "Usar el nombre del perfil como remitente",
|
||||
"use-name-as-sender-help": "Cuando está activado, {{appName}} usa el nombre de tu perfil como remitente para las solicitudes de firma y el correo de finalización."
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "Chaque {{ ouvrante doit avoir une }} correspondante.",
|
||||
"formula-error-operators": "Lorsque vous utilisez des opérateurs ou des parenthèses, ajoutez au moins deux valeurs.",
|
||||
"formula-error-leading-operator": "Les formules ne peuvent pas commencer par des opérateurs.",
|
||||
"formula-error-trailing-operator": "Les formules ne peuvent pas se terminer par un opérateur.",
|
||||
"formula-error-adjacent-placeholders": "Deux espaces réservés ne peuvent pas être écrits l’un à côté de l’autre sans opérateur entre eux.",
|
||||
"formula-error-continuous-operators": "Deux opérateurs ne peuvent pas être utilisés à la suite.",
|
||||
"formula-error-empty-parentheses": "Les parenthèses ne peuvent pas être vides.",
|
||||
"formula-error-invalid-parentheses-content": "Les parenthèses ne peuvent pas commencer ou se terminer par un opérateur.",
|
||||
"formula-error-before-open-parenthesis": "Opérateur manquant avant '('. Ajoutez un opérateur avant la parenthèse ouvrante.",
|
||||
"formula-error-after-close-parenthesis": "Opérateur manquant après ')'. Ajoutez un opérateur comme '+' ou '*' après la parenthèse fermante.",
|
||||
"formula-error-between-parentheses": "Opérateur manquant entre ')' et '('. Ajoutez un opérateur comme '*' entre les deux groupes.",
|
||||
"formula-error-division-by-zero": "La division par zéro n'est pas autorisée. Remplacez '0' par un nombre ou une variable valide non nul.",
|
||||
"formula-error-invalid-number-format": "Format de nombre invalide détecté. Les nombres doivent être dans un format valide comme '1' ou '3.14', et non '.5', '1.' ou '1.2.3'.",
|
||||
"formula-error-unclosed-parenthesis": "Parenthèse '(' non fermée détectée. Chaque parenthèse ouvrante '(' doit avoir une parenthèse fermante ')' correspondante.",
|
||||
"formula-error-unexpected-closing-parenthesis": "Parenthèse fermante ')' inattendue détectée sans parenthèse ouvrante '(' correspondante.",
|
||||
"formula-error-empty-placeholder": "Un espace réservé vide '{{}}' n'est pas autorisé. Indiquez un nom de variable à l'intérieur des accolades.",
|
||||
"decimal-places": "Décimales",
|
||||
"number-of-decimal-places": "Nombre de décimales",
|
||||
"decimal-places-helper": "Définir sur 0 pour les nombres entiers",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "Remarque : Cette fonctionnalité consomme des crédits premium.",
|
||||
"note-consume-premium-credits-on-document-send": "Remarque : Cette fonctionnalité consomme des crédits premium. Les crédits sont consommés chaque fois qu'un document est envoyé, qu'il soit signé ou non.",
|
||||
"webhook-authentication-help": "{{appName}} sécurise chaque webhook en générant une signature HMAC-SHA256 à l'aide de votre clé de sécurité webhook et du payload brut de la requête. Cette signature est envoyée dans l'en-tête x-webhook-signature. Votre serveur doit générer la même signature en utilisant le payload reçu et votre clé secrète. Si les deux signatures correspondent, la requête webhook est authentique et n'a pas été modifiée.",
|
||||
"invalid-widget-found-in-row": "Valeur invalide détectée à la ligne {{row}} pour le widget {{widget}}."
|
||||
"invalid-widget-found-in-row": "Valeur invalide détectée à la ligne {{row}} pour le widget {{widget}}.",
|
||||
"all-signer-status": "Statut de tous les signataires",
|
||||
"viewed": "Consulté",
|
||||
"signed": "Signé",
|
||||
"switch-to-advanced": "Passer en mode avancé",
|
||||
"switch-to-basic": "Passer en mode basique",
|
||||
"use-name-as-sender": "Utiliser le nom du profil comme expéditeur",
|
||||
"use-name-as-sender-help": "Lorsque cette option est activée, {{appName}} utilise le nom de votre profil comme expéditeur pour les demandes de signature et l'e-mail de finalisation."
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "हर खुलने वाली {{ के लिए एक मिलान करने वाली }} होना चाहिए।",
|
||||
"formula-error-operators": "ऑपरेटर या कोष्ठक का उपयोग करते समय कम से कम दो मान जोड़ें।",
|
||||
"formula-error-leading-operator": "सूत्र ऑपरेटर से शुरू नहीं हो सकते।",
|
||||
"formula-error-trailing-operator": "सूत्र किसी ऑपरेटर पर समाप्त नहीं हो सकते।",
|
||||
"formula-error-adjacent-placeholders": "दो प्लेसहोल्डर बिना बीच में ऑपरेटर के साथ-साथ नहीं लिखे जा सकते।",
|
||||
"formula-error-continuous-operators": "दो ऑपरेटर लगातार साथ में उपयोग नहीं किए जा सकते।",
|
||||
"formula-error-empty-parentheses": "कोष्ठक (parentheses) खाली नहीं हो सकते।",
|
||||
"formula-error-invalid-parentheses-content": "कोष्ठक किसी ऑपरेटर से शुरू या समाप्त नहीं हो सकते।",
|
||||
"formula-error-before-open-parenthesis": "'(' से पहले ऑपरेटर गायब है। ओपनिंग कोष्ठक से पहले एक ऑपरेटर जोड़ें।",
|
||||
"formula-error-after-close-parenthesis": "')' के बाद ऑपरेटर गायब है। क्लोज़िंग कोष्ठक के बाद '+' या '*' जैसा ऑपरेटर जोड़ें।",
|
||||
"formula-error-between-parentheses": "')' और '(' के बीच ऑपरेटर गायब है। दोनों समूहों के बीच '*' जैसा ऑपरेटर जोड़ें।",
|
||||
"formula-error-division-by-zero": "शून्य से भाग देना अनुमति नहीं है। '0' को किसी मान्य गैर-शून्य संख्या या वेरिएबल से बदलें।",
|
||||
"formula-error-invalid-number-format": "अमान्य संख्या फ़ॉर्मेट मिला। संख्याएँ '1', '3.14' जैसे मान्य फ़ॉर्मेट में होनी चाहिए, '.5', '1.' या '1.2.3' नहीं।",
|
||||
"formula-error-unclosed-parenthesis": "बिना बंद किया हुआ '(' मिला। हर ओपनिंग '(' के लिए एक मैचिंग क्लोज़िंग ')' होना चाहिए।",
|
||||
"formula-error-unexpected-closing-parenthesis": "बिना मैचिंग ओपनिंग '(' के अप्रत्याशित क्लोज़िंग ')' मिला।",
|
||||
"formula-error-empty-placeholder": "खाली प्लेसहोल्डर '{{}}' अनुमति नहीं है। ब्रेसेज़ के अंदर वेरिएबल का नाम दें।",
|
||||
"decimal-places": "दशमलव स्थान",
|
||||
"number-of-decimal-places": "दशमलव स्थानों की संख्या",
|
||||
"decimal-places-helper": "पूर्णांक के लिए 0 पर सेट करें",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "नोट: यह सुविधा प्रीमियम क्रेडिट का उपयोग करती है।",
|
||||
"note-consume-premium-credits-on-document-send": "नोट: यह सुविधा प्रीमियम क्रेडिट का उपयोग करती है। दस्तावेज़ भेजे जाने पर हर बार क्रेडिट उपयोग किए जाते हैं, चाहे दस्तावेज़ पर हस्ताक्षर किए जाएँ या नहीं।",
|
||||
"webhook-authentication-help": "{{appName}} प्रत्येक वेबहुक को सुरक्षित करता है, आपके वेबहुक सुरक्षा कुंजी और मूल अनुरोध पेलोड का उपयोग करके HMAC-SHA256 हस्ताक्षर उत्पन्न करके। यह हस्ताक्षर x-webhook-signature हेडर में भेजा जाता है। आपके सर्वर को प्राप्त पेलोड और आपकी सीक्रेट कुंजी का उपयोग करके वही हस्ताक्षर उत्पन्न करना होगा। यदि दोनों हस्ताक्षर मेल खाते हैं, तो वेबहुक अनुरोध प्रमाणिक है और उसमें कोई परिवर्तन नहीं किया गया है।",
|
||||
"invalid-widget-found-in-row": "पंक्ति {{row}} में विजेट {{widget}} के लिए अमान्य मान पाया गया।"
|
||||
"invalid-widget-found-in-row": "पंक्ति {{row}} में विजेट {{widget}} के लिए अमान्य मान पाया गया।",
|
||||
"all-signer-status": "सभी हस्ताक्षरकर्ताओं की स्थिति",
|
||||
"viewed": "देखा गया",
|
||||
"signed": "हस्ताक्षरित",
|
||||
"switch-to-advanced": "एडवांस्ड पर स्विच करें",
|
||||
"switch-to-basic": "बेसिक पर स्विच करें",
|
||||
"use-name-as-sender": "प्रेषक के रूप में प्रोफ़ाइल नाम का उपयोग करें",
|
||||
"use-name-as-sender-help": "सक्षम होने पर, {{appName}} हस्ताक्षर अनुरोध और पूर्णता ईमेल के लिए आपके प्रोफ़ाइल नाम को प्रेषक के रूप में उपयोग करता है।"
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "Ogni {{ di apertura deve avere una }} corrispondente.",
|
||||
"formula-error-operators": "Quando usi operatori o parentesi, aggiungi almeno due valori.",
|
||||
"formula-error-leading-operator": "Le formule non possono iniziare con operatori.",
|
||||
"formula-error-trailing-operator": "Le formule non possono terminare con un operatore.",
|
||||
"formula-error-adjacent-placeholders": "Due segnaposto non possono essere scritti insieme senza un operatore tra di loro.",
|
||||
"formula-error-continuous-operators": "Non è possibile usare due operatori consecutivi.",
|
||||
"formula-error-empty-parentheses": "Le parentesi non possono essere vuote.",
|
||||
"formula-error-invalid-parentheses-content": "Le parentesi non possono iniziare o terminare con un operatore.",
|
||||
"formula-error-before-open-parenthesis": "Operatore mancante prima di '('. Aggiungi un operatore prima della parentesi di apertura.",
|
||||
"formula-error-after-close-parenthesis": "Operatore mancante dopo ')'. Aggiungi un operatore come '+' o '*' dopo la parentesi di chiusura.",
|
||||
"formula-error-between-parentheses": "Operatore mancante tra ')' e '('. Aggiungi un operatore come '*' tra i due gruppi.",
|
||||
"formula-error-division-by-zero": "La divisione per zero non è consentita. Sostituisci '0' con un numero o una variabile valida diversa da zero.",
|
||||
"formula-error-invalid-number-format": "Trovato un formato numerico non valido. I numeri devono essere in un formato valido come '1' o '3.14', non '.5', '1.' o '1.2.3'.",
|
||||
"formula-error-unclosed-parenthesis": "Trovata una '(' non chiusa. Ogni parentesi di apertura '(' deve avere una parentesi di chiusura ')' corrispondente.",
|
||||
"formula-error-unexpected-closing-parenthesis": "Trovata una parentesi di chiusura ')' inattesa senza una parentesi di apertura '(' corrispondente.",
|
||||
"formula-error-empty-placeholder": "Il segnaposto vuoto '{{}}' non è consentito. Fornisci un nome variabile all'interno delle parentesi graffe.",
|
||||
"decimal-places": "Cifre decimali",
|
||||
"number-of-decimal-places": "Numero di cifre decimali",
|
||||
"decimal-places-helper": "Imposta su 0 per numeri interi",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "Nota: Questa funzione consuma crediti premium.",
|
||||
"note-consume-premium-credits-on-document-send": "Nota: Questa funzione consuma crediti premium. I crediti vengono consumati ogni volta che un documento viene inviato, indipendentemente dal fatto che venga firmato o meno.",
|
||||
"webhook-authentication-help": "{{appName}} protegge ogni webhook generando una firma HMAC-SHA256 utilizzando la tua chiave di sicurezza webhook e il payload originale della richiesta. Questa firma viene inviata nell'intestazione x-webhook-signature. Il tuo server deve generare la stessa firma utilizzando il payload ricevuto e la tua chiave segreta. Se entrambe le firme coincidono, la richiesta webhook è autentica e non è stata modificata.",
|
||||
"invalid-widget-found-in-row": "Valore non valido rilevato nella riga {{row}} per il widget {{widget}}."
|
||||
"invalid-widget-found-in-row": "Valore non valido rilevato nella riga {{row}} per il widget {{widget}}.",
|
||||
"all-signer-status": "Stato di tutti i firmatari",
|
||||
"viewed": "Visualizzato",
|
||||
"signed": "Firmato",
|
||||
"switch-to-advanced": "Passa alla modalità avanzata",
|
||||
"switch-to-basic": "Passa alla modalità base",
|
||||
"use-name-as-sender": "Usa il nome del profilo come mittente",
|
||||
"use-name-as-sender-help": "Quando abilitato, {{appName}} usa il nome del tuo profilo come mittente per le richieste di firma e l'email di completamento."
|
||||
}
|
||||
@@ -462,6 +462,19 @@
|
||||
"formula-error-braces": "모든 열리는 중괄호 '{{' 는 반드시 닫히는 중괄호 '}}' 가 있어야 합니다.",
|
||||
"formula-error-operators": "연산자나 괄호를 사용할 때는 최소 두 개의 값을 추가하세요.",
|
||||
"formula-error-leading-operator": "수식은 연산자로 시작할 수 없습니다.",
|
||||
"formula-error-trailing-operator": "수식은 연산자로 끝날 수 없습니다.",
|
||||
"formula-error-adjacent-placeholders": "두 플레이스홀더는 사이에 연산자 없이 붙여 쓸 수 없습니다.",
|
||||
"formula-error-continuous-operators": "연산자를 두 개 연속으로 사용할 수 없습니다.",
|
||||
"formula-error-empty-parentheses": "괄호는 비워둘 수 없습니다.",
|
||||
"formula-error-invalid-parentheses-content": "괄호는 연산자로 시작하거나 끝날 수 없습니다.",
|
||||
"formula-error-before-open-parenthesis": "'(' 앞에 연산자가 없습니다. 여는 괄호 앞에 연산자를 추가하세요.",
|
||||
"formula-error-after-close-parenthesis": "')' 뒤에 연산자가 없습니다. 닫는 괄호 뒤에 '+' 또는 '*' 같은 연산자를 추가하세요.",
|
||||
"formula-error-between-parentheses": "')'와 '(' 사이에 연산자가 없습니다. 두 그룹 사이에 '*' 같은 연산자를 추가하세요.",
|
||||
"formula-error-division-by-zero": "0으로 나눌 수 없습니다. '0'을 유효한 0이 아닌 숫자 또는 변수로 바꾸세요.",
|
||||
"formula-error-invalid-number-format": "잘못된 숫자 형식이 발견되었습니다. 숫자는 '1', '3.14'처럼 올바른 형식이어야 하며, '.5', '1.', '1.2.3'은 허용되지 않습니다.",
|
||||
"formula-error-unclosed-parenthesis": "닫히지 않은 '('가 발견되었습니다. 모든 여는 괄호 '('는 대응하는 닫는 괄호 ')'가 있어야 합니다.",
|
||||
"formula-error-unexpected-closing-parenthesis": "대응하는 여는 괄호 '(' 없이 예상치 못한 닫는 괄호 ')'가 발견되었습니다.",
|
||||
"formula-error-empty-placeholder": "빈 플레이스홀더 '{{}}'는 허용되지 않습니다. 중괄호 안에 변수 이름을 입력하세요.",
|
||||
"decimal-places": "소수점 자리수",
|
||||
"number-of-decimal-places": "소수점 자리수",
|
||||
"decimal-places-helper": "정수를 원하면 0으로 설정하세요",
|
||||
@@ -1396,5 +1409,12 @@
|
||||
"note-consume-premium-credits": "참고: 이 기능은 프리미엄 크레딧을 사용합니다.",
|
||||
"note-consume-premium-credits-on-document-send": "참고: 이 기능은 프리미엄 크레딧을 사용합니다. 문서가 서명되었는지 여부와 관계없이 문서를 보낼 때마다 크레딧이 차감됩니다.",
|
||||
"webhook-authentication-help": "{{appName}}은(는) 웹훅 보안 키와 원본 요청 페이로드를 사용하여 HMAC-SHA256 서명을 생성함으로써 모든 웹훅을 보호합니다. 이 서명은 x-webhook-signature 헤더에 전송됩니다. 귀하의 서버는 수신된 페이로드와 비밀 키를 사용하여 동일한 서명을 생성해야 합니다. 두 서명이 일치하면 해당 웹훅 요청은 인증되었으며 수정되지 않았음을 의미합니다.",
|
||||
"invalid-widget-found-in-row": "{{row}}행에서 위젯 {{widget}}에 대한 유효하지 않은 값이 감지되었습니다."
|
||||
"invalid-widget-found-in-row": "{{row}}행에서 위젯 {{widget}}에 대한 유효하지 않은 값이 감지되었습니다.",
|
||||
"all-signer-status": "전체 서명자 상태",
|
||||
"viewed": "열람함",
|
||||
"signed": "서명 완료",
|
||||
"switch-to-advanced": "고급 모드로 전환",
|
||||
"switch-to-basic": "기본 모드로 전환",
|
||||
"use-name-as-sender": "프로필 이름을 발신자로 사용",
|
||||
"use-name-as-sender-help": "활성화하면 {{appName}}에서 서명 요청 및 완료 이메일의 발신자로 프로필 이름을 사용합니다."
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from "../../../constant/Utils";
|
||||
import PenColorComponent from "../../pdf/tab/PenColorComponent";
|
||||
import { getDatePickerDate, toHtmlPattern } from "../../../utils";
|
||||
import CellsInput from "../../shared/fields/CellsInput";
|
||||
import { emailRegex } from "../../../constant/const";
|
||||
|
||||
const inputOpt = new Set(["text", "email", "number"]);
|
||||
@@ -527,29 +526,23 @@ const DrawWidget = ({ widget, isRequired, onChange, showLabel }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CellsWidget = ({
|
||||
widget,
|
||||
isRequired,
|
||||
onChange,
|
||||
showLabel,
|
||||
}) => {
|
||||
const CellsWidget = ({ widget, isRequired, onChange, showLabel }) => {
|
||||
const { t } = useTranslation();
|
||||
const count = widget?.options?.cellCount || 1;
|
||||
const [word, setWord] = useState("");
|
||||
const inputType = widget?.options?.validation?.type || "";
|
||||
const serverRegex = widget?.options?.validation?.pattern;
|
||||
const hint = widget?.options?.hint;
|
||||
const regExpression = inputValidation(serverRegex, inputType);
|
||||
const pattern = useMemo(() => toHtmlPattern(regExpression), [regExpression]);
|
||||
|
||||
useEffect(() => {
|
||||
const response =
|
||||
widget?.options?.response ?? widget?.options?.defaultValue ?? "";
|
||||
const isNumber = inputType === "number" && response;
|
||||
setWord(isNumber ? Number(response) : response);
|
||||
setWord(String(response || ""));
|
||||
}, []);
|
||||
|
||||
const handleChange = (words) => {
|
||||
const value = words;
|
||||
const handleChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setWord(value);
|
||||
onChange(value);
|
||||
};
|
||||
@@ -562,23 +555,13 @@ const CellsWidget = ({
|
||||
</div>
|
||||
)}
|
||||
<div className="relative">
|
||||
<CellsInput
|
||||
hint={
|
||||
widget?.options?.hint ||
|
||||
t("enter-value", { value: widget?.options?.name })
|
||||
}
|
||||
count={count}
|
||||
value={word}
|
||||
onChange={handleChange}
|
||||
only="all"
|
||||
autoFocus
|
||||
/>
|
||||
<input
|
||||
id={`widget-${widget?.options?.name}-${widget.key}`}
|
||||
value={word}
|
||||
required={isRequired}
|
||||
tabIndex={-1}
|
||||
onChange={() => {}}
|
||||
type="text"
|
||||
placeholder={
|
||||
hint || t("enter-value", { value: widget?.options?.name })
|
||||
}
|
||||
value={word ?? ""}
|
||||
onChange={(e) => handleChange(e)}
|
||||
pattern={pattern || undefined} // if no pattern, browser won't do pattern validation
|
||||
onInvalid={(e) => {
|
||||
const el = e.currentTarget;
|
||||
@@ -592,7 +575,8 @@ const CellsWidget = ({
|
||||
onInput={(e) => {
|
||||
e.currentTarget.setCustomValidity("");
|
||||
}}
|
||||
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 w-[1px] h-[1px] pointer-events-none"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none text-base-content hover:border-base-content w-full text-xs"
|
||||
maxLength={count}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import EmailBodyEditor from "../EmailBodyEditor";
|
||||
import { QuillEmailEditor } from "./quillemailbuilder";
|
||||
|
||||
const EmailEditor = ({
|
||||
type = "basic",
|
||||
values = {},
|
||||
onChange,
|
||||
placeholder = "add body of email",
|
||||
bodyName,
|
||||
isReset,
|
||||
isTemplateLoaded,
|
||||
smallscreen
|
||||
}) => {
|
||||
const currentValue = values[type] ?? "";
|
||||
const handleChange = (newValue) => {
|
||||
onChange?.(newValue, type);
|
||||
};
|
||||
|
||||
if (type === "advanced") {
|
||||
return (
|
||||
<EmailBodyEditor
|
||||
value={currentValue}
|
||||
onChange={handleChange}
|
||||
bodyName={bodyName}
|
||||
isReset={isReset}
|
||||
isTemplateLoaded={isTemplateLoaded}
|
||||
smallscreen={smallscreen}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<QuillEmailEditor
|
||||
value={currentValue}
|
||||
onChange={handleChange}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmailEditor;
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./EmailEditor";
|
||||
export { default as EmailEditor } from "./EmailEditor";
|
||||
@@ -0,0 +1,43 @@
|
||||
import React, { useId, useMemo } from "react";
|
||||
import ReactQuill from "react-quill-new";
|
||||
// import "react-quill-new/dist/quill.snow.css";
|
||||
import '../../../styles/quill.css'
|
||||
|
||||
import QuillToolbar from "./QuillToolbar";
|
||||
import { createModules, formats } from "./quillConfig";
|
||||
|
||||
const QuillEmailEditor = ({
|
||||
value = "",
|
||||
onChange,
|
||||
placeholder = "Write your email...",
|
||||
readOnly = false,
|
||||
theme = "snow",
|
||||
className = "",
|
||||
editorClassName = "",
|
||||
toolbarId: toolbarIdProp
|
||||
}) => {
|
||||
const reactId = useId();
|
||||
const safeId = reactId.replace(/:/g, "");
|
||||
const toolbarId = toolbarIdProp || `quill-toolbar-${safeId}`;
|
||||
|
||||
const modules = useMemo(() => createModules({ toolbarId }), [toolbarId]);
|
||||
|
||||
return (
|
||||
<div className={`quill-email-editor ${className}`}>
|
||||
{!readOnly && <QuillToolbar toolbarId={toolbarId} />}
|
||||
|
||||
<ReactQuill
|
||||
theme={theme}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
modules={modules}
|
||||
formats={formats}
|
||||
readOnly={readOnly}
|
||||
placeholder={placeholder}
|
||||
className={editorClassName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(QuillEmailEditor);
|
||||
@@ -0,0 +1,98 @@
|
||||
import React from "react";
|
||||
|
||||
const CustomUndo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CustomRedo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const QuillToolbar = ({ toolbarId }) => {
|
||||
return (
|
||||
<div id={toolbarId}>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-font" defaultValue="arial">
|
||||
<option value="arial">Arial</option>
|
||||
<option value="comic-sans">Comic Sans</option>
|
||||
<option value="courier-new">Courier New</option>
|
||||
<option value="georgia">Georgia</option>
|
||||
<option value="helvetica">Helvetica</option>
|
||||
<option value="lucida">Lucida</option>
|
||||
</select>
|
||||
|
||||
<select className="ql-size" defaultValue="medium">
|
||||
<option value="extra-small">Size 1</option>
|
||||
<option value="small">Size 2</option>
|
||||
<option value="medium">Size 3</option>
|
||||
<option value="large">Size 4</option>
|
||||
</select>
|
||||
|
||||
<select className="ql-header" defaultValue="">
|
||||
<option value="1">Heading</option>
|
||||
<option value="2">Subheading</option>
|
||||
<option value="3">Normal</option>
|
||||
</select>
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<button className="ql-bold" type="button" />
|
||||
<button className="ql-italic" type="button" />
|
||||
<button className="ql-underline" type="button" />
|
||||
<button className="ql-strike" type="button" />
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<button className="ql-list" value="ordered" type="button" />
|
||||
<button className="ql-list" value="bullet" type="button" />
|
||||
<button className="ql-indent" value="-1" type="button" />
|
||||
<button className="ql-indent" value="+1" type="button" />
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<button className="ql-script" value="super" type="button" />
|
||||
<button className="ql-script" value="sub" type="button" />
|
||||
<button className="ql-blockquote" type="button" />
|
||||
<button className="ql-direction" type="button" />
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<select className="ql-align" />
|
||||
<select className="ql-color" />
|
||||
<select className="ql-background" />
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<button className="ql-link" type="button" />
|
||||
<button className="ql-image" type="button" />
|
||||
<button className="ql-video" type="button" />
|
||||
<button className="ql-formula" type="button" />
|
||||
<button className="ql-code-block" type="button" />
|
||||
<button className="ql-clean" type="button" />
|
||||
</span>
|
||||
|
||||
<span className="ql-formats">
|
||||
<button className="ql-undo" type="button">
|
||||
<CustomUndo />
|
||||
</button>
|
||||
<button className="ql-redo" type="button">
|
||||
<CustomRedo />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(QuillToolbar);
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./QuillEmailEditor";
|
||||
export { default as QuillEmailEditor } from "./QuillEmailEditor";
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Quill } from "react-quill-new";
|
||||
import htmlEditButton from "quill-html-edit-button";
|
||||
|
||||
// Register modules once
|
||||
// Quill.register({ "modules/table-better": QuillTableBetter }, true);
|
||||
Quill.register("modules/htmlEditButton", htmlEditButton, true);
|
||||
|
||||
// Size whitelist
|
||||
const Size = Quill.import("formats/size");
|
||||
Size.whitelist = ["extra-small", "small", "medium", "large"];
|
||||
Quill.register(Size, true);
|
||||
|
||||
// Font whitelist
|
||||
const Font = Quill.import("formats/font");
|
||||
Font.whitelist = [
|
||||
"arial",
|
||||
"comic-sans",
|
||||
"courier-new",
|
||||
"georgia",
|
||||
"helvetica",
|
||||
"lucida"
|
||||
];
|
||||
Quill.register(Font, true);
|
||||
|
||||
const undoChange = function () {
|
||||
this.quill.history.undo();
|
||||
};
|
||||
|
||||
const redoChange = function () {
|
||||
this.quill.history.redo();
|
||||
};
|
||||
|
||||
const HISTORY_CONFIG = { delay: 500, maxStack: 100, userOnly: true };
|
||||
|
||||
export const createModules = ({ toolbarId }) => ({
|
||||
toolbar: {
|
||||
container: `#${toolbarId}`,
|
||||
handlers: { undo: undoChange, redo: redoChange }
|
||||
},
|
||||
htmlEditButton: {},
|
||||
history: HISTORY_CONFIG
|
||||
});
|
||||
|
||||
export const formats = [
|
||||
"header",
|
||||
"font",
|
||||
"size",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"strike",
|
||||
"script",
|
||||
"blockquote",
|
||||
"direction",
|
||||
"align",
|
||||
"background",
|
||||
"list",
|
||||
"indent",
|
||||
"link",
|
||||
"image",
|
||||
"video",
|
||||
"color",
|
||||
"formula",
|
||||
"code-block"
|
||||
];
|
||||
@@ -46,11 +46,18 @@ function CustomizeMail(props) {
|
||||
);
|
||||
props?.setCurrUserId && props?.setCurrUserId(ownerDetails?.objectId);
|
||||
}
|
||||
const customMail = {
|
||||
body:
|
||||
props?.emailEditorType === "basic"
|
||||
? props?.customizeMail?.body?.basic
|
||||
: props?.customizeMail?.body?.advanced,
|
||||
subject: props?.customizeMail?.subject
|
||||
};
|
||||
//function is used to send email to signers for sign the document
|
||||
const mailRes = await sendEmailToSigners(
|
||||
documentData,
|
||||
props?.signerList,
|
||||
props?.customizeMail,
|
||||
customMail,
|
||||
props?.defaultMail,
|
||||
isCustomize,
|
||||
);
|
||||
@@ -67,17 +74,28 @@ function CustomizeMail(props) {
|
||||
setIsReset(true);
|
||||
props?.setCustomizeMail({
|
||||
subject: defaultMailSubject,
|
||||
body: defaultMailBody
|
||||
body: { basic: defaultMailBody, advanced: defaultMailBody }
|
||||
});
|
||||
};
|
||||
const onChangeSubject = (value) => {
|
||||
setIsReset(false);
|
||||
props?.setCustomizeMail((prev) => ({ ...prev, subject: value }));
|
||||
};
|
||||
const onChangeBody = (value) => {
|
||||
|
||||
const onChangeBody = (newValue, changedType) => {
|
||||
setIsReset(false);
|
||||
props?.setCustomizeMail((prev) => ({ ...prev, body: value }));
|
||||
props?.setCustomizeMail((prev) => ({
|
||||
...prev,
|
||||
body: { ...prev.body, [changedType]: newValue }
|
||||
}));
|
||||
};
|
||||
const handleSwitch = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const editor = props?.emailEditorType === "basic" ? "advanced" : "basic";
|
||||
props.setEmailEditorType(editor);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoader ? (
|
||||
@@ -101,6 +119,8 @@ function CustomizeMail(props) {
|
||||
onChangeBody={onChangeBody}
|
||||
onChangeSubject={onChangeSubject}
|
||||
isReset={isReset}
|
||||
handleSwitch={handleSwitch}
|
||||
emailEditorType={props.emailEditorType}
|
||||
/>
|
||||
<div
|
||||
className="flex justify-end items-center gap-1 mt-2 op-link op-link-primary"
|
||||
|
||||
@@ -220,7 +220,7 @@ function DropdownWidgetOption(props) {
|
||||
/>
|
||||
|
||||
<i
|
||||
className="fa-light fa-rectangle-xmark text-[25px] ml-[10px] text-accent cursor-pointer"
|
||||
className="fa-light fa-trash-can text-[25px] ml-[10px] text-accent cursor-pointer"
|
||||
onClick={() => handleDeleteInput(index)}
|
||||
></i>
|
||||
</div>
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
import { Quill } from "react-quill-new";
|
||||
|
||||
// Custom Undo button icon component for Quill editor. You can import it directly
|
||||
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
|
||||
// handle them correctly
|
||||
const CustomUndo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Redo button icon component for Quill editor
|
||||
const CustomRedo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Undo and redo functions for Custom Toolbar
|
||||
function undoChange() {
|
||||
this.quill.history.undo();
|
||||
}
|
||||
function redoChange() {
|
||||
this.quill.history.redo();
|
||||
}
|
||||
|
||||
// Add sizes to whitelist and register them
|
||||
const Size = Quill.import("formats/size");
|
||||
Size.whitelist = ["extra-small", "small", "medium", "large"];
|
||||
Quill.register(Size, true);
|
||||
|
||||
// Add fonts to whitelist and register them
|
||||
const Font = Quill.import("formats/font");
|
||||
Font.whitelist = [
|
||||
"arial",
|
||||
"comic-sans",
|
||||
"courier-new",
|
||||
"georgia",
|
||||
"helvetica",
|
||||
"lucida"
|
||||
];
|
||||
Quill.register(Font, true);
|
||||
|
||||
// Modules object for setting up the Quill editor
|
||||
export const module1 = {
|
||||
toolbar: {
|
||||
container: "#toolbar1",
|
||||
handlers: { undo: undoChange, redo: redoChange }
|
||||
},
|
||||
history: { delay: 500, maxStack: 100, userOnly: true }
|
||||
};
|
||||
|
||||
// Modules object for setting up the Quill editor
|
||||
export const module2 = {
|
||||
toolbar: {
|
||||
container: "#toolbar2",
|
||||
handlers: { undo: undoChange, redo: redoChange }
|
||||
},
|
||||
history: { delay: 500, maxStack: 100, userOnly: true }
|
||||
};
|
||||
|
||||
// Formats objects for setting up the Quill editor
|
||||
export const formats = [
|
||||
"header",
|
||||
"font",
|
||||
"size",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"align",
|
||||
"strike",
|
||||
"script",
|
||||
"blockquote",
|
||||
"background",
|
||||
"list",
|
||||
"indent",
|
||||
"link",
|
||||
"image",
|
||||
"color",
|
||||
"code-block"
|
||||
];
|
||||
// Quill Toolbar component
|
||||
export const QuillToolbar = ({ containerId }) => (
|
||||
<div id={containerId}>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-font" defaultValue="arial">
|
||||
<option value="arial">Arial</option>
|
||||
<option value="comic-sans">Comic Sans</option>
|
||||
<option value="courier-new">Courier New</option>
|
||||
<option value="georgia">Georgia</option>
|
||||
<option value="helvetica">Helvetica</option>
|
||||
<option value="lucida">Lucida</option>
|
||||
</select>
|
||||
<select className="ql-size" defaultValue="medium">
|
||||
<option value="extra-small">Size 1</option>
|
||||
<option value="small">Size 2</option>
|
||||
<option value="medium">Size 3</option>
|
||||
<option value="large">Size 4</option>
|
||||
</select>
|
||||
<select className="ql-header" defaultValue="3">
|
||||
<option value="1">Heading</option>
|
||||
<option value="2">Subheading</option>
|
||||
<option value="3">Normal</option>
|
||||
</select>
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-bold" />
|
||||
<button className="ql-italic" />
|
||||
<button className="ql-underline" />
|
||||
<button className="ql-strike" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-list" value="ordered" />
|
||||
<button className="ql-list" value="bullet" />
|
||||
<button className="ql-indent" value="-1" />
|
||||
<button className="ql-indent" value="+1" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-script" value="super" />
|
||||
<button className="ql-script" value="sub" />
|
||||
<button className="ql-blockquote" />
|
||||
<button className="ql-direction" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-align" />
|
||||
<select className="ql-color" />
|
||||
<select className="ql-background" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-link" />
|
||||
<button className="ql-image" />
|
||||
<button className="ql-video" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-formula" />
|
||||
<button className="ql-code-block" />
|
||||
<button className="ql-clean" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-undo">
|
||||
<CustomUndo />
|
||||
</button>
|
||||
<button className="ql-redo">
|
||||
<CustomRedo />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default QuillToolbar;
|
||||
@@ -1,9 +1,6 @@
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
// import ReactQuill from "react-quill-new";
|
||||
// import "../../styles/quill.css";
|
||||
// import EditorToolbar, { module1, formats } from "./EditorToolbar";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import EmailBodyEditor from "../EmailBodyEditor";
|
||||
import EmailEditor from "../emaileditor";
|
||||
|
||||
export function EmailBody(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -22,21 +19,23 @@ export function EmailBody(props) {
|
||||
placeholder='${senderName} has requested you to sign "${documentName}"'
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
<label className="text-sm ml-2 mt-3">
|
||||
{t("body")} <Tooltip message={t("email-body")} />
|
||||
<label className="flex justify-between text-sm ml-2 mt-3">
|
||||
<span>
|
||||
{t("body")} <Tooltip message={t("email-body")} />
|
||||
</span>
|
||||
<button
|
||||
className="op-link op-link-primary"
|
||||
onClick={(e) => props?.handleSwitch(e)}
|
||||
>
|
||||
{props?.emailEditorType === "basic"
|
||||
? t("switch-to-advanced")
|
||||
: t("switch-to-basic")}
|
||||
</button>
|
||||
</label>
|
||||
<div className="px-1 py-2 w-full focus:outline-none text-xs">
|
||||
{/* <EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={props.requestBody}
|
||||
placeholder={t("email-placeholder")}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={props.handleOnchangeRequest}
|
||||
/> */}
|
||||
<EmailBodyEditor
|
||||
value={props.requestBody}
|
||||
<EmailEditor
|
||||
type={props?.emailEditorType}
|
||||
values={props.requestBody}
|
||||
onChange={props?.onChangeBody}
|
||||
isReset={props?.isReset}
|
||||
smallscreen
|
||||
|
||||
@@ -496,7 +496,7 @@ function Placeholder(props) {
|
||||
></i>
|
||||
{/* delete icon for all widgets */}
|
||||
<i
|
||||
className="fa-light fa-circle-xmark icon text-[#188ae2] -right-[8px] -top-[18px] "
|
||||
className="fa-light fa-trash-can icon text-[#188ae2] -right-[8px] -top-[18px] "
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
showGuidelines(false);
|
||||
|
||||
@@ -33,6 +33,8 @@ function PlaceholderType(props) {
|
||||
const textRef = useRef();
|
||||
const prefillImg = useSelector((state) => state.widget.prefillImg);
|
||||
const prefillImgLoad = useSelector((state) => state.widget.prefillImgLoad);
|
||||
const defaultData = props?.pos?.options?.defaultValue;
|
||||
const response = props?.pos?.options?.response;
|
||||
const type = props?.pos?.type;
|
||||
const iswidgetEnable =
|
||||
props.isSignYourself ||
|
||||
@@ -64,11 +66,7 @@ function PlaceholderType(props) {
|
||||
useEffect(() => {
|
||||
if (type !== "date") {
|
||||
if (type && type === "checkbox") {
|
||||
setSelectedCheckbox(
|
||||
props?.pos?.options?.response ||
|
||||
props?.pos?.options?.defaultValue ||
|
||||
[]
|
||||
);
|
||||
setSelectedCheckbox(response || defaultData || []);
|
||||
}
|
||||
else {
|
||||
// keep displayed value in sync with the stored response
|
||||
@@ -128,7 +126,7 @@ function PlaceholderType(props) {
|
||||
const lastWord = name.length > 1 ? `-${name[name.length - 1]}` : "";
|
||||
const title =
|
||||
props?.pos?.type === name[0] ? `${name[0]}${lastWord}` : widgetName;
|
||||
return props?.pos?.options?.hint || title;
|
||||
return defaultData || props?.pos?.options?.hint || title;
|
||||
};
|
||||
//useEffect is used to increase auto height of text/textInput widget when user using multiline option and enter value in next line
|
||||
useEffect(() => {
|
||||
@@ -176,6 +174,7 @@ function PlaceholderType(props) {
|
||||
setDate("");
|
||||
}
|
||||
}, [props?.startDate, props?.selectDate?.format]);
|
||||
|
||||
switch (type) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
@@ -438,10 +437,12 @@ function PlaceholderType(props) {
|
||||
) : (
|
||||
<div
|
||||
style={textWidgetStyle}
|
||||
className="select-none-cls overflow-hidden uppercase"
|
||||
className="select-none-cls overflow-hidden"
|
||||
>
|
||||
<span>
|
||||
{props?.selectDate?.format ||
|
||||
{(defaultData !== "today" && defaultData) ||
|
||||
(response !== "today" && response) ||
|
||||
props?.selectDate?.format ||
|
||||
props.pos?.options?.validation?.format ||
|
||||
"MM/dd/yyyy"}
|
||||
</span>
|
||||
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
getContainerScale,
|
||||
handleImageResize,
|
||||
handleSignYourselfImageResize,
|
||||
isMobile
|
||||
isMobile,
|
||||
textInputWidget,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import Placeholder from "./Placeholder";
|
||||
import Alert from "../../primitives/Alert";
|
||||
@@ -44,6 +46,20 @@ function RenderPdf(props) {
|
||||
const isOpen = useSelector((state) => state.sidebar.isOpen);
|
||||
const scrollRef = useRef(null);
|
||||
const pdfContainerRef = useRef(null);
|
||||
|
||||
// Refs for drawing Text Input widget — avoids re-rendering the full tree on
|
||||
// every pointermove event. Only isDrawing (a boolean) is kept in React state
|
||||
// so that the overlay div is mounted/unmounted; coordinates are stored in
|
||||
// refs and the overlay style is updated imperatively via drawingOverlayRef.
|
||||
const [isDrawing, setIsDrawing] = useState(false);
|
||||
const isDrawingRef = useRef(false);
|
||||
const drawStartRef = useRef(null);
|
||||
const drawEndRef = useRef(null);
|
||||
const drawingOverlayRef = useRef(null);
|
||||
// Ref flag to suppress the click event that fires after pointerup when a
|
||||
// widget was just drawn, preventing the Document onClick from deselecting it.
|
||||
const justDrewRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(toggleSidebar(false));
|
||||
return () => {
|
||||
@@ -311,6 +327,125 @@ function RenderPdf(props) {
|
||||
setScaledHeight(scaleHeight);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle drawing Text Input widget on PDF
|
||||
const handlePointerDown = (e) => {
|
||||
// Only allow drawing in placeholder mode when no widget is selected
|
||||
if (
|
||||
!props.placeholder ||
|
||||
props.currWidgetsDetails?.key ||
|
||||
props.isDragging
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable drawing on mobile/touch devices to avoid conflicts with pinch-to-zoom
|
||||
if (isMobile || e.pointerType !== "mouse") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't draw if clicking on an existing widget
|
||||
if (e.target.closest(".signYourselfBlock")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = pdfContainerRef.current.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
isDrawingRef.current = true;
|
||||
drawStartRef.current = { x, y };
|
||||
drawEndRef.current = { x, y };
|
||||
setIsDrawing(true); // single setState to mount the overlay div
|
||||
};
|
||||
|
||||
const handlePointerMove = (e) => {
|
||||
if (!isDrawingRef.current || !drawStartRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = pdfContainerRef.current.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
drawEndRef.current = { x, y };
|
||||
|
||||
// Update the overlay position directly via DOM — no setState, no re-render
|
||||
if (drawingOverlayRef.current) {
|
||||
const width = Math.abs(x - drawStartRef.current.x);
|
||||
const height = Math.abs(y - drawStartRef.current.y);
|
||||
const rectX = Math.min(drawStartRef.current.x, x);
|
||||
const rectY = Math.min(drawStartRef.current.y, y);
|
||||
const s = drawingOverlayRef.current.style;
|
||||
s.left = `${rectX}px`;
|
||||
s.top = `${rectY}px`;
|
||||
s.width = `${width}px`;
|
||||
s.height = `${height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerUp = (e) => {
|
||||
if (!isDrawingRef.current || !drawStartRef.current || !drawEndRef.current) {
|
||||
isDrawingRef.current = false;
|
||||
drawStartRef.current = null;
|
||||
drawEndRef.current = null;
|
||||
setIsDrawing(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const width = Math.abs(drawEndRef.current.x - drawStartRef.current.x);
|
||||
const height = Math.abs(drawEndRef.current.y - drawStartRef.current.y);
|
||||
|
||||
// Only create widget if the drawn area is large enough (minimum 30x20 pixels)
|
||||
if (width > 30 && height > 20) {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
|
||||
const rectX = Math.min(drawStartRef.current.x, drawEndRef.current.x);
|
||||
const rectY = Math.min(drawStartRef.current.y, drawEndRef.current.y);
|
||||
|
||||
// Convert pixel coordinates to PDF coordinates
|
||||
const xPosition = rectX / (containerScale * props.scale);
|
||||
const yPosition = rectY / (containerScale * props.scale);
|
||||
const widgetWidth = width / (containerScale * props.scale);
|
||||
const widgetHeight = height / (containerScale * props.scale);
|
||||
|
||||
// Create a Text Input widget at the drawn position
|
||||
if (props.addPositionOfSignature) {
|
||||
// Simulate dropping a text input widget
|
||||
const type =
|
||||
props?.roleName === "prefill" ? textWidget : textInputWidget;
|
||||
const textInputItem = { text: type, type: "BOX" };
|
||||
|
||||
const monitor = {
|
||||
type: type,
|
||||
getClientOffset: () => ({ x: rectX, y: rectY }),
|
||||
getSourceClientOffset: () => ({ x: rectX, y: rectY })
|
||||
};
|
||||
|
||||
props.addPositionOfSignature(textInputItem, monitor, {
|
||||
customPosition: {
|
||||
xPosition,
|
||||
yPosition,
|
||||
width: widgetWidth,
|
||||
height: widgetHeight
|
||||
}
|
||||
});
|
||||
// Mark that a widget was just drawn so the subsequent click event
|
||||
// does not deselect it via the Document onClick handler.
|
||||
justDrewRef.current = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset drawing state
|
||||
isDrawingRef.current = false;
|
||||
drawStartRef.current = null;
|
||||
drawEndRef.current = null;
|
||||
setIsDrawing(false); // single setState to unmount the overlay div
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{props.successEmail && (
|
||||
@@ -340,13 +475,28 @@ function RenderPdf(props) {
|
||||
}
|
||||
style={{
|
||||
width:
|
||||
props.containerWH?.width && props.containerWH?.width * props.scale
|
||||
props.containerWH?.width &&
|
||||
props.containerWH?.width * props.scale,
|
||||
cursor:
|
||||
props.placeholder &&
|
||||
!props.currWidgetsDetails?.key &&
|
||||
!props.isDragging
|
||||
? "crosshair"
|
||||
: "default"
|
||||
}}
|
||||
ref={(node) => {
|
||||
pdfContainerRef.current = node;
|
||||
drop && drop(node);
|
||||
}}
|
||||
id="container"
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerLeave={() => {
|
||||
if (isDrawing) {
|
||||
handlePointerUp();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.pdfLoad !== false &&
|
||||
props.containerWH?.width &&
|
||||
@@ -515,6 +665,10 @@ function RenderPdf(props) {
|
||||
props.pageDetails(pdf);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (justDrewRef.current) {
|
||||
justDrewRef.current = false;
|
||||
return;
|
||||
}
|
||||
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails({});
|
||||
showGuidelines(false);
|
||||
}}
|
||||
@@ -561,6 +715,23 @@ function RenderPdf(props) {
|
||||
containerWH={props.containerWH}
|
||||
scale={props?.scale}
|
||||
/>
|
||||
{/* Drawing rectangle overlay for Text Input widget */}
|
||||
{isDrawing && (
|
||||
<div
|
||||
ref={drawingOverlayRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "0px",
|
||||
top: "0px",
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
border: "2px dashed #007bff",
|
||||
backgroundColor: "rgba(0, 123, 255, 0.1)",
|
||||
pointerEvents: "none",
|
||||
zIndex: 9999
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</RSC>
|
||||
</>
|
||||
|
||||
@@ -27,9 +27,9 @@ import {
|
||||
getBase64MimeType,
|
||||
drawWidget,
|
||||
getBase64FromUrl,
|
||||
clearResponse
|
||||
clearResponse,
|
||||
isEmptyValue
|
||||
} from "../../constant/Utils";
|
||||
import CellsWidget from "./CellsWidget";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import moment from "moment";
|
||||
@@ -61,6 +61,7 @@ import UploadImage from "./tab/UploadImage";
|
||||
import TypeSignature from "./tab/TypeSignature";
|
||||
import PenColorComponent from "./tab/PenColorComponent";
|
||||
import TextInput from "./widgets/TextInput";
|
||||
import CellsInput from "./widgets/CellsInput";
|
||||
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
@@ -95,8 +96,6 @@ function WidgetsValueModal(props) {
|
||||
setXyPosition,
|
||||
isSave,
|
||||
signatureTypes,
|
||||
setCellCount,
|
||||
allowCellResize = true,
|
||||
penColors
|
||||
} = props;
|
||||
const previousWidgetRes = signatureResponse?.find(
|
||||
@@ -162,45 +161,21 @@ function WidgetsValueModal(props) {
|
||||
);
|
||||
});
|
||||
const blocked = [
|
||||
"checkbox",
|
||||
"dropdown",
|
||||
"date",
|
||||
"radio",
|
||||
];
|
||||
const blockedTab = ["mysignature", "myinitials", "myStamp"];
|
||||
const showClearbtn =
|
||||
(!blocked.includes(type) && !blockedTab.includes(isTab));
|
||||
const [cellsValue, setCellsValue] = useState(() => {
|
||||
const count = currWidgetsDetails?.options?.cellCount || 5;
|
||||
const val =
|
||||
currWidgetsDetails?.options?.response ||
|
||||
currWidgetsDetails?.options?.defaultValue ||
|
||||
"";
|
||||
return Array.from({ length: count }, (_, i) => val[i] || "");
|
||||
});
|
||||
const isSignOrInitials = ["signature", "initials"].includes(
|
||||
currWidgetsDetails?.type
|
||||
);
|
||||
const isImageOrStamp = ["image", "stamp"].includes(currWidgetsDetails?.type);
|
||||
const cellRefs = useRef([]);
|
||||
const widgetRef = useRef(null);
|
||||
// keep track of the first empty cell to automatically focus it after updates
|
||||
useEffect(() => {
|
||||
const index = cellsValue.findIndex((v) => !v);
|
||||
if (index !== -1) {
|
||||
setTimeout(() => {
|
||||
cellRefs.current[index]?.focus();
|
||||
}, 0);
|
||||
}
|
||||
}, [cellsValue]);
|
||||
|
||||
useEffect(() => {
|
||||
const count = currWidgetsDetails?.options?.cellCount || 5;
|
||||
const val =
|
||||
currWidgetsDetails?.options?.response ||
|
||||
currWidgetsDetails?.options?.defaultValue ||
|
||||
"";
|
||||
setCellsValue(Array.from({ length: count }, (_, i) => val[i] || ""));
|
||||
setWidgetValue(String(val || ""));
|
||||
}, [currWidgetsDetails?.key, currWidgetsDetails?.options?.cellCount]);
|
||||
useEffect(() => {
|
||||
dispatch(setScrollTriggerId(currWidgetsDetails?.key));
|
||||
@@ -225,9 +200,14 @@ function WidgetsValueModal(props) {
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
["name", "email", "job title", "company", textInputWidget].includes(
|
||||
currWidgetsDetails?.type
|
||||
)
|
||||
[
|
||||
"name",
|
||||
"email",
|
||||
"job title",
|
||||
"company",
|
||||
textInputWidget,
|
||||
cellsWidget
|
||||
].includes(currWidgetsDetails?.type)
|
||||
) {
|
||||
if (currWidgetsDetails?.options?.hint) {
|
||||
setHint(currWidgetsDetails?.options.hint);
|
||||
@@ -240,6 +220,7 @@ function WidgetsValueModal(props) {
|
||||
setHint(currWidgetsDetails?.type);
|
||||
}
|
||||
}
|
||||
|
||||
else if (currWidgetsDetails?.type === "date") {
|
||||
setHint(currWidgetsDetails?.options.hint || "");
|
||||
} else if (isSignOrInitials) {
|
||||
@@ -295,6 +276,10 @@ function WidgetsValueModal(props) {
|
||||
uniqueId,
|
||||
false,
|
||||
data?.format,
|
||||
currWidgetsDetails?.options?.fontSize,
|
||||
currWidgetsDetails?.options?.fontColor,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
setSelectDate({ date: date, format: data?.format });
|
||||
};
|
||||
@@ -608,14 +593,9 @@ function WidgetsValueModal(props) {
|
||||
}
|
||||
} else {
|
||||
if (currWidgetsDetails?.type === cellsWidget) {
|
||||
const count =
|
||||
currWidgetsDetails?.options?.cellCount || cellsValue.length || 1;
|
||||
const cleared = Array.from({ length: count }, () => "");
|
||||
setCellsValue(cleared);
|
||||
const combined = cleared.join("");
|
||||
setWidgetValue(combined);
|
||||
setWidgetValue("");
|
||||
onChangeInput(
|
||||
combined,
|
||||
"",
|
||||
currWidgetsDetails,
|
||||
xyPosition,
|
||||
props.index,
|
||||
@@ -623,7 +603,9 @@ function WidgetsValueModal(props) {
|
||||
uniqueId
|
||||
);
|
||||
} else {
|
||||
setSelectedCheckbox([]);
|
||||
setWidgetValue("");
|
||||
setStartDate("");
|
||||
onChangeInput(
|
||||
"",
|
||||
currWidgetsDetails,
|
||||
@@ -1060,17 +1042,16 @@ function WidgetsValueModal(props) {
|
||||
uniqueId
|
||||
);
|
||||
};
|
||||
|
||||
const updateCells = (updated) => {
|
||||
setCellsValue(updated);
|
||||
const combined = updated.join("");
|
||||
setWidgetValue(combined);
|
||||
setWidgetValue(updated);
|
||||
props.setCurrWidgetsDetails?.((prev) =>
|
||||
prev && prev.key === currWidgetsDetails?.key
|
||||
? { ...prev, options: { ...prev.options, response: combined } }
|
||||
? { ...prev, options: { ...prev.options, response: updated } }
|
||||
: prev
|
||||
);
|
||||
onChangeInput(
|
||||
combined,
|
||||
updated,
|
||||
currWidgetsDetails,
|
||||
xyPosition,
|
||||
props.index,
|
||||
@@ -1078,37 +1059,11 @@ function WidgetsValueModal(props) {
|
||||
uniqueId
|
||||
);
|
||||
};
|
||||
const handleCellsInput = (e, idx) => {
|
||||
const handleCellsInput = (e) => {
|
||||
const value = e.target.value;
|
||||
setIsShowValidation(false);
|
||||
const val = e.target.value.slice(0, 1);
|
||||
const updated = [...cellsValue];
|
||||
updated[idx] = val;
|
||||
updateCells(updated);
|
||||
updateCells(value);
|
||||
};
|
||||
const handleCellsKeyDown = (e, idx) => {
|
||||
if (e.key === "Backspace" && !cellsValue[idx] && idx > 0) {
|
||||
e.preventDefault();
|
||||
cellRefs.current[idx - 1]?.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCellResize = (newCount) => {
|
||||
let updated = [...cellsValue];
|
||||
if (newCount > updated.length) {
|
||||
updated = [...updated, ...Array(newCount - updated.length).fill("")];
|
||||
} else if (newCount < updated.length) {
|
||||
updated = updated.slice(0, newCount);
|
||||
}
|
||||
cellRefs.current = cellRefs.current.slice(0, newCount);
|
||||
updateCells(updated);
|
||||
setCellCount?.(currWidgetsDetails?.key, newCount);
|
||||
};
|
||||
|
||||
// when focus leaves the cells widget, validate the input
|
||||
const handleCellsBlur = (e, idx) => {
|
||||
handleInputBlur();
|
||||
};
|
||||
|
||||
//function is used to show widgets on modal according to selected widget type checkbox/date/radio/drodown/textbox/signature/image
|
||||
const getWidgetType = (type) => {
|
||||
const isStampOrImage =
|
||||
@@ -1360,6 +1315,7 @@ function WidgetsValueModal(props) {
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
switch (type) {
|
||||
case "image":
|
||||
case "initials":
|
||||
@@ -1464,24 +1420,18 @@ function WidgetsValueModal(props) {
|
||||
handleOnchangeTextBox={handleOnchangeTextBox}
|
||||
textInputcls={textInputcls}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
handleInputBlur={handleInputBlur}
|
||||
handleInputBlur={handleValidation}
|
||||
/>
|
||||
);
|
||||
case cellsWidget:
|
||||
return (
|
||||
<CellsWidget
|
||||
isEnabled={true}
|
||||
count={cellsValue.length}
|
||||
height="100%"
|
||||
value={cellsValue.join("")}
|
||||
editable={true}
|
||||
resizable={allowCellResize}
|
||||
onChange={handleCellsInput}
|
||||
onKeyDown={handleCellsKeyDown}
|
||||
onBlur={handleCellsBlur}
|
||||
onCellCountChange={allowCellResize ? handleCellResize : undefined}
|
||||
inputRefs={cellRefs}
|
||||
<CellsInput
|
||||
cellsValue={widgetValue}
|
||||
handleCellsInput={handleCellsInput}
|
||||
textInputcls={textInputcls}
|
||||
handleValidation={handleValidation}
|
||||
hint={hint}
|
||||
count={currWidgetsDetails?.options?.cellCount || 0}
|
||||
/>
|
||||
);
|
||||
case "dropdown":
|
||||
@@ -1511,7 +1461,7 @@ function WidgetsValueModal(props) {
|
||||
type="text"
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
value={widgetValue}
|
||||
onBlur={handleInputBlur}
|
||||
onBlur={handleValidation}
|
||||
onChange={(e) => handleOnchangeTextBox(e)}
|
||||
className={textInputcls}
|
||||
/>
|
||||
@@ -1523,7 +1473,7 @@ function WidgetsValueModal(props) {
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
value={widgetValue}
|
||||
type="text"
|
||||
onBlur={handleInputBlur}
|
||||
onBlur={handleValidation}
|
||||
onChange={(e) => handleOnchangeTextBox(e)}
|
||||
className={textInputcls}
|
||||
/>
|
||||
@@ -1533,7 +1483,7 @@ function WidgetsValueModal(props) {
|
||||
<input
|
||||
ref={widgetRef}
|
||||
type="text"
|
||||
onBlur={handleInputBlur}
|
||||
onBlur={handleValidation}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
value={widgetValue}
|
||||
onChange={(e) => handleOnchangeTextBox(e)}
|
||||
@@ -1607,7 +1557,7 @@ function WidgetsValueModal(props) {
|
||||
<input
|
||||
ref={widgetRef}
|
||||
type="email"
|
||||
onBlur={handleInputBlur}
|
||||
onBlur={handleValidation}
|
||||
placeholder={hint || widgetTypeTranslation}
|
||||
value={widgetValue}
|
||||
onChange={(e) => handleOnchangeTextBox(e)}
|
||||
@@ -1773,7 +1723,7 @@ function WidgetsValueModal(props) {
|
||||
}
|
||||
};
|
||||
//function is used when user enter value in any textbox then check validation
|
||||
const handleInputBlur = () => {
|
||||
const handleValidation = () => {
|
||||
const validateType = currWidgetsDetails?.options?.validation?.type;
|
||||
let regexValidation;
|
||||
switch (validateType) {
|
||||
@@ -1938,10 +1888,10 @@ function WidgetsValueModal(props) {
|
||||
selectedCheckbox?.length > 0
|
||||
) {
|
||||
return false;
|
||||
} else if (widgetValue) {
|
||||
return false;
|
||||
} else {
|
||||
} else if (isEmptyValue(widgetValue)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function CellsInput(props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="w-full">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={props?.hint || t("widgets-name.text")}
|
||||
value={props?.cellsValue ?? ""}
|
||||
onChange={(e) => props?.handleCellsInput(e)}
|
||||
className={`${props?.textInputcls} pr-4`}
|
||||
onBlur={props?.handleValidation || props?.handleValidation}
|
||||
maxLength={props?.count}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +1,28 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
// import ReactQuill from "react-quill-new";
|
||||
// import "../../styles/quill.css";
|
||||
// import EditorToolbar, { module1, module2, formats } from "../pdf/EditorToolbar";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import Alert from "../../primitives/Alert";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { withSessionValidation } from "../../utils";
|
||||
import EmailBodyEditor from "../EmailBodyEditor";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setTenantInfo, setUserInfo } from "../../redux/reducers/userReducer";
|
||||
import EmailEditor from "../emaileditor";
|
||||
|
||||
const MailTemplateEditor = ({
|
||||
info,
|
||||
tenantId,
|
||||
}) => {
|
||||
const appName = localStorage.getItem("appname") || "OpenSign™";
|
||||
const appName =
|
||||
"OpenSign™";
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const [requestBody, setRequestBody] = useState("");
|
||||
const [requestBody, setRequestBody] = useState({ basic: "", advanced: "" });
|
||||
const [requestSubject, setRequestSubject] = useState("");
|
||||
const [completionBody, setCompletionBody] = useState("");
|
||||
const [completionBody, setCompletionBody] = useState({
|
||||
basic: "",
|
||||
advanced: ""
|
||||
});
|
||||
const [completionSubject, setCompletionSubject] = useState("");
|
||||
const [isTemplateLoaded, setIsTemplateLoaded] = useState(false);
|
||||
const [isDefaultMail, setIsDefaultMail] = useState({
|
||||
@@ -33,6 +34,10 @@ const MailTemplateEditor = ({
|
||||
completion: false
|
||||
});
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [editorType, setEditorType] = useState({
|
||||
request: "basic",
|
||||
completion: "basic"
|
||||
});
|
||||
const defaultRequestSubject = `{{sender_name}} has requested you to sign {{document_title}}`;
|
||||
const defaultRequestBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
const defaultCompletionSubject = `Document {{document_title}} has been signed by all parties`;
|
||||
@@ -63,23 +68,44 @@ const MailTemplateEditor = ({
|
||||
const updateRes = tenantRes;
|
||||
const defaultRequestBody = `<p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign {{document_title}}.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br>`;
|
||||
if (updateRes?.RequestBody) {
|
||||
setRequestBody(updateRes?.RequestBody);
|
||||
setRequestBody((p) => ({
|
||||
...p,
|
||||
basic: updateRes?.RequestBody,
|
||||
advanced: updateRes?.RequestBody
|
||||
}));
|
||||
setRequestSubject(updateRes?.RequestSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, requestMail: false }));
|
||||
} else {
|
||||
setRequestBody(defaultRequestBody);
|
||||
setRequestBody((p) => ({
|
||||
...p,
|
||||
basic: defaultRequestBody,
|
||||
advanced: defaultRequestBody
|
||||
}));
|
||||
setRequestSubject(defaultRequestSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, requestMail: true }));
|
||||
}
|
||||
if (updateRes?.CompletionBody) {
|
||||
setCompletionBody(updateRes?.CompletionBody);
|
||||
setCompletionBody((p) => ({
|
||||
...p,
|
||||
basic: updateRes?.CompletionBody,
|
||||
advanced: updateRes?.CompletionBody
|
||||
}));
|
||||
setCompletionSubject(updateRes?.CompletionSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, completionMail: false }));
|
||||
} else {
|
||||
setCompletionBody(defaultCompletionBody);
|
||||
setCompletionBody((p) => ({
|
||||
...p,
|
||||
basic: defaultCompletionBody,
|
||||
advanced: defaultCompletionBody
|
||||
}));
|
||||
setCompletionSubject(defaultCompletionSubject);
|
||||
setIsDefaultMail((prev) => ({ ...prev, completionMail: true }));
|
||||
}
|
||||
setEditorType((p) => ({
|
||||
...p,
|
||||
request: updateRes?.EmailEditorType?.request || "basic",
|
||||
completion: updateRes?.EmailEditorType?.completion || "basic"
|
||||
}));
|
||||
setIsTemplateLoaded((prev) => !prev);
|
||||
}
|
||||
};
|
||||
@@ -90,25 +116,35 @@ const MailTemplateEditor = ({
|
||||
const updatedInfo = { ...info };
|
||||
updatedInfo[subject] = response?.[subject] ?? "";
|
||||
updatedInfo[body] = response?.[body] ?? "";
|
||||
updatedInfo.EmailEditorType = response?.EmailEditorType;
|
||||
dispatch(action(updatedInfo));
|
||||
};
|
||||
//function to save completion email template
|
||||
const handleSaveCompletionEmail = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = completionBody.replace(/"/g, "'");
|
||||
const replacedHtmlBody = completionBody[editorType.completion]?.replace(
|
||||
/"/g,
|
||||
"'"
|
||||
);
|
||||
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
|
||||
const updateTenant = await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: {
|
||||
CompletionBody: htmlBody,
|
||||
CompletionSubject: completionSubject
|
||||
CompletionSubject: completionSubject,
|
||||
EmailEditorType: editorType
|
||||
}
|
||||
});
|
||||
if (updateTenant) {
|
||||
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
||||
setCompletionBody(updateRes?.CompletionBody);
|
||||
setCompletionBody((p) => ({
|
||||
...p,
|
||||
basic: updateRes?.CompletionBody,
|
||||
advanced: updateRes?.CompletionBody
|
||||
}));
|
||||
setCompletionSubject(updateRes?.CompletionSubject);
|
||||
setEditorType(updateRes?.EmailEditorType);
|
||||
updateValuesInRedux("CompletionSubject", "CompletionBody", updateRes);
|
||||
setIsAlert({ type: "success", msg: t("saved-successfully") });
|
||||
setTimeout(() => setIsAlert({ type: "", msg: "" }), 1500);
|
||||
@@ -123,22 +159,36 @@ const MailTemplateEditor = ({
|
||||
const handleSaveRequestEmail = withSessionValidation(async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const replacedHtmlBody = requestBody.replace(/"/g, "'");
|
||||
const replacedHtmlBody = requestBody[editorType.request]?.replace(
|
||||
/"/g,
|
||||
"'"
|
||||
);
|
||||
|
||||
const htmlBody = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>${replacedHtmlBody}</body></html>`;
|
||||
const updateTenant = await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { RequestBody: htmlBody, RequestSubject: requestSubject }
|
||||
details: {
|
||||
RequestBody: htmlBody,
|
||||
RequestSubject: requestSubject,
|
||||
EmailEditorType: editorType
|
||||
}
|
||||
});
|
||||
if (updateTenant) {
|
||||
const updateRes = JSON.parse(JSON.stringify(updateTenant));
|
||||
setRequestBody(updateRes?.RequestBody);
|
||||
setRequestBody((p) => ({
|
||||
...p,
|
||||
basic: updateRes?.RequestBody,
|
||||
advanced: updateRes?.RequestBody
|
||||
}));
|
||||
setRequestSubject(updateRes?.RequestSubject);
|
||||
setEditorType(updateRes?.EmailEditorType);
|
||||
let extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.TenantId.RequestBody = updateRes?.RequestBody;
|
||||
extUser.TenantId.RequestSubject = updateRes?.RequestSubject;
|
||||
extUser.TenantId.EmailEditorType = updateRes?.EmailEditorType;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
@@ -160,22 +210,41 @@ const MailTemplateEditor = ({
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
handleModifyMail(request);
|
||||
if (request && !isDefaultMail?.requestMail) {
|
||||
setRequestBody(defaultRequestBody);
|
||||
const emailEditor = {
|
||||
request: "basic",
|
||||
completion: editorType.completion
|
||||
};
|
||||
setRequestBody((p) => ({
|
||||
...p,
|
||||
basic: defaultRequestBody,
|
||||
advanced: defaultRequestBody
|
||||
}));
|
||||
setEditorType((p) => ({ ...p, request: "basic" }));
|
||||
setRequestSubject(defaultRequestSubject);
|
||||
setIsMailLoader((p) => ({ ...p, request: true }));
|
||||
try {
|
||||
await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { RequestBody: "", RequestSubject: "" }
|
||||
details: {
|
||||
RequestBody: "",
|
||||
RequestSubject: "",
|
||||
EmailEditorType: emailEditor
|
||||
}
|
||||
});
|
||||
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.TenantId.RequestBody = "";
|
||||
extUser.TenantId.RequestSubject = "";
|
||||
extUser.TenantId.EmailEditorType = emailEditor;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
dispatch(
|
||||
setUserInfo({ ...info, RequestSubject: "", RequestBody: "" })
|
||||
setUserInfo({
|
||||
...info,
|
||||
RequestSubject: "",
|
||||
RequestBody: "",
|
||||
EmailEditorType: emailEditor
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -184,21 +253,37 @@ const MailTemplateEditor = ({
|
||||
setIsMailLoader((p) => ({ ...p, request: false }));
|
||||
}
|
||||
} else if (completion && !isDefaultMail?.completionMail) {
|
||||
const emailEditor = { request: editorType.request, completion: "basic" };
|
||||
setCompletionSubject(defaultCompletionSubject);
|
||||
setCompletionBody(defaultCompletionBody);
|
||||
setCompletionBody((p) => ({
|
||||
...p,
|
||||
basic: defaultCompletionBody,
|
||||
advanced: defaultCompletionBody
|
||||
}));
|
||||
setEditorType((p) => ({ ...p, completion: "basic" }));
|
||||
setIsMailLoader((p) => ({ ...p, completion: true }));
|
||||
try {
|
||||
await Parse.Cloud.run(cloudfunction, {
|
||||
tenantId: tenantId,
|
||||
details: { CompletionBody: "", CompletionSubject: "" }
|
||||
details: {
|
||||
CompletionBody: "",
|
||||
CompletionSubject: "",
|
||||
EmailEditorType: emailEditor
|
||||
}
|
||||
});
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.TenantId.CompletionBody = "";
|
||||
extUser.TenantId.CompletionSubject = "";
|
||||
extUser.TenantId.EmailEditorType = emailEditor;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
dispatch(
|
||||
setUserInfo({ ...info, CompletionSubject: "", CompletionBody: "" })
|
||||
setUserInfo({
|
||||
...info,
|
||||
CompletionSubject: "",
|
||||
CompletionBody: "",
|
||||
EmailEditorType: emailEditor
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -209,16 +294,21 @@ const MailTemplateEditor = ({
|
||||
}
|
||||
});
|
||||
//function for handle ontext change and save again text in delta
|
||||
const handleOnchangeRequest = (html) => {
|
||||
if (html) {
|
||||
setRequestBody(html);
|
||||
}
|
||||
const handleOnchangeRequest = (newValue, changedType) => {
|
||||
setRequestBody((prev) => ({ ...prev, [changedType]: newValue }));
|
||||
};
|
||||
const handleOnchangeCompletion = (html) => {
|
||||
if (html) {
|
||||
setCompletionBody(html);
|
||||
}
|
||||
|
||||
const handleOnchangeCompletion = (newValue, changedType) => {
|
||||
setCompletionBody((prev) => ({ ...prev, [changedType]: newValue }));
|
||||
};
|
||||
|
||||
const handleSwitch = (e, flow) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const editor = editorType[flow] === "basic" ? "advanced" : "basic";
|
||||
setEditorType((p) => ({ ...p, [flow]: editor }));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
|
||||
@@ -266,24 +356,26 @@ const MailTemplateEditor = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="text-lg font-normal py-2">
|
||||
<label className="text-sm mt-3">
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"request-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{document_title}} {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}`}
|
||||
/>
|
||||
<label className="flex justify-between text-sm mt-3">
|
||||
<span>
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"request-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{document_title}} {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}, {{note}}`}
|
||||
/>
|
||||
</span>
|
||||
<button
|
||||
className="op-link op-link-primary"
|
||||
onClick={(e) => handleSwitch(e, "request")}
|
||||
>
|
||||
{editorType.request === "basic"
|
||||
? t("switch-to-advanced")
|
||||
: t("switch-to-basic")}
|
||||
</button>
|
||||
</label>
|
||||
{/* <EditorToolbar containerId="toolbar1" /> */}
|
||||
{/* <ReactQuill
|
||||
theme="snow"
|
||||
value={requestBody}
|
||||
placeholder="add body of email"
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={(value) => handleOnchangeRequest(value)}
|
||||
/> */}
|
||||
<EmailBodyEditor
|
||||
value={requestBody}
|
||||
<EmailEditor
|
||||
type={editorType.request}
|
||||
values={requestBody}
|
||||
onChange={handleOnchangeRequest}
|
||||
bodyName="request"
|
||||
isReset={isMailLoader?.request}
|
||||
@@ -292,7 +384,7 @@ const MailTemplateEditor = ({
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
disabled={!requestBody || !requestSubject}
|
||||
disabled={!requestBody[editorType.request] || !requestSubject}
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
@@ -350,24 +442,26 @@ const MailTemplateEditor = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="text-lg font-normal py-2">
|
||||
<label className="text-sm mt-3">
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"complete-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{document_title}} {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{company_name}}, {{signing_url}}, {{note}}`}
|
||||
/>
|
||||
<label className="flex justify-between text-sm mt-3">
|
||||
<span>
|
||||
{t("body")}{" "}
|
||||
<Tooltip
|
||||
id={"complete-body-tooltip"}
|
||||
message={`${t("variables-use")}: {{document_title}} {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{company_name}}, {{signing_url}}, {{note}}`}
|
||||
/>
|
||||
</span>
|
||||
<button
|
||||
className="op-link op-link-primary"
|
||||
onClick={(e) => handleSwitch(e, "completion")}
|
||||
>
|
||||
{editorType.completion === "basic"
|
||||
? t("switch-to-advanced")
|
||||
: t("switch-to-basic")}
|
||||
</button>
|
||||
</label>
|
||||
{/* <EditorToolbar containerId="toolbar2" /> */}
|
||||
{/* <ReactQuill
|
||||
theme="snow"
|
||||
value={completionBody}
|
||||
placeholder="add body of email"
|
||||
modules={module2}
|
||||
formats={formats}
|
||||
onChange={(value) => handleOnchangeCompletion(value)}
|
||||
/> */}
|
||||
<EmailBodyEditor
|
||||
value={completionBody}
|
||||
<EmailEditor
|
||||
type={editorType.completion}
|
||||
values={completionBody}
|
||||
onChange={handleOnchangeCompletion}
|
||||
bodyName="completion"
|
||||
isReset={isMailLoader?.completion}
|
||||
@@ -376,7 +470,9 @@ const MailTemplateEditor = ({
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
disabled={!completionBody || !completionSubject}
|
||||
disabled={
|
||||
!completionBody[editorType.completion] || !completionSubject
|
||||
}
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* CellsInput (cells for a single word/value)
|
||||
*
|
||||
* Props:
|
||||
* - count: number (how many cells)
|
||||
* - value: string (controlled)
|
||||
* - onChange: (nextValue: string) => void
|
||||
* - hint
|
||||
* - textSize: number (px) default 12
|
||||
* - textColor: string default "#000000"
|
||||
* - only: "all" | "digits" | "letters" | "alphanumeric"
|
||||
* - autoFocus: boolean
|
||||
* - disabled: boolean
|
||||
*/
|
||||
export default function CellsInput({
|
||||
count = 1,
|
||||
value,
|
||||
onChange,
|
||||
hint = "",
|
||||
textSize = 12,
|
||||
textColor = "#000000",
|
||||
only = "all",
|
||||
autoFocus = false,
|
||||
disabled = false,
|
||||
className = "",
|
||||
}) {
|
||||
const isControlled = value !== undefined && typeof onChange === "function";
|
||||
const [internal, setInternal] = useState("");
|
||||
|
||||
const currentValue = isControlled ? value : internal;
|
||||
|
||||
const safeCount = useMemo(() => clampInt(count, 1, 64, 6), [count]);
|
||||
|
||||
// refs for focusing next/prev
|
||||
const inputsRef = useRef([]);
|
||||
|
||||
// normalize value length
|
||||
useEffect(() => {
|
||||
const normalized = normalizeValue(currentValue, safeCount, only);
|
||||
if (normalized !== currentValue) emit(normalized);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [safeCount, only]);
|
||||
|
||||
useEffect(() => {
|
||||
if (autoFocus && !disabled) {
|
||||
queueMicrotask(() => inputsRef.current?.[0]?.focus?.());
|
||||
}
|
||||
}, [autoFocus, disabled]);
|
||||
|
||||
function emit(next) {
|
||||
if (isControlled) onChange(next);
|
||||
else setInternal(next);
|
||||
}
|
||||
|
||||
const chars = useMemo(() => {
|
||||
const v = normalizeValue(currentValue, safeCount, only);
|
||||
const arr = Array.from({ length: safeCount }, (_, i) => v[i] || "");
|
||||
return arr;
|
||||
}, [currentValue, safeCount, only]);
|
||||
|
||||
const inputStyle = {
|
||||
fontSize: `${Number(textSize || 12)}px`,
|
||||
color: textColor && String(textColor).trim() ? textColor : "#000000"
|
||||
};
|
||||
|
||||
function focusIndex(i) {
|
||||
const el = inputsRef.current?.[i];
|
||||
if (el) {
|
||||
el.focus();
|
||||
el.select?.();
|
||||
}
|
||||
}
|
||||
|
||||
function setCharAt(index, ch) {
|
||||
const v = normalizeValue(currentValue, safeCount, only);
|
||||
const nextArr = Array.from({ length: safeCount }, (_, i) => v[i] || "");
|
||||
nextArr[index] = ch;
|
||||
emit(nextArr.join(""));
|
||||
}
|
||||
|
||||
function handleChange(index, raw) {
|
||||
if (disabled) return;
|
||||
|
||||
// user typed multiple chars (mobile suggestions/paste) -> distribute
|
||||
const cleaned = filterString(raw, only);
|
||||
if (!cleaned) {
|
||||
setCharAt(index, "");
|
||||
return;
|
||||
}
|
||||
|
||||
const v = normalizeValue(currentValue, safeCount, only);
|
||||
const nextArr = Array.from({ length: safeCount }, (_, i) => v[i] || "");
|
||||
|
||||
let writeAt = index;
|
||||
for (const ch of cleaned) {
|
||||
if (writeAt >= safeCount) break;
|
||||
nextArr[writeAt] = ch;
|
||||
writeAt++;
|
||||
}
|
||||
emit(nextArr.join(""));
|
||||
|
||||
// move focus
|
||||
const nextFocus = Math.min(writeAt, safeCount - 1);
|
||||
if (nextFocus !== index) focusIndex(nextFocus);
|
||||
}
|
||||
|
||||
function handleKeyDown(index, e) {
|
||||
if (disabled) return;
|
||||
|
||||
const key = e.key;
|
||||
|
||||
if (key === "Backspace") {
|
||||
e.preventDefault();
|
||||
const v = normalizeValue(currentValue, safeCount, only);
|
||||
if (v[index]) {
|
||||
setCharAt(index, "");
|
||||
return;
|
||||
}
|
||||
// if empty, go previous
|
||||
if (index > 0) {
|
||||
focusIndex(index - 1);
|
||||
setCharAt(index - 1, "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "ArrowLeft") {
|
||||
e.preventDefault();
|
||||
if (index > 0) focusIndex(index - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === "ArrowRight") {
|
||||
e.preventDefault();
|
||||
if (index < safeCount - 1) focusIndex(index + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === " " && only !== "all") {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePaste(index, e) {
|
||||
if (disabled) return;
|
||||
e.preventDefault();
|
||||
const text = e.clipboardData?.getData("text") || "";
|
||||
const cleaned = filterString(text, only);
|
||||
if (!cleaned) return;
|
||||
|
||||
const v = normalizeValue(currentValue, safeCount, only);
|
||||
const nextArr = Array.from({ length: safeCount }, (_, i) => v[i] || "");
|
||||
|
||||
let writeAt = index;
|
||||
for (const ch of cleaned) {
|
||||
if (writeAt >= safeCount) break;
|
||||
nextArr[writeAt] = ch;
|
||||
writeAt++;
|
||||
}
|
||||
emit(nextArr.join(""));
|
||||
|
||||
const nextFocus = Math.min(writeAt, safeCount - 1);
|
||||
focusIndex(nextFocus);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`w-full ${className}`}>
|
||||
<div className="flex items-center justify-center gap-0">
|
||||
{chars.map((ch, i) => (
|
||||
<input
|
||||
key={i}
|
||||
ref={(el) => (inputsRef.current[i] = el)}
|
||||
type="text"
|
||||
inputMode={only === "digits" ? "numeric" : "text"}
|
||||
autoComplete="one-time-code"
|
||||
className={[
|
||||
"op-input op-input-bordered op-input-sm w-9 text-center rounded-sm focus:outline-none hover:border-base-content disabled:opacity-60",
|
||||
].join(" ")}
|
||||
style={inputStyle}
|
||||
value={ch}
|
||||
// maxLength={safeCount} // allows multi-char suggestions; we still distribute
|
||||
disabled={disabled}
|
||||
onChange={(e) => handleChange(i, e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(i, e)}
|
||||
onPaste={(e) => handlePaste(i, e)}
|
||||
placeholder={hint[i]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ---------------- helpers ---------------- */
|
||||
|
||||
function clampInt(v, min, max, fallback) {
|
||||
const n = parseInt(String(v), 10);
|
||||
if (Number.isNaN(n)) return fallback;
|
||||
return Math.max(min, Math.min(max, n));
|
||||
}
|
||||
|
||||
function filterString(str, only) {
|
||||
const s = String(str ?? "");
|
||||
if (only === "digits") return s.replace(/[^0-9]/g, "");
|
||||
if (only === "letters") return s.replace(/[^a-zA-Z]/g, "");
|
||||
if (only === "alphanumeric") return s.replace(/[^a-zA-Z0-9]/g, "");
|
||||
return s; // for "all" support all characters
|
||||
}
|
||||
|
||||
function normalizeValue(v, count, only) {
|
||||
const cleaned = filterString(v || "", only);
|
||||
return cleaned.slice(0, count);
|
||||
}
|
||||
@@ -923,6 +923,13 @@ export const createDocument = async (
|
||||
}
|
||||
});
|
||||
}
|
||||
const useNameAsSender = extClass?.[0]?.UseNameAsSender === true;
|
||||
const senderName =
|
||||
Doc?.SenderName || (useNameAsSender ? extClass?.[0]?.Name || "" : "");
|
||||
const senderMail =
|
||||
Doc?.SenderMail || (useNameAsSender ? extClass?.[0]?.Email || "" : "");
|
||||
const SenderName = senderName ? { SenderName: senderName } : {};
|
||||
const SenderMail = senderMail ? { SenderMail: senderMail } : {};
|
||||
const SignatureType = Doc?.SignatureType
|
||||
? { SignatureType: Doc?.SignatureType }
|
||||
: {};
|
||||
@@ -970,6 +977,8 @@ export const createDocument = async (
|
||||
DocSentAt: { __type: "Date", iso: isoDate },
|
||||
...SignatureType,
|
||||
...NotifyOnSignatures,
|
||||
...SenderName,
|
||||
...SenderMail,
|
||||
...Bcc,
|
||||
...RedirectUrl,
|
||||
...TemplateId,
|
||||
@@ -1891,6 +1900,12 @@ const forceBreakLongWord = (word, width, font, fontSize) => {
|
||||
return parts;
|
||||
};
|
||||
|
||||
export const isEmptyValue = (val) =>
|
||||
val === null ||
|
||||
val === undefined ||
|
||||
(typeof val === "string" && val.trim() === "") ||
|
||||
(Array.isArray(val) && val.length === 0);
|
||||
|
||||
//function for embed all type widgets in document using pdf-lib
|
||||
export const embedWidgetsToDoc = async (
|
||||
widgets,
|
||||
@@ -1921,8 +1936,8 @@ export const embedWidgetsToDoc = async (
|
||||
updateItem = item.pos.filter(
|
||||
(data) =>
|
||||
data?.options?.SignUrl ||
|
||||
data?.options?.defaultValue ||
|
||||
data?.options?.response ||
|
||||
!isEmptyValue(data?.options?.defaultValue) ||
|
||||
!isEmptyValue(data?.options?.response) ||
|
||||
data?.type === "checkbox" ||
|
||||
data?.type === radioButtonWidget
|
||||
);
|
||||
@@ -2134,7 +2149,7 @@ export const embedWidgetsToDoc = async (
|
||||
}
|
||||
} else if (isTextTypeWidget) {
|
||||
let textContent = "";
|
||||
if (position?.options?.response) {
|
||||
if (!isEmptyValue(position?.options?.response)) {
|
||||
if (
|
||||
position.type === "date" &&
|
||||
position.options?.response === "today"
|
||||
@@ -2146,7 +2161,7 @@ export const embedWidgetsToDoc = async (
|
||||
} else {
|
||||
textContent = position.options?.response;
|
||||
}
|
||||
} else if (position?.options?.defaultValue) {
|
||||
} else if (!isEmptyValue(position?.options?.defaultValue)) {
|
||||
textContent = position?.options?.defaultValue?.toString();
|
||||
}
|
||||
if (position.type === cellsWidget) {
|
||||
@@ -2293,7 +2308,11 @@ export const embedWidgetsToDoc = async (
|
||||
1,
|
||||
getSize
|
||||
);
|
||||
const dropdownSelected = { ...dropdownOption, font: font };
|
||||
const dropdownSelected = {
|
||||
...dropdownOption,
|
||||
font: font,
|
||||
textColor: updateColorInRgb
|
||||
};
|
||||
dropdown.defaultUpdateAppearances(font);
|
||||
dropdown.addToPage(page, dropdownSelected);
|
||||
dropdown.enableReadOnly();
|
||||
@@ -2624,9 +2643,8 @@ export const handleCopyNextToWidget = (
|
||||
const getXYdata = getPageNumer?.pos;
|
||||
|
||||
getXYdata.push(newposition);
|
||||
|
||||
const updatePlaceholder = xyPosition.map((obj, ind) => {
|
||||
if (ind === index) {
|
||||
if (obj?.pageNumber === index) {
|
||||
return { ...obj, pos: getXYdata };
|
||||
}
|
||||
return obj;
|
||||
@@ -2923,14 +2941,18 @@ export const handleToPrint = async (event, setIsDownloading, pdfDetails) => {
|
||||
const downloadCertificate = async (certificate, isZip, asBlob) => {
|
||||
try {
|
||||
const appName = "OpenSign™";
|
||||
const fetchCertificate = await fetch(certificate);
|
||||
const certificateUrl = certificate;
|
||||
if (isZip) {
|
||||
return certificateUrl;
|
||||
} else {
|
||||
// Convert the response into a Blob
|
||||
const blob = asBlob ? await fetchCertificate.blob() : certificateUrl;
|
||||
saveAs(blob, `Certificate_signed_by_${appName}.pdf`);
|
||||
if (asBlob) {
|
||||
const fetchCertificate = await fetch(certificateUrl);
|
||||
// Convert the response into a Blob
|
||||
const blob = await fetchCertificate.blob();
|
||||
saveAs(blob, `Certificate_signed_by_${appName}.pdf`);
|
||||
return;
|
||||
}
|
||||
saveAs(certificateUrl, `Certificate_signed_by_${appName}.pdf`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("download certificate err", err);
|
||||
@@ -2954,7 +2976,7 @@ export const handleDownloadCertificate = async (
|
||||
};
|
||||
|
||||
if (initialCertificateUrl) {
|
||||
await downloadCertificate(initialCertificateUrl, isZip);
|
||||
return await downloadCertificate(initialCertificateUrl, isZip);
|
||||
} else {
|
||||
setIsDownloading("certificate");
|
||||
try {
|
||||
@@ -2964,8 +2986,9 @@ export const handleDownloadCertificate = async (
|
||||
});
|
||||
const cert = docDetails?.data?.result?.CertificateUrl;
|
||||
if (cert) {
|
||||
await downloadCertificate(cert, isZip);
|
||||
const certificateUrl = await downloadCertificate(cert, isZip);
|
||||
setIsDownloading("");
|
||||
return certificateUrl;
|
||||
} else {
|
||||
const generateRes = await axios.post(
|
||||
`${baseUrl}/generatecertificate`,
|
||||
@@ -2975,8 +2998,13 @@ export const handleDownloadCertificate = async (
|
||||
const certificate = generateRes?.data?.result?.CertificateUrl;
|
||||
if (certificate) {
|
||||
try {
|
||||
await downloadCertificate(certificate, isZip, true);
|
||||
const certificateUrl = await downloadCertificate(
|
||||
certificate,
|
||||
isZip,
|
||||
true
|
||||
);
|
||||
setIsDownloading("");
|
||||
return certificateUrl;
|
||||
} catch (err) {
|
||||
console.error("download certificate err", err);
|
||||
setIsDownloading("certificate_err");
|
||||
@@ -2991,6 +3019,7 @@ export const handleDownloadCertificate = async (
|
||||
alert(i18n.t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// Function to escape special characters in the search string
|
||||
export function escapeRegExp(string) {
|
||||
@@ -3792,10 +3821,10 @@ export const handleCheckResponse = (checkUser, setminRequiredCount) => {
|
||||
let checkSigned;
|
||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||
checkSigned = requiredWidgets[i]?.options?.response;
|
||||
if (!checkSigned) {
|
||||
if (isEmptyValue(checkSigned)) {
|
||||
let checkDefaultSigned =
|
||||
requiredWidgets[i]?.options?.defaultValue;
|
||||
if (!checkDefaultSigned && !showAlert) {
|
||||
if (isEmptyValue(checkDefaultSigned) && !showAlert) {
|
||||
showAlert = true;
|
||||
widgetKey = requiredWidgets[i].key;
|
||||
tourPageNumber = updatePage;
|
||||
@@ -3910,7 +3939,8 @@ export const sendEmailToSigners = async (
|
||||
year: "numeric"
|
||||
});
|
||||
|
||||
let senderEmail = pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderEmail =
|
||||
pdfDetails?.[0]?.SenderMail || pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
let signerMail = signersdata.slice();
|
||||
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
|
||||
@@ -3934,7 +3964,18 @@ export const sendEmailToSigners = async (
|
||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||
? pdfDetails[0].ExtUserPtr.Company
|
||||
: "";
|
||||
const senderName = pdfDetails?.[0]?.ExtUserPtr.Name;
|
||||
|
||||
const useNameAsSender =
|
||||
pdfDetails?.[0]?.ExtUserPtr?.UseNameAsSender === true;
|
||||
|
||||
const senderName =
|
||||
pdfDetails?.[0]?.SenderName || pdfDetails?.[0]?.ExtUserPtr?.Name;
|
||||
|
||||
const from =
|
||||
pdfDetails?.[0]?.SenderName || useNameAsSender
|
||||
? pdfDetails?.[0]?.ExtUserPtr?.Name || ""
|
||||
: senderEmail;
|
||||
|
||||
const documentName = `${pdfDetails?.[0].Name}`;
|
||||
let replaceVar;
|
||||
|
||||
@@ -4002,7 +4043,7 @@ export const sendEmailToSigners = async (
|
||||
? replaceVar?.subject
|
||||
: mailTemplate(mailparam).subject,
|
||||
replyto: senderEmail,
|
||||
from: senderEmail,
|
||||
from: from,
|
||||
html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const appInfo = {
|
||||
defaultRole: "contracts_User",
|
||||
fev_Icon:
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC",
|
||||
googleClietId: process.env.REACT_APP_GOOGLECLIENTID
|
||||
googleClientId: process.env.REACT_APP_GOOGLECLIENTID
|
||||
? `${process.env.REACT_APP_GOOGLECLIENTID}`
|
||||
: "",
|
||||
metaDescription:
|
||||
|
||||
@@ -401,6 +401,10 @@ const Forms = (props) => {
|
||||
"RequestSubject",
|
||||
extUserData?.TenantId?.RequestSubject
|
||||
);
|
||||
object.set(
|
||||
"EmailEditorType",
|
||||
extUserData?.TenantId?.EmailEditorType
|
||||
);
|
||||
}
|
||||
}
|
||||
if (props.title !== "Sign Yourself") {
|
||||
@@ -457,6 +461,16 @@ const Forms = (props) => {
|
||||
className: "contracts_Users",
|
||||
objectId: ExtCls[0].objectId
|
||||
});
|
||||
if (extUserData?.UseNameAsSender) {
|
||||
const senderName = extUserData?.Name || "";
|
||||
const senderMail = extUserData?.Email || "";
|
||||
if (senderName) {
|
||||
object.set("SenderName", senderName);
|
||||
}
|
||||
if (senderMail) {
|
||||
object.set("SenderMail", senderMail);
|
||||
}
|
||||
}
|
||||
const res = await object.save();
|
||||
if (res) {
|
||||
setSigners([]);
|
||||
|
||||
@@ -813,9 +813,11 @@ function PdfRequestFiles(
|
||||
{ day: "numeric", month: "long", year: "numeric" }
|
||||
);
|
||||
let senderEmail =
|
||||
pdfDetails?.[0]?.SenderMail ||
|
||||
pdfDetails?.[0]?.ExtUserPtr?.Email;
|
||||
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
|
||||
const senderName =
|
||||
pdfDetails?.[0]?.SenderName ||
|
||||
pdfDetails?.[0].ExtUserPtr.Name;
|
||||
const documentName = pdfDetails?.[0].Name;
|
||||
try {
|
||||
@@ -892,6 +894,7 @@ function PdfRequestFiles(
|
||||
? replaceVar?.subject
|
||||
: mailTemplate(mailparam).subject,
|
||||
from:
|
||||
pdfDetails?.[0]?.SenderName ||
|
||||
senderEmail,
|
||||
html: replaceVar?.body
|
||||
? replaceVar?.body
|
||||
|
||||
@@ -77,7 +77,11 @@ function PlaceHolderSign() {
|
||||
const editorRef = useRef();
|
||||
const navigate = useNavigate();
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [customizeMail, setCustomizeMail] = useState({
|
||||
body: { basic: "", advanced: "" },
|
||||
subject: ""
|
||||
});
|
||||
const [emailEditorType, setEmailEditorType] = useState("basic");
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [pdfDetails, setPdfDetails] = useState([]);
|
||||
const [isMailSend, setIsMailSend] = useState(false);
|
||||
@@ -196,9 +200,13 @@ function PlaceHolderSign() {
|
||||
const userBody =
|
||||
body;
|
||||
|
||||
const finalBody = userBody || defaultMailBody;
|
||||
const emailEditorType =
|
||||
tenantDetails?.EmailEditorType;
|
||||
setEmailEditorType(emailEditorType?.request);
|
||||
setCustomizeMail({
|
||||
subject: userSubject || defaultMailSubject,
|
||||
body: userBody || defaultMailBody
|
||||
body: { basic: finalBody, advanced: finalBody }
|
||||
});
|
||||
setDefaultMail({ subject: userSubject, body: userBody });
|
||||
return filterSignTypes;
|
||||
@@ -488,10 +496,10 @@ function PlaceHolderSign() {
|
||||
}
|
||||
};
|
||||
//function for setting position after drop signature button over pdf
|
||||
const addPositionOfSignature = (item, monitor) => {
|
||||
getSignerPos(item, monitor);
|
||||
const addPositionOfSignature = (item, monitor, customOptions) => {
|
||||
getSignerPos(item, monitor, customOptions);
|
||||
};
|
||||
const getSignerPos = (item, monitor) => {
|
||||
const getSignerPos = (item, monitor, customOptions) => {
|
||||
if (uniqueId) {
|
||||
const signer = signersdata.find((x) => x.Id === uniqueId);
|
||||
const prefillUser = prefillSigner.find((x) => x.Id === uniqueId);
|
||||
@@ -516,7 +524,22 @@ function PlaceHolderSign() {
|
||||
const widgetHeight =
|
||||
defaultWidthHeight(dragTypeValue).height * containerScale;
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
if (item === "onclick") {
|
||||
|
||||
// Handle custom position from drawing (OS-1229)
|
||||
if (customOptions?.customPosition) {
|
||||
({ dropObj, placeHolder } = utils.createCustomPositionWidget({
|
||||
customPosition: customOptions.customPosition,
|
||||
key,
|
||||
containerScale,
|
||||
posZIndex,
|
||||
dragTypeValue,
|
||||
pageNumber,
|
||||
owner,
|
||||
signerPlaceHolder: filterSignerPos?.placeHolder,
|
||||
roleName
|
||||
}));
|
||||
dropData = placeHolder.pos;
|
||||
} else if (item === "onclick") {
|
||||
// `getBoundingClientRect()` is used to get accurate measurement width, height of the Pdf div
|
||||
const divWidth = divRef.current.getBoundingClientRect().width;
|
||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||
@@ -1990,16 +2013,13 @@ function PlaceHolderSign() {
|
||||
pdfDetails[0]?.SendinOrder &&
|
||||
isCurrUser && (
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1 group"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<i className="fa-regular fa-envelope text-[19px] op-text-primary group-hover:text-base-100 "></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -2165,6 +2185,7 @@ function PlaceHolderSign() {
|
||||
divRef={divRef}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setRoleName={setRoleName}
|
||||
roleName={roleName}
|
||||
isShowModal={isShowModal}
|
||||
signBtnPosition={signBtnPosition}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
@@ -2262,6 +2283,8 @@ function PlaceHolderSign() {
|
||||
handleShareList={handleShareList}
|
||||
setCurrUserId={setIsCurrUser}
|
||||
copyUrlRef={copyUrlRef}
|
||||
emailEditorType={emailEditorType}
|
||||
setEmailEditorType={setEmailEditorType}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -51,6 +51,7 @@ const Preferences = () => {
|
||||
const [is12HourTime, setIs12HourTime] = useState(false);
|
||||
const [isLTVEnabled, setIsLTVEnabled] = useState(false);
|
||||
const [fileNameFormat, setFileNameFormat] = useState("DOCNAME");
|
||||
const [useNameAsSender, setUseNameAsSender] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSignType();
|
||||
@@ -138,6 +139,7 @@ const Preferences = () => {
|
||||
const downloadFilenameFormat =
|
||||
_getUser?.DownloadFilenameFormat || "DOCNAME";
|
||||
setFileNameFormat(downloadFilenameFormat);
|
||||
setUseNameAsSender(_getUser?.UseNameAsSender === true);
|
||||
} catch (err) {
|
||||
console.error("Error while getting user details: ", err);
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
@@ -201,6 +203,7 @@ const Preferences = () => {
|
||||
Is12HourTime: is12HourTime,
|
||||
IsLTVEnabled: isLTVEnabled,
|
||||
DownloadFilenameFormat: fileNameFormat,
|
||||
UseNameAsSender: useNameAsSender,
|
||||
};
|
||||
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
||||
if (updateRes) {
|
||||
@@ -215,6 +218,7 @@ const Preferences = () => {
|
||||
extUser.DateFormat = dateFormat;
|
||||
extUser.Is12HourTime = is12HourTime;
|
||||
extUser.DownloadFilenameFormat = fileNameFormat;
|
||||
extUser.UseNameAsSender = useNameAsSender;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
@@ -521,6 +525,46 @@ const Preferences = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label
|
||||
htmlFor="sender-name-toggle"
|
||||
className="text-[14px] mb-[0.7rem] font-medium"
|
||||
>
|
||||
{t("use-name-as-sender")}
|
||||
</label>
|
||||
<a
|
||||
data-tooltip-id="sender-name-toggle-tooltip"
|
||||
className="ml-1"
|
||||
>
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id="sender-name-toggle-tooltip"
|
||||
className="z-[999]"
|
||||
>
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[13px] font-medium">
|
||||
<p>
|
||||
{t("use-name-as-sender-help", {
|
||||
appName: appName
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
<div className="cursor-pointer relative block items-center mb-0 ml-1">
|
||||
<input
|
||||
id="sender-name-toggle"
|
||||
type="checkbox"
|
||||
className="op-toggle checked:[--tglbg:#3368ff] transition-all checked:text-white"
|
||||
checked={useNameAsSender}
|
||||
onChange={() =>
|
||||
setUseNameAsSender((prevValue) => !prevValue)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Enable Tour Section */}
|
||||
<div className="mb-6">
|
||||
<label className="text-[14px] mb-[0.7rem] font-medium">
|
||||
|
||||
@@ -25,6 +25,7 @@ const Report = () => {
|
||||
const [isMoreDocs, setIsMoreDocs] = useState(true);
|
||||
const [tourData, setTourData] = useState([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [signerStatusFilter, setSignerStatusFilter] = useState("all");
|
||||
const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
|
||||
const [isSearchResult, setIsSearchResult] = useState(false);
|
||||
const [allColumns, setAllColumns] = useState([]);
|
||||
@@ -34,6 +35,7 @@ const Report = () => {
|
||||
const [isColumnModal, setIsColumnModal] = useState(false);
|
||||
const [searchLoader, setSearchLoader] = useState(false);
|
||||
const debounceTimer = useRef(null);
|
||||
const hasMountedSignerFilter = useRef(false);
|
||||
const searchAbortRef = useRef(null); // holds AbortController
|
||||
// Number of documents to display per page (should always be half of docLimit for proper pagination)
|
||||
const docPerPage = 10;
|
||||
@@ -46,6 +48,7 @@ const Report = () => {
|
||||
setReportName("");
|
||||
setList([]);
|
||||
setSearchTerm("");
|
||||
setSignerStatusFilter("all");
|
||||
setMobileSearchOpen(false);
|
||||
const saved = JSON.parse(localStorage.getItem("reportColumns") || "{}");
|
||||
if (saved[id]) {
|
||||
@@ -80,6 +83,29 @@ const Report = () => {
|
||||
// eslint-disable-next-line
|
||||
}, [isNextRecord]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasMountedSignerFilter.current) {
|
||||
hasMountedSignerFilter.current = true;
|
||||
return;
|
||||
}
|
||||
setList([]);
|
||||
setIsNextRecord(false);
|
||||
setIsMoreDocs(true);
|
||||
getDocumentbyUserStatus();
|
||||
// eslint-disable-next-line
|
||||
}, [signerStatusFilter]);
|
||||
|
||||
const getDocumentbyUserStatus = async () => {
|
||||
setSearchLoader(true);
|
||||
try {
|
||||
await getReportData(0, docLimit, searchTerm, signerStatusFilter);
|
||||
} catch (error) {
|
||||
console.error("get report by user status error", error);
|
||||
} finally {
|
||||
setSearchLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchChange = withSessionValidation(async (e) => {
|
||||
const term = e.target.value.toLowerCase();
|
||||
setSearchTerm(term);
|
||||
@@ -106,7 +132,13 @@ const Report = () => {
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(
|
||||
url,
|
||||
{ reportId: id, searchTerm: term, skip: 0, limit: docPerPage },
|
||||
{
|
||||
reportId: id,
|
||||
searchTerm: term,
|
||||
signerStatus: signerStatusFilter,
|
||||
skip: 0,
|
||||
limit: docPerPage
|
||||
},
|
||||
{ headers: headers, signal: controller.signal } // ✅ axios abort
|
||||
);
|
||||
// if you want to be extra safe (ignore late responses)
|
||||
@@ -147,7 +179,12 @@ const Report = () => {
|
||||
};
|
||||
}, []);
|
||||
const getReportData = withSessionValidation(
|
||||
async (skipUserRecord = 0, limit = 20, term = searchTerm) => {
|
||||
async (
|
||||
skipUserRecord = 0,
|
||||
limit = 20,
|
||||
term = searchTerm,
|
||||
signerStatus = signerStatusFilter
|
||||
) => {
|
||||
// setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
if (json) {
|
||||
@@ -181,6 +218,9 @@ const Report = () => {
|
||||
if (term) {
|
||||
params.searchTerm = term;
|
||||
}
|
||||
if (signerStatus && signerStatus !== "all") {
|
||||
params.signerStatus = signerStatus;
|
||||
}
|
||||
const url = `${localStorage.getItem("baseUrl")}functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
@@ -271,6 +311,8 @@ const Report = () => {
|
||||
isSearchResult,
|
||||
columnLabels,
|
||||
searchLoader,
|
||||
signerStatusFilter,
|
||||
handleSignerStatusFilter: setSignerStatusFilter,
|
||||
openColumnModal: () => setIsColumnModal(true)
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -35,7 +35,8 @@ import {
|
||||
handleRemoveWidgets,
|
||||
addWidgetSelfsignOptions,
|
||||
getOriginalWH,
|
||||
signatureTypes
|
||||
signatureTypes,
|
||||
isEmptyValue
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router";
|
||||
import Tour from "../primitives/Tour";
|
||||
@@ -556,6 +557,15 @@ function SignYourSelf() {
|
||||
}));
|
||||
}
|
||||
try {
|
||||
const currentExtUser =
|
||||
JSON.parse(localStorage.getItem("Extand_Class") || "[]")?.[0] || {};
|
||||
const useNameAsSender = currentExtUser?.UseNameAsSender === true;
|
||||
const senderName =
|
||||
pdfDetails?.[0]?.SenderName ||
|
||||
(useNameAsSender ? currentExtUser?.Name || "" : "");
|
||||
const senderMail =
|
||||
pdfDetails?.[0]?.SenderMail ||
|
||||
(useNameAsSender ? currentExtUser?.Email || "" : "");
|
||||
const docCls = new Parse.Object("contracts_Document");
|
||||
docCls.id = documentId;
|
||||
if (xyPosition?.length > 0) {
|
||||
@@ -565,6 +575,12 @@ function SignYourSelf() {
|
||||
if (pdfUrl) {
|
||||
docCls.set("URL", pdfUrl);
|
||||
}
|
||||
if (senderName) {
|
||||
docCls.set("SenderName", senderName);
|
||||
}
|
||||
if (senderMail) {
|
||||
docCls.set("SenderMail", senderMail);
|
||||
}
|
||||
const res = await docCls.save();
|
||||
if (res) {
|
||||
if (res) {
|
||||
@@ -627,11 +643,15 @@ function SignYourSelf() {
|
||||
let checkSigned;
|
||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||
checkSigned = requiredWidgets[i]?.options?.response;
|
||||
if (!checkSigned) {
|
||||
if (isEmptyValue(checkSigned)) {
|
||||
const checkSignUrl = requiredWidgets[i]?.SignUrl;
|
||||
let checkDefaultSigned =
|
||||
requiredWidgets[i]?.options?.defaultValue;
|
||||
if (!checkSignUrl && !checkDefaultSigned && !showAlert) {
|
||||
if (
|
||||
!checkSignUrl &&
|
||||
isEmptyValue(checkDefaultSigned) &&
|
||||
!showAlert
|
||||
) {
|
||||
showAlert = true;
|
||||
widgetKey = requiredWidgets[i]?.key;
|
||||
}
|
||||
|
||||
@@ -154,7 +154,11 @@ const TemplatePlaceholder = () => {
|
||||
const [isNewContact, setIsNewContact] = useState({ status: false, id: "" });
|
||||
const [alertMsg, setAlertMsg] = useState({ type: "success", message: "" });
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [customizeMail, setCustomizeMail] = useState({
|
||||
body: { basic: "", advanced: "" },
|
||||
subject: ""
|
||||
});
|
||||
const [emailEditorType, setEmailEditorType] = useState("basic");
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [currUserId, setCurrUserId] = useState(false);
|
||||
const [documentDetails, setDocumentDetails] = useState();
|
||||
@@ -220,9 +224,13 @@ const TemplatePlaceholder = () => {
|
||||
subject;
|
||||
const userBody =
|
||||
body;
|
||||
const finalBody = userBody || defaultMailBody;
|
||||
const emailEditorType =
|
||||
tenantDetails?.EmailEditorType;
|
||||
setEmailEditorType(emailEditorType?.request || "basic");
|
||||
setCustomizeMail({
|
||||
subject: userSubject || defaultMailSubject,
|
||||
body: userBody || defaultMailBody
|
||||
body: { basic: finalBody, advanced: finalBody }
|
||||
});
|
||||
setDefaultMail({ subject: userSubject, body: userBody });
|
||||
return filterSignTypes;
|
||||
@@ -422,11 +430,11 @@ const TemplatePlaceholder = () => {
|
||||
});
|
||||
|
||||
//function for setting position after drop signature button over pdf
|
||||
const addPositionOfSignature = (item, monitor) => {
|
||||
getSignerPos(item, monitor);
|
||||
const addPositionOfSignature = (item, monitor, customOptions) => {
|
||||
getSignerPos(item, monitor, customOptions);
|
||||
};
|
||||
// `getSignerPos` is used to get placeholder position when user place it and save it in array
|
||||
const getSignerPos = (item, monitor) => {
|
||||
const getSignerPos = (item, monitor, customOptions) => {
|
||||
if (uniqueId) {
|
||||
const signer = signersdata.find((x) => x.Id === uniqueId);
|
||||
const prefillUser = prefillSigner.find((x) => x.Id === uniqueId);
|
||||
@@ -450,7 +458,22 @@ const TemplatePlaceholder = () => {
|
||||
filterSignerPos,
|
||||
placeHolder;
|
||||
filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
|
||||
if (item === "onclick") {
|
||||
|
||||
// Handle custom position from drawing (OS-1229)
|
||||
if (customOptions?.customPosition) {
|
||||
({ dropObj, placeHolder } = utils.createCustomPositionWidget({
|
||||
customPosition: customOptions.customPosition,
|
||||
key,
|
||||
containerScale,
|
||||
posZIndex,
|
||||
dragTypeValue,
|
||||
pageNumber,
|
||||
owner,
|
||||
signerPlaceHolder: filterSignerPos?.placeHolder,
|
||||
roleName
|
||||
}));
|
||||
dropData = placeHolder.pos;
|
||||
} else if (item === "onclick") {
|
||||
// `getBoundingClientRect()` is used to get accurate measurement width, height of the Pdf div
|
||||
const divWidth = divRef.current.getBoundingClientRect().width;
|
||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||
@@ -2020,6 +2043,7 @@ const TemplatePlaceholder = () => {
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setRoleName={setRoleName}
|
||||
roleName={roleName}
|
||||
isShowModal={isShowModal}
|
||||
signBtnPosition={signBtnPosition}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
@@ -2216,6 +2240,8 @@ const TemplatePlaceholder = () => {
|
||||
handleShareList={handleShareList}
|
||||
setDocumentDetails={setDocumentDetails}
|
||||
copyUrlRef={copyUrlRef}
|
||||
emailEditorType={emailEditorType}
|
||||
setEmailEditorType={setEmailEditorType}
|
||||
/>
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
@@ -2313,16 +2339,13 @@ const TemplatePlaceholder = () => {
|
||||
currUserId &&
|
||||
pdfDetails[0]?.SendinOrder && (
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1 group"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<i className="fa-regular fa-envelope text-[19px] op-text-primary group-hover:text-base-100 "></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ShareButton from "../../primitives/ShareButton";
|
||||
import DatePicker from "../../components/DatePicker";
|
||||
import Parse from "parse";
|
||||
import {
|
||||
formatDateToDdMmmYyyy,
|
||||
copytoData,
|
||||
fetchUrl,
|
||||
getSignedUrl,
|
||||
@@ -23,12 +24,6 @@ import {
|
||||
defaultMailBody,
|
||||
defaultMailSubject
|
||||
} from "../../constant/Utils";
|
||||
// import EditorToolbar, {
|
||||
// module1,
|
||||
// formats
|
||||
// } from "../../components/pdf/EditorToolbar";
|
||||
// import ReactQuill from "react-quill-new";
|
||||
// import "../../styles/quill.css";
|
||||
import BulkSendUi from "../../components/bulksend/BulkSendUi";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { serverUrl_fn } from "../../constant/appinfo";
|
||||
@@ -41,7 +36,7 @@ import * as utils from "../../utils";
|
||||
import { RenderReportCell } from "../../primitives/RenderReportCell";
|
||||
import CustomizeMail from "../../components/pdf/CustomizeMail";
|
||||
import { useSelector } from "react-redux";
|
||||
import EmailBodyEditor from "../../components/EmailBodyEditor";
|
||||
import EmailEditor from "../../components/emaileditor";
|
||||
|
||||
const DocumentsReport = (props) => {
|
||||
const copyUrlRef = useRef(null);
|
||||
@@ -66,6 +61,7 @@ const DocumentsReport = (props) => {
|
||||
const [alertMsg, setAlertMsg] = useState({ type: "success", message: "" });
|
||||
const [isResendMail, setIsResendMail] = useState({});
|
||||
const [mail, setMail] = useState({ subject: "", body: "" });
|
||||
const [emailEditorType, setEmailEditorType] = useState("basic");
|
||||
const [userDetails, setUserDetails] = useState({});
|
||||
const [isNextStep, setIsNextStep] = useState({});
|
||||
const [isBulkSend, setIsBulkSend] = useState({});
|
||||
@@ -93,7 +89,10 @@ const DocumentsReport = (props) => {
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [customizeMail, setCustomizeMail] = useState({
|
||||
body: { basic: "", advanced: "" },
|
||||
subject: ""
|
||||
});
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [currUserId, setCurrUserId] = useState(false);
|
||||
const [documentDetails, setDocumentDetails] = useState();
|
||||
@@ -213,9 +212,13 @@ const DocumentsReport = (props) => {
|
||||
subject;
|
||||
const userBody =
|
||||
body;
|
||||
const finalBody = userBody || defaultMailBody;
|
||||
const emailEditorType =
|
||||
tenantDetails?.EmailEditorType;
|
||||
setEmailEditorType(emailEditorType?.request || "basic");
|
||||
setCustomizeMail({
|
||||
subject: userSubject || defaultMailSubject,
|
||||
body: userBody || defaultMailBody
|
||||
body: { basic: finalBody, advanced: finalBody }
|
||||
});
|
||||
setDefaultMail({ subject: userSubject, body: userBody });
|
||||
return filterSignTypes;
|
||||
@@ -525,6 +528,14 @@ const DocumentsReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSwitch` is used to change email editor from basic => advanced or vice versa
|
||||
const handleSwitch = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const editor = emailEditorType === "basic" ? "advanced" : "basic";
|
||||
setEmailEditorType(editor);
|
||||
};
|
||||
|
||||
// `handleSubjectChange` is used to add or change subject of resend mail
|
||||
const handleSubjectChange = (subject, doc) => {
|
||||
const encodeBase64 = userDetails?.objectId
|
||||
@@ -542,9 +553,9 @@ const DocumentsReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email,
|
||||
@@ -558,7 +569,7 @@ const DocumentsReport = (props) => {
|
||||
};
|
||||
|
||||
// `handlebodyChange` is used to add or change body of resend mail
|
||||
const handlebodyChange = (body, doc) => {
|
||||
const handlebodyChange = (body, doc, type) => {
|
||||
const encodeBase64 = userDetails?.objectId
|
||||
? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`)
|
||||
: btoa(`${doc.objectId}/${userDetails.Email}`);
|
||||
@@ -574,9 +585,9 @@ const DocumentsReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email || "",
|
||||
@@ -588,7 +599,10 @@ const DocumentsReport = (props) => {
|
||||
const res = replaceMailVaribles("", body, variables);
|
||||
|
||||
if (body) {
|
||||
setMail((prev) => ({ ...prev, body: res.body }));
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
body: { ...prev.body, [type]: res.body }
|
||||
}));
|
||||
}
|
||||
};
|
||||
// `handleNextBtn` is used to open edit mail template screen in resend mail modal
|
||||
@@ -618,9 +632,9 @@ const DocumentsReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: user?.signerPtr?.Name || "",
|
||||
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
|
||||
@@ -638,7 +652,17 @@ const DocumentsReport = (props) => {
|
||||
doc?.ExtUserPtr?.TenantId?.RequestBody ||
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br></body> </html>`;
|
||||
const res = replaceMailVaribles(subject, body, variables);
|
||||
setMail((prev) => ({ ...prev, subject: res.subject, body: res.body }));
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
subject: res.subject,
|
||||
body: { basic: res.body, advanced: res.body }
|
||||
}));
|
||||
setEmailEditorType(
|
||||
doc?.EmailEditorType?.request ||
|
||||
doc?.ExtUserPtr?.EmailEditorType?.request ||
|
||||
doc?.ExtUserPtr?.TenantId?.EmailEditorType?.request ||
|
||||
"basic"
|
||||
);
|
||||
setIsNextStep({ [user.Id]: true });
|
||||
};
|
||||
const handleResendMail = utils.withSessionValidation(async (e, doc, user) => {
|
||||
@@ -651,15 +675,14 @@ const DocumentsReport = (props) => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
let params = {
|
||||
replyto:
|
||||
doc?.ExtUserPtr?.Email ||
|
||||
"",
|
||||
replyto: doc?.SenderMail || doc?.ExtUserPtr?.Email || "",
|
||||
extUserId: doc?.ExtUserPtr?.objectId,
|
||||
recipient: userDetails?.Email,
|
||||
subject: mail.subject,
|
||||
from:
|
||||
doc?.SenderName ||
|
||||
doc?.ExtUserPtr?.Email,
|
||||
html: mail.body
|
||||
html: emailEditorType === "basic" ? mail.body.basic : mail.body.advanced
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(url, params, { headers: headers });
|
||||
@@ -1110,6 +1133,31 @@ const DocumentsReport = (props) => {
|
||||
<i className="fa-light fa-table-columns"></i>
|
||||
</button>
|
||||
)}
|
||||
{props?.ReportName === "In-progress documents" && (
|
||||
<div className="op-dropdown op-dropdown-end">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="focus:outline-none rounded-md text-[18px]"
|
||||
>
|
||||
<i className="fa-light fa-filter"></i>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex="-1"
|
||||
className="op-dropdown-content op-menu op-menu-sm shadow-black/20 bg-base-100 text-base-content rounded-box z-[70] w-52 p-2 shadow-sm"
|
||||
>
|
||||
<li onClick={() => props.handleSignerStatusFilter("all")}>
|
||||
<a>{t("all-signer-status")}</a>
|
||||
</li>
|
||||
<li onClick={() => props.handleSignerStatusFilter("viewed")}>
|
||||
<a>{t("viewed")}</a>
|
||||
</li>
|
||||
<li onClick={() => props.handleSignerStatusFilter("signed")}>
|
||||
<a>{t("signed")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* Search input for report smalle in width */}
|
||||
@@ -1637,27 +1685,28 @@ const DocumentsReport = (props) => {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
className="text-xs ml-1"
|
||||
className="flex justify-between text-sm ml-1"
|
||||
htmlFor="mailbody"
|
||||
>
|
||||
{t("body")}{" "}
|
||||
<span>{t("body")} </span>
|
||||
<button
|
||||
className="op-link op-link-primary"
|
||||
onClick={(e) => handleSwitch(e)}
|
||||
>
|
||||
{emailEditorType === "basic"
|
||||
? t("switch-to-advanced")
|
||||
: t("switch-to-basic")}
|
||||
</button>
|
||||
</label>
|
||||
{/* <EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
id="mailbody"
|
||||
theme="snow"
|
||||
value={mail.body || ""}
|
||||
placeholder="add body of email "
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={(value) =>
|
||||
handlebodyChange(value, item)
|
||||
}
|
||||
/> */}
|
||||
<EmailBodyEditor
|
||||
value={mail.body || ""}
|
||||
onChange={(value) =>
|
||||
handlebodyChange(value, item)
|
||||
<EmailEditor
|
||||
type={emailEditorType}
|
||||
values={mail.body || ""}
|
||||
onChange={(value, type) =>
|
||||
handlebodyChange(
|
||||
value,
|
||||
item,
|
||||
type
|
||||
)
|
||||
}
|
||||
smallscreen
|
||||
/>
|
||||
@@ -1807,6 +1856,8 @@ const DocumentsReport = (props) => {
|
||||
setDocumentDetails={setDocumentDetails}
|
||||
handleClose={handleCloseMail}
|
||||
copyUrlRef={copyUrlRef}
|
||||
emailEditorType={emailEditorType}
|
||||
setEmailEditorType={setEmailEditorType}
|
||||
/>
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
|
||||
@@ -20,12 +20,6 @@ import {
|
||||
defaultMailBody,
|
||||
defaultMailSubject
|
||||
} from "../../constant/Utils";
|
||||
// import EditorToolbar, {
|
||||
// module1,
|
||||
// formats
|
||||
// } from "../../components/pdf/EditorToolbar";
|
||||
// import ReactQuill from "react-quill-new";
|
||||
// import "../../styles/quill.css";
|
||||
import BulkSendUi from "../../components/bulksend/BulkSendUi";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import { serverUrl_fn } from "../../constant/appinfo";
|
||||
@@ -40,7 +34,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import { RenderReportCell } from "../../primitives/RenderReportCell";
|
||||
import CustomizeMail from "../../components/pdf/CustomizeMail";
|
||||
import { resetWidgetState } from "../../redux/reducers/widgetSlice";
|
||||
import EmailBodyEditor from "../../components/EmailBodyEditor";
|
||||
import EmailEditor from "../../components/emaileditor";
|
||||
|
||||
const isSignExist = (placeholders = []) => {
|
||||
const isSignature =
|
||||
@@ -76,7 +70,11 @@ const TemplatesReport = (props) => {
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||
const [isResendMail, setIsResendMail] = useState({});
|
||||
const [mail, setMail] = useState({ subject: "", body: "" });
|
||||
const [mail, setMail] = useState({
|
||||
subject: "",
|
||||
body: { basic: "", advanced: "" }
|
||||
});
|
||||
const [emailEditorType, setEmailEditorType] = useState("basic");
|
||||
const [userDetails, setUserDetails] = useState({});
|
||||
const [isNextStep, setIsNextStep] = useState({});
|
||||
const [isBulkSend, setIsBulkSend] = useState({});
|
||||
@@ -103,7 +101,10 @@ const TemplatesReport = (props) => {
|
||||
const startIndex = (currentPage - 1) * props.docPerPage;
|
||||
const { isMoreDocs, setIsNextRecord } = props;
|
||||
const [isMailModal, setIsMailModal] = useState(false);
|
||||
const [customizeMail, setCustomizeMail] = useState({ body: "", subject: "" });
|
||||
const [customizeMail, setCustomizeMail] = useState({
|
||||
body: { basic: "", advanced: "" },
|
||||
subject: ""
|
||||
});
|
||||
const [defaultMail, setDefaultMail] = useState({ body: "", subject: "" });
|
||||
const [currUserId, setCurrUserId] = useState("");
|
||||
const [documentDetails, setDocumentDetails] = useState();
|
||||
@@ -554,6 +555,13 @@ const TemplatesReport = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSwitch` is used to change email editor from basic => advanced or vice versa
|
||||
const handleSwitch = (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const editor = emailEditorType === "basic" ? "advanced" : "basic";
|
||||
setEmailEditorType(editor);
|
||||
};
|
||||
// `handleSubjectChange` is used to add or change subject of resend mail
|
||||
const handleSubjectChange = (subject, doc) => {
|
||||
const encodeBase64 = userDetails?.objectId
|
||||
@@ -571,9 +579,9 @@ const TemplatesReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email,
|
||||
@@ -586,7 +594,7 @@ const TemplatesReport = (props) => {
|
||||
setMail((prev) => ({ ...prev, subject: res.subject }));
|
||||
};
|
||||
// `handlebodyChange` is used to add or change body of resend mail
|
||||
const handlebodyChange = (body, doc) => {
|
||||
const handlebodyChange = (body, doc, type) => {
|
||||
const encodeBase64 = userDetails?.objectId
|
||||
? btoa(`${doc.objectId}/${userDetails.Email}/${userDetails.objectId}`)
|
||||
: btoa(`${doc.objectId}/${userDetails.Email}`);
|
||||
@@ -602,9 +610,9 @@ const TemplatesReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: userDetails?.Name || "",
|
||||
receiver_email: userDetails?.Email || "",
|
||||
@@ -616,7 +624,10 @@ const TemplatesReport = (props) => {
|
||||
const res = replaceMailVaribles("", body, variables);
|
||||
|
||||
if (body) {
|
||||
setMail((prev) => ({ ...prev, body: res.body }));
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
body: { ...prev.body, [type]: res.body }
|
||||
}));
|
||||
}
|
||||
};
|
||||
// `handleNextBtn` is used to open edit mail template screen in resend mail modal
|
||||
@@ -646,9 +657,9 @@ const TemplatesReport = (props) => {
|
||||
document_title: doc.Name,
|
||||
note: doc?.Note || "",
|
||||
sender_name:
|
||||
doc?.SenderName ||
|
||||
doc.ExtUserPtr.Name,
|
||||
sender_mail:
|
||||
doc.ExtUserPtr.Email,
|
||||
sender_mail: doc?.SenderMail || doc.ExtUserPtr.Email,
|
||||
sender_phone: doc.ExtUserPtr?.Phone || "",
|
||||
receiver_name: user?.signerPtr?.Name || "",
|
||||
receiver_email: user?.email ? user?.email : user?.signerPtr?.Email,
|
||||
@@ -666,7 +677,17 @@ const TemplatesReport = (props) => {
|
||||
doc?.ExtUserPtr?.TenantId?.RequestBody ||
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p><a href='{{signing_url}}' rel='noopener noreferrer' target='_blank'>Sign here</a></p><br><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team ${appName}</p><br></body> </html>`;
|
||||
const res = replaceMailVaribles(subject, body, variables);
|
||||
setMail((prev) => ({ ...prev, subject: res.subject, body: res.body }));
|
||||
setEmailEditorType(
|
||||
doc?.EmailEditorType?.request ||
|
||||
doc?.ExtUserPtr?.EmailEditorType?.request ||
|
||||
doc?.ExtUserPtr?.TenantId?.EmailEditorType?.request ||
|
||||
"basic"
|
||||
);
|
||||
setMail((prev) => ({
|
||||
...prev,
|
||||
subject: res.subject,
|
||||
body: { basic: res.body, advanced: res.body }
|
||||
}));
|
||||
setIsNextStep({ [user.Id]: true });
|
||||
});
|
||||
const handleResendMail = utils.withSessionValidation(async (e, doc, user) => {
|
||||
@@ -686,8 +707,9 @@ const TemplatesReport = (props) => {
|
||||
recipient: userDetails?.Email,
|
||||
subject: mail.subject,
|
||||
from:
|
||||
doc?.SenderName ||
|
||||
doc?.ExtUserPtr?.Email,
|
||||
html: mail.body
|
||||
html: emailEditorType === "basic" ? mail.body.basic : mail.body.advanced
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(url, params, { headers: headers });
|
||||
@@ -958,9 +980,13 @@ const TemplatesReport = (props) => {
|
||||
subject;
|
||||
const userBody =
|
||||
body;
|
||||
const finalBody = userBody || defaultMailBody;
|
||||
const emailEditorType =
|
||||
tenantDetails?.EmailEditorType?.request;
|
||||
setEmailEditorType(emailEditorType || "basic");
|
||||
setCustomizeMail({
|
||||
subject: userSubject || defaultMailSubject,
|
||||
body: userBody || defaultMailBody
|
||||
body: { basic: finalBody, advanced: finalBody }
|
||||
});
|
||||
setDefaultMail({ subject: userSubject, body: userBody });
|
||||
}
|
||||
@@ -1493,27 +1519,29 @@ const TemplatesReport = (props) => {
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
className="text-xs ml-1"
|
||||
className="flex justify-between text-sm ml-1"
|
||||
htmlFor="mailbody"
|
||||
>
|
||||
{t("body")}{" "}
|
||||
<span>{t("body")} </span>
|
||||
<button
|
||||
className="op-link op-link-primary"
|
||||
onClick={(e) => handleSwitch(e)}
|
||||
>
|
||||
Switch to{" "}
|
||||
{emailEditorType === "basic"
|
||||
? t("switch-to-advanced")
|
||||
: t("switch-to-basic")}
|
||||
</button>
|
||||
</label>
|
||||
{/* <EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
id="mailbody"
|
||||
theme="snow"
|
||||
value={mail.body || ""}
|
||||
placeholder="add body of email "
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={(value) =>
|
||||
handlebodyChange(value, item)
|
||||
}
|
||||
/> */}
|
||||
<EmailBodyEditor
|
||||
value={mail.body || ""}
|
||||
onChange={(value) =>
|
||||
handlebodyChange(value, item)
|
||||
<EmailEditor
|
||||
type={emailEditorType}
|
||||
values={mail.body || ""}
|
||||
onChange={(value, type) =>
|
||||
handlebodyChange(
|
||||
value,
|
||||
item,
|
||||
type
|
||||
)
|
||||
}
|
||||
smallscreen
|
||||
/>
|
||||
@@ -1654,6 +1682,8 @@ const TemplatesReport = (props) => {
|
||||
handleShareList={handleShareList}
|
||||
setDocumentDetails={setDocumentDetails}
|
||||
copyUrlRef={copyUrlRef}
|
||||
emailEditorType={emailEditorType}
|
||||
setEmailEditorType={setEmailEditorType}
|
||||
/>
|
||||
<ModalUi
|
||||
isOpen={isSend}
|
||||
@@ -1747,21 +1777,16 @@ const TemplatesReport = (props) => {
|
||||
{mailStatus !== "success" &&
|
||||
currUserId &&
|
||||
templateDetails?.SendinOrder && (
|
||||
<>
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-regular fa-envelope"
|
||||
style={{ color: "#002864", fontSize: "19px" }}
|
||||
></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
</>
|
||||
<div
|
||||
className="op-btn op-btn-outline w-[50%] md:w-[35%] mt-1 group"
|
||||
onClick={() => {
|
||||
setIsSend(false);
|
||||
setIsMailModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-regular fa-envelope text-[19px] op-text-primary group-hover:text-base-100 "></i>{" "}
|
||||
<span>{t("send-to-email")}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{!mailStatus && (
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
cellsWidget
|
||||
cellsWidget,
|
||||
addWidgetOptions
|
||||
} from "../constant/Utils";
|
||||
import { base64StringtoFile, uploadFile } from "./fileUtils";
|
||||
import { sanitizeFileName } from "./sanitizeFileName";
|
||||
@@ -106,6 +107,43 @@ export const hasDuplicateWidgetNames = (placeholders = []) => {
|
||||
return false;
|
||||
};
|
||||
|
||||
// `createCustomPositionWidget` builds a dropObj and placeHolder for a widget placed
|
||||
// via click-and-drag drawing (i.e. when customOptions.customPosition is provided).
|
||||
// Returns { dropObj, placeHolder } where placeHolder.pos === [dropObj].
|
||||
export const createCustomPositionWidget = ({
|
||||
customPosition,
|
||||
key,
|
||||
containerScale,
|
||||
posZIndex,
|
||||
dragTypeValue,
|
||||
pageNumber,
|
||||
owner,
|
||||
signerPlaceHolder,
|
||||
roleName
|
||||
}) => {
|
||||
const { xPosition, yPosition, width, height } = customPosition;
|
||||
const dropObj = {
|
||||
xPosition,
|
||||
yPosition,
|
||||
isStamp: false,
|
||||
key,
|
||||
scale: containerScale,
|
||||
zIndex: posZIndex,
|
||||
type: dragTypeValue,
|
||||
options: addWidgetOptions(
|
||||
dragTypeValue,
|
||||
owner,
|
||||
signerPlaceHolder,
|
||||
roleName
|
||||
),
|
||||
Width: width,
|
||||
Height: height
|
||||
};
|
||||
const dropData = [dropObj];
|
||||
const placeHolder = { pageNumber, pos: dropData };
|
||||
return { dropObj, placeHolder };
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Array<object>} pages
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { appName, cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
import { appName } from '../../Utils.js';
|
||||
import sendMailWithAttachment from './sendMailWithAttachment.js';
|
||||
|
||||
export default async function forwardDoc(request) {
|
||||
try {
|
||||
@@ -28,9 +28,9 @@ export default async function forwardDoc(request) {
|
||||
const docName = _docRes.Name;
|
||||
const extUserId = _docRes?.ExtUserPtr?.objectId;
|
||||
const TenantAppName = appName;
|
||||
const from = _docRes?.ExtUserPtr?.Email;
|
||||
const replyTo = _docRes?.ExtUserPtr?.Email;
|
||||
const senderName = _docRes?.ExtUserPtr?.Name;
|
||||
const from = _docRes?.SenderName || _docRes?.ExtUserPtr?.Email;
|
||||
const replyTo = _docRes?.SenderMail || _docRes?.ExtUserPtr?.Email;
|
||||
const senderName = _docRes?.SenderName || _docRes?.ExtUserPtr?.Name;
|
||||
|
||||
try {
|
||||
let mailRes;
|
||||
@@ -53,15 +53,10 @@ export default async function forwardDoc(request) {
|
||||
`<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 ${TenantAppName}. For any queries regarding this email, please contact the sender ${replyTo} directly.</p></div></div></body></html>`,
|
||||
};
|
||||
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': serverAppId,
|
||||
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||
},
|
||||
});
|
||||
mailRes = await sendMailWithAttachment(params);
|
||||
// console.log('mailRes', mailRes);
|
||||
}
|
||||
return mailRes.data?.result;
|
||||
return mailRes;
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.error ||
|
||||
|
||||
@@ -110,8 +110,12 @@ async function sendMail(document, publicUrl) {
|
||||
year: 'numeric',
|
||||
});
|
||||
let signerMail = document.Placeholders?.filter(x => x?.Role !== 'prefill');
|
||||
const senderName = document.ExtUserPtr.Name;
|
||||
const senderEmail = document.ExtUserPtr.Email;
|
||||
const senderName = document?.SenderName || document.ExtUserPtr.Name;
|
||||
const senderEmail = document?.SenderMail || document.ExtUserPtr.Email;
|
||||
const from =
|
||||
document?.SenderName || document?.ExtUserPtr?.UseNameAsSender === true
|
||||
? document.ExtUserPtr.Name
|
||||
: senderEmail;
|
||||
|
||||
if (document.SendinOrder) {
|
||||
signerMail = signerMail.slice();
|
||||
@@ -171,7 +175,7 @@ async function sendMail(document, publicUrl) {
|
||||
extUserId: document.ExtUserPtr.objectId,
|
||||
recipient: existSigner?.Email || signerMail[i].email,
|
||||
subject: replaceVar?.subject ? replaceVar?.subject : mailTemplate(mailparam).subject,
|
||||
from: document.ExtUserPtr.Email,
|
||||
from: from,
|
||||
replyto: senderEmail || '',
|
||||
html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body,
|
||||
};
|
||||
@@ -215,6 +219,7 @@ async function startBulkSendInBackground(userId, Documents, Ip, parseConfig, typ
|
||||
}
|
||||
let mailBody = x?.ExtUserPtr?.TenantId?.RequestBody || '';
|
||||
let mailSubject = x?.ExtUserPtr?.TenantId?.RequestSubject || '';
|
||||
let EmailEditorType = x?.ExtUserPtr?.TenantId?.EmailEditorType || '';
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/app/classes/contracts_Document',
|
||||
@@ -261,12 +266,16 @@ async function startBulkSendInBackground(userId, Documents, Ip, parseConfig, typ
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
IsTourEnabled: x?.IsTourEnabled || false,
|
||||
AllowModifications: x?.AllowModifications || false,
|
||||
...(x?.SenderName ? { SenderName: x?.SenderName } : {}),
|
||||
...(x?.SenderMail ? { SenderMail: x?.SenderMail } : {}),
|
||||
...(type === 'bulksend' ? { BulkSendToken: generateId(10) } : {}),
|
||||
...(x?.SignatureType ? { SignatureType: x?.SignatureType } : {}),
|
||||
...(x?.NotifyOnSignatures ? { NotifyOnSignatures: x?.NotifyOnSignatures } : {}),
|
||||
...(x?.Bcc?.length > 0 ? { Bcc: x?.Bcc } : {}),
|
||||
...(x?.RedirectUrl ? { RedirectUrl: x?.RedirectUrl } : {}),
|
||||
...(mailBody ? { RequestBody: mailBody } : {}),
|
||||
...(mailSubject ? { RequestSubject: mailSubject } : {}),
|
||||
...(EmailEditorType ? { EmailEditorType: EmailEditorType } : {}),
|
||||
...(x?.objectId
|
||||
? {
|
||||
TemplateId: {
|
||||
@@ -294,6 +303,7 @@ async function startBulkSendInBackground(userId, Documents, Ip, parseConfig, typ
|
||||
createdAt: response.data[0]?.success?.createdAt,
|
||||
};
|
||||
deductcount(response.data.length, resExt.id);
|
||||
console.log('here');
|
||||
sendMail(updateDocuments, publicUrl); //sessionToken
|
||||
return { total: 1, created: 1, failed: 0 };
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export default async function getReport(request) {
|
||||
const limit = request.params.limit;
|
||||
const skip = request.params.skip;
|
||||
const searchTerm = request.params.searchTerm || '';
|
||||
const signerStatus = request.params.signerStatus || '';
|
||||
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = serverAppId;
|
||||
@@ -67,13 +68,23 @@ export default async function getReport(request) {
|
||||
}
|
||||
}
|
||||
paramsObj = applySearch({ reportId, baseWhere: paramsObj, searchTerm });
|
||||
|
||||
const clsName = json?.reportClass ? json.reportClass : 'contracts_Document';
|
||||
|
||||
if (clsName === 'contracts_Document' && signerStatus) {
|
||||
const normalizedStatus =
|
||||
signerStatus === 'viewed' ? 'Viewed' : signerStatus === 'signed' ? 'Signed' : '';
|
||||
if (normalizedStatus) {
|
||||
paramsObj = { ...paramsObj, 'AuditTrail.Activity': normalizedStatus };
|
||||
}
|
||||
}
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Master-Key': masterKey,
|
||||
};
|
||||
|
||||
const clsName = json?.reportClass ? json.reportClass : 'contracts_Document';
|
||||
const orderBy = '-updatedAt';
|
||||
const include = 'AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId';
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Placeholder } from './Placeholder.js';
|
||||
import { SignPdf } from '@signpdf/signpdf';
|
||||
import { P12Signer } from '@signpdf/signer-p12';
|
||||
import { buildDownloadFilename, parseUploadFile } from '../../../utils/fileUtils.js';
|
||||
import sendMailWithAttachment from '../sendMailWithAttachment.js';
|
||||
|
||||
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
|
||||
const APPID = serverAppId;
|
||||
@@ -224,7 +225,7 @@ async function sendCompletedMail(obj) {
|
||||
const variables = {
|
||||
document_title: pdfName,
|
||||
note: doc?.Note,
|
||||
sender_name: sender.Name,
|
||||
sender_name: doc?.SenderName || sender.Name,
|
||||
sender_mail: doc?.SenderMail || sender.Email,
|
||||
sender_phone: sender?.Phone || '',
|
||||
receiver_name: sender.Name,
|
||||
@@ -249,8 +250,8 @@ async function sendCompletedMail(obj) {
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
url: url,
|
||||
from: TenantAppName,
|
||||
replyto: doc?.ExtUserPtr?.Email || '',
|
||||
from: doc?.SenderName || TenantAppName,
|
||||
replyto: doc?.SenderMail || doc?.ExtUserPtr?.Email || '',
|
||||
recipient: recipient,
|
||||
subject: subject,
|
||||
pdfName: pdfName,
|
||||
@@ -261,9 +262,9 @@ async function sendCompletedMail(obj) {
|
||||
filename: docName,
|
||||
};
|
||||
try {
|
||||
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, { headers });
|
||||
// console.log('res', res.data.result);
|
||||
if (res.data?.result?.status !== 'success') {
|
||||
const res = await sendMailWithAttachment(params);
|
||||
// console.log("res ", res)
|
||||
if (res?.status !== 'success') {
|
||||
unlinkFile(`./exports/signed_certificate_${doc.objectId}.pdf`);
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -18,6 +18,8 @@ export default function reportJson(id, currentUserId) {
|
||||
'Placeholders',
|
||||
'TemplateId',
|
||||
'ExpiryDate',
|
||||
'SenderName',
|
||||
'SenderMail',
|
||||
];
|
||||
const inProgressKeys = [
|
||||
...commanKeys,
|
||||
@@ -25,8 +27,10 @@ export default function reportJson(id, currentUserId) {
|
||||
'SendMail',
|
||||
'RequestBody',
|
||||
'RequestSubject',
|
||||
'EmailEditorType',
|
||||
'ExtUserPtr.TenantId.RequestBody',
|
||||
'ExtUserPtr.TenantId.RequestSubject',
|
||||
'ExtUserPtr.TenantId.EmailEditorType',
|
||||
'DocSentAt',
|
||||
];
|
||||
const filterKeys = [
|
||||
|
||||
@@ -29,11 +29,11 @@ export default async function saveAsTemplate(request) {
|
||||
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('RequestBody', _docRes?.RequestBody);
|
||||
templateCls.set('RequestSubject', _docRes?.RequestSubject);
|
||||
if (_docRes?.EmailEditorType) templateCls.set('EmailEditorType', _docRes?.EmailEditorType);
|
||||
templateCls.set('NextReminderDate', _docRes?.NextReminderDate);
|
||||
templateCls.set('RedirectUrl', _docRes?.RedirectUrl);
|
||||
templateCls.set(
|
||||
|
||||
@@ -24,6 +24,19 @@ const refreshAccessToken = async refreshToken => {
|
||||
return response.data.access_token;
|
||||
};
|
||||
|
||||
const getGmail = async (access_token, displayName) => {
|
||||
try {
|
||||
const res = await axios.get('https://www.googleapis.com/oauth2/v2/userinfo', {
|
||||
headers: { Authorization: `Bearer ${access_token}` },
|
||||
});
|
||||
|
||||
const gmail = res.data.email;
|
||||
return `${displayName} <${gmail}>`;
|
||||
} catch (err) {
|
||||
console.log('gmail retrive error ', err);
|
||||
return displayName;
|
||||
}
|
||||
};
|
||||
// Function to create a raw email message
|
||||
const makeEmail = async (
|
||||
to,
|
||||
@@ -101,7 +114,7 @@ const makeEmail = async (
|
||||
}
|
||||
} catch (err) {
|
||||
attachments = [file];
|
||||
console.log('Err in read certificate sendmailv3', err);
|
||||
console.log('sendMailGmailProvider read certificate error', err);
|
||||
}
|
||||
}
|
||||
const attachmentParts = attachments.map(attachment => {
|
||||
@@ -166,7 +179,8 @@ export default async function sendMailGmailProvider(_extRes, template) {
|
||||
|
||||
try {
|
||||
// Construct email message
|
||||
const from = sender || _extRes.Email || 'me';
|
||||
const displayName = sender || _extRes.Email || 'me';
|
||||
const from = await getGmail(access_token, displayName);
|
||||
const to = receiver;
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const testPdf = `test_${randomNumber}.pdf`;
|
||||
@@ -203,7 +217,7 @@ export default async function sendMailGmailProvider(_extRes, template) {
|
||||
try {
|
||||
fs.unlinkSync(testPdf);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink pdf sendmailv3');
|
||||
console.log('sendMailGmailProvider unlink pdf error');
|
||||
}
|
||||
}
|
||||
return { code: 200, message: 'Email sent successfully' };
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
import fs from 'node:fs';
|
||||
import https from 'https';
|
||||
import formData from 'form-data';
|
||||
import Mailgun from 'mailgun.js';
|
||||
import { appName, smtpenable, smtpsecure, updateMailCount } from '../../Utils.js';
|
||||
import { createTransport } from 'nodemailer';
|
||||
import axios from 'axios';
|
||||
|
||||
function safeUnlink(filePath, label = 'file') {
|
||||
if (fs.existsSync(filePath)) {
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (err) {
|
||||
console.log(`sendMailWithAttachment unlink ${label} error`);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function sendMailProvider(params) {
|
||||
const app = appName;
|
||||
const extUserId = params?.extUserId || '';
|
||||
const reportMsg = `<p style="font-size: 13px; color:grey; text-align: center;">If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href="mailto:complaints@opensignlabs.com?subject=Spam%20report%20for%20user%20ID%20${extUserId}&body=Hello%20Support%20Team%2C%0D%0A%0D%0AI%E2%80%99m%20reporting%20spam%20activity%20coming%20from%20a%20sender%20using%20your%20platform.%0D%0A%0D%0AThe%20messages%20I%20received%20appear%20unsolicited%20and%20suspicious.%20The%20user%20ID%20associated%20with%20the%20emails%20is%3A%20${extUserId}.%20Please%20investigate%20this%20account%20and%20take%20appropriate%20action%20to%20prevent%20further%20abuse.%0D%0A%0D%0AIf%20you%20need%20additional%20details%2C%20I%E2%80%99m%20happy%20to%20provide%20the%20original%20email%20headers%20or%20screenshots.%0D%0A%0D%0AThank%20you%20for%20looking%20into%20this.%0D%0A%0D%0ABest%20regards%2C%0D%0A%5BYour%20Name%5D">here</a>.</p>`;
|
||||
|
||||
const mailgunApiKey = process.env.MAILGUN_API_KEY;
|
||||
try {
|
||||
let transporterSMTP;
|
||||
let mailgunClient;
|
||||
let mailgunDomain;
|
||||
if (smtpenable) {
|
||||
let transporterConfig = {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: process.env.SMTP_PORT || 465,
|
||||
secure: smtpsecure,
|
||||
};
|
||||
|
||||
// ✅ Add auth only if BOTH username & password exist
|
||||
const smtpUser = process.env.SMTP_USERNAME;
|
||||
const smtpPass = process.env.SMTP_PASS;
|
||||
|
||||
if (smtpUser && smtpPass) {
|
||||
transporterConfig.auth = {
|
||||
user: process.env.SMTP_USERNAME ? process.env.SMTP_USERNAME : process.env.SMTP_USER_EMAIL,
|
||||
pass: smtpPass,
|
||||
};
|
||||
}
|
||||
transporterSMTP = createTransport(transporterConfig);
|
||||
} else {
|
||||
if (mailgunApiKey) {
|
||||
const mailgun = new Mailgun(formData);
|
||||
mailgunClient = mailgun.client({ username: 'api', key: mailgunApiKey });
|
||||
mailgunDomain = process.env.MAILGUN_DOMAIN;
|
||||
}
|
||||
}
|
||||
if (params.url) {
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const testPdf = `test_${randomNumber}.pdf`;
|
||||
try {
|
||||
let Pdf = fs.createWriteStream(testPdf);
|
||||
const writeToLocalDisk = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isSecure =
|
||||
new URL(params.url)?.protocol === 'https:' &&
|
||||
new URL(params.url)?.hostname !== 'localhost';
|
||||
if (isSecure) {
|
||||
https
|
||||
.get(params.url, async function (response) {
|
||||
response.pipe(Pdf);
|
||||
response.on('end', () => resolve('success'));
|
||||
})
|
||||
.on('error', e => {
|
||||
console.error(`error: ${e.message}`);
|
||||
resolve('error');
|
||||
});
|
||||
} else {
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false }); // Disable SSL validation
|
||||
const localUrl = params.url;
|
||||
const newlocalUrl = localUrl.replace(
|
||||
'https://localhost:3001/api',
|
||||
'http://localhost:8080'
|
||||
);
|
||||
axios
|
||||
.get(newlocalUrl, { responseType: 'stream', httpsAgent: httpsAgent })
|
||||
.then(response => {
|
||||
response.data.pipe(Pdf);
|
||||
Pdf.on('finish', () => resolve('success'));
|
||||
Pdf.on('error', () => resolve('error'));
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('error in localurl', e.message);
|
||||
resolve('error');
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
// `writeToLocalDisk` is used to create pdf file from doc url
|
||||
const ress = await writeToLocalDisk();
|
||||
if (ress) {
|
||||
function readTolocal() {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
let PdfBuffer = fs.readFileSync(Pdf.path);
|
||||
resolve(PdfBuffer);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
// `PdfBuffer` used to create buffer from pdf file
|
||||
let PdfBuffer = await readTolocal();
|
||||
const pdfName = params.pdfName && `${params.pdfName}.pdf`;
|
||||
const filename = params.filename;
|
||||
const file = {
|
||||
filename: filename || pdfName || 'exported.pdf',
|
||||
content: smtpenable ? PdfBuffer : undefined,
|
||||
data: smtpenable ? undefined : PdfBuffer,
|
||||
};
|
||||
|
||||
let attachment;
|
||||
const certificatePath = params.certificatePath || `./exports/certificate.pdf`;
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
// `certificateBuffer` used to create buffer from pdf file
|
||||
const certificateBuffer = fs.readFileSync(certificatePath);
|
||||
const certificate = {
|
||||
filename: 'certificate.pdf',
|
||||
content: smtpenable ? certificateBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'),
|
||||
data: smtpenable ? undefined : certificateBuffer,
|
||||
};
|
||||
attachment = [file, certificate];
|
||||
} catch (err) {
|
||||
attachment = [file];
|
||||
console.log('sendMailWithAttachment read certificate error', err);
|
||||
}
|
||||
} else {
|
||||
attachment = [file];
|
||||
}
|
||||
const from = params.from || '';
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
const replyto = params?.replyto || '';
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: params.recipient,
|
||||
subject: params.subject,
|
||||
text: params.text || 'mail',
|
||||
html: params?.html ? params.html + reportMsg : '',
|
||||
attachments: smtpenable ? attachment : undefined,
|
||||
attachment: smtpenable ? undefined : attachment,
|
||||
bcc: params.bcc ? params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
const cleanupPaths = [
|
||||
{ path: certificatePath, label: 'certificate' },
|
||||
{ path: testPdf, label: 'pdf' },
|
||||
];
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
|
||||
cleanupPaths.forEach(file => safeUnlink(file.path, file.label));
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
if (mailgunApiKey) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
cleanupPaths.forEach(file => safeUnlink(file.path, file.label));
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
cleanupPaths.forEach(file => safeUnlink(file.path, file.label));
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`sendMailWithAttachment error: ${err}`);
|
||||
safeUnlink(testPdf, 'testPdf');
|
||||
if (err) return { status: 'error' };
|
||||
}
|
||||
} else {
|
||||
const from = params.from || '';
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
const replyto = params?.replyto || '';
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: params.recipient,
|
||||
subject: params.subject,
|
||||
text: params.text || 'mail',
|
||||
html: params?.html ? params.html + reportMsg : '',
|
||||
bcc: params.bcc ? params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
if (mailgunApiKey) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`sendMailWithAttachment Error: ${err}`);
|
||||
if (err) {
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `sendMailWithAttachment` function is used to send completion and forwarded document mail and it also fix security issue.
|
||||
export default async function sendMailWithAttachment(params) {
|
||||
const nonCustomMail = await sendMailProvider(params);
|
||||
return nonCustomMail;
|
||||
}
|
||||
@@ -40,220 +40,45 @@ async function sendMailProvider(req) {
|
||||
mailgunDomain = process.env.MAILGUN_DOMAIN;
|
||||
}
|
||||
}
|
||||
if (req.params.url) {
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const testPdf = `test_${randomNumber}.pdf`;
|
||||
try {
|
||||
let Pdf = fs.createWriteStream(testPdf);
|
||||
const writeToLocalDisk = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isSecure =
|
||||
new URL(req.params.url)?.protocol === 'https:' &&
|
||||
new URL(req.params.url)?.hostname !== 'localhost';
|
||||
if (isSecure) {
|
||||
https
|
||||
.get(req.params.url, async function (response) {
|
||||
response.pipe(Pdf);
|
||||
response.on('end', () => resolve('success'));
|
||||
})
|
||||
.on('error', e => {
|
||||
console.error(`error: ${e.message}`);
|
||||
resolve('error');
|
||||
});
|
||||
} else {
|
||||
const httpsAgent = new https.Agent({ rejectUnauthorized: false }); // Disable SSL validation
|
||||
const localUrl = req.params.url;
|
||||
const newlocalUrl = localUrl.replace(
|
||||
'https://localhost:3001/api',
|
||||
'http://localhost:8080'
|
||||
);
|
||||
axios
|
||||
.get(newlocalUrl, { responseType: 'stream', httpsAgent: httpsAgent })
|
||||
.then(response => {
|
||||
response.data.pipe(Pdf);
|
||||
Pdf.on('finish', () => resolve('success'));
|
||||
Pdf.on('error', () => resolve('error'));
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('error in localurl', e.message);
|
||||
resolve('error');
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
// `writeToLocalDisk` is used to create pdf file from doc url
|
||||
const ress = await writeToLocalDisk();
|
||||
if (ress) {
|
||||
function readTolocal() {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
let PdfBuffer = fs.readFileSync(Pdf.path);
|
||||
resolve(PdfBuffer);
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
// `PdfBuffer` used to create buffer from pdf file
|
||||
let PdfBuffer = await readTolocal();
|
||||
const pdfName = req.params.pdfName && `${req.params.pdfName}.pdf`;
|
||||
const filename = req.params.filename;
|
||||
const file = {
|
||||
filename: filename || pdfName || 'exported.pdf',
|
||||
content: smtpenable ? PdfBuffer : undefined,
|
||||
data: smtpenable ? undefined : PdfBuffer,
|
||||
};
|
||||
|
||||
let attachment;
|
||||
const certificatePath = req.params.certificatePath || `./exports/certificate.pdf`;
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
// `certificateBuffer` used to create buffer from pdf file
|
||||
const certificateBuffer = fs.readFileSync(certificatePath);
|
||||
const certificate = {
|
||||
filename: 'certificate.pdf',
|
||||
content: smtpenable ? certificateBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'),
|
||||
data: smtpenable ? undefined : certificateBuffer,
|
||||
};
|
||||
attachment = [file, certificate];
|
||||
} catch (err) {
|
||||
attachment = [file];
|
||||
console.log('Err in read certificate sendmailv3', err);
|
||||
}
|
||||
} else {
|
||||
attachment = [file];
|
||||
}
|
||||
const from = req.params.from || '';
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
const replyto = req.params?.replyto || '';
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params?.html ? req.params.html + reportMsg : '',
|
||||
attachments: smtpenable ? attachment : undefined,
|
||||
attachment: smtpenable ? undefined : attachment,
|
||||
bcc: req.params.bcc ? req.params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
fs.unlinkSync(certificatePath);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(testPdf)) {
|
||||
try {
|
||||
fs.unlinkSync(testPdf);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink pdf sendmailv3');
|
||||
}
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
if (mailgunApiKey) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
fs.unlinkSync(certificatePath);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(testPdf)) {
|
||||
try {
|
||||
fs.unlinkSync(testPdf);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink pdf sendmailv3');
|
||||
}
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
fs.unlinkSync(certificatePath);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(testPdf)) {
|
||||
try {
|
||||
fs.unlinkSync(testPdf);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink pdf sendmailv3');
|
||||
}
|
||||
}
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Error in sendmailv3: ${err}`);
|
||||
if (fs.existsSync(testPdf)) {
|
||||
try {
|
||||
fs.unlinkSync(testPdf);
|
||||
} catch (err) {
|
||||
console.log('Err in unlink pdf sendmailv3');
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
return { status: 'error' };
|
||||
const from = req.params.from || '';
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
const replyto = req.params?.replyto || '';
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params?.html ? req.params.html + reportMsg : '',
|
||||
bcc: req.params.bcc ? req.params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
const from = req.params.from || '';
|
||||
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
|
||||
const replyto = req.params?.replyto || '';
|
||||
const messageParams = {
|
||||
from: from + ' <' + mailsender + '>',
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params?.html ? req.params.html + reportMsg : '',
|
||||
bcc: req.params.bcc ? req.params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (mailgunApiKey) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
if (mailgunApiKey) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error' };
|
||||
}
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(`Error in sendmailv3: ${err}`);
|
||||
console.log(`sendmailv3 Error: ${err}`);
|
||||
if (err) {
|
||||
return { status: 'error' };
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ export default async function updateEmailTemplates(request) {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (details?.EmailEditorType) extUserRes.set('EmailEditorType', details.EmailEditorType);
|
||||
|
||||
const updateExtRes = await extUserRes.save(null, { useMasterKey: true });
|
||||
if (updateExtRes) {
|
||||
const res = JSON.parse(JSON.stringify(updateExtRes));
|
||||
|
||||
@@ -5,6 +5,7 @@ export default async function updatePreferences(request) {
|
||||
const SignatureType = request.params.SignatureType || [];
|
||||
const NotifyOnSignatures = request.params.NotifyOnSignatures;
|
||||
const Timezone = request.params.Timezone;
|
||||
const UseNameAsSender = request.params.UseNameAsSender;
|
||||
if (SignatureType?.length > 0 || NotifyOnSignatures !== undefined || Timezone) {
|
||||
try {
|
||||
const orgQuery = new Parse.Query('contracts_Users');
|
||||
@@ -60,6 +61,9 @@ export default async function updatePreferences(request) {
|
||||
if (request.params.DownloadFilenameFormat) {
|
||||
newOrg.set('DownloadFilenameFormat', request.params.DownloadFilenameFormat);
|
||||
}
|
||||
if (UseNameAsSender !== undefined) {
|
||||
newOrg.set('UseNameAsSender', UseNameAsSender);
|
||||
}
|
||||
const updateUserRes = await newOrg.save(null, { useMasterKey: true });
|
||||
if (updateUserRes) {
|
||||
const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes));
|
||||
|
||||
@@ -22,6 +22,7 @@ export default async function updateTenant(request) {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (details?.EmailEditorType) tenant.set('EmailEditorType', details.EmailEditorType);
|
||||
|
||||
const tenantRes = await tenant.save(null, { useMasterKey: true });
|
||||
if (tenantRes) {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.up = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.addObject('EmailEditorType');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.addObject('EmailEditorType');
|
||||
await templateSchema.update();
|
||||
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
tenantSchema.addObject('EmailEditorType');
|
||||
await tenantSchema.update();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.down = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.deleteField('EmailEditorType');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.deleteField('EmailEditorType');
|
||||
await templateSchema.update();
|
||||
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
tenantSchema.deleteField('EmailEditorType');
|
||||
await tenantSchema.update();
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.up = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.addString('SenderName');
|
||||
docSchema.addString('SenderMail');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.addString('SenderName');
|
||||
templateSchema.addString('SenderMail');
|
||||
await templateSchema.update();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Parse} Parse
|
||||
*/
|
||||
exports.down = async Parse => {
|
||||
const docSchema = new Parse.Schema('contracts_Document');
|
||||
docSchema.deleteField('SenderName');
|
||||
docSchema.deleteField('SenderMail');
|
||||
await docSchema.update();
|
||||
|
||||
const templateSchema = new Parse.Schema('contracts_Template');
|
||||
templateSchema.deleteField('SenderName');
|
||||
templateSchema.deleteField('SenderMail');
|
||||
await templateSchema.update();
|
||||
};
|
||||
@@ -158,7 +158,7 @@ export const config = {
|
||||
}
|
||||
: {}),
|
||||
filesAdapter: fsAdapter,
|
||||
auth: { google: { enabled: true }, sso: SSOAuth },
|
||||
auth: { google: { clientId: process.env.GOOGLE_CLIENT_ID }, sso: SSOAuth },
|
||||
// for fix Adapter prototype don't match expected prototype
|
||||
push: { queueOptions: { disablePushWorker: true } },
|
||||
};
|
||||
|
||||
Generated
+633
-654
File diff suppressed because it is too large
Load Diff
@@ -18,8 +18,8 @@
|
||||
"watch": "nodemon index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.997.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.997.0",
|
||||
"@aws-sdk/client-s3": "^3.1008.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.1008.0",
|
||||
"@parse/fs-files-adapter": "^3.0.0",
|
||||
"@parse/push-adapter": "^8.3.1",
|
||||
"@parse/s3-files-adapter": "5.0.0",
|
||||
@@ -27,7 +27,7 @@
|
||||
"@signpdf/placeholder-pdf-lib": "^3.3.0",
|
||||
"@signpdf/signer-p12": "^3.3.0",
|
||||
"@signpdf/signpdf": "^3.3.0",
|
||||
"axios": "^1.13.5",
|
||||
"axios": "^1.13.6",
|
||||
"coherentpdf": "^2.5.5",
|
||||
"cors": "^2.8.6",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
@@ -37,17 +37,17 @@
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^171.4.0",
|
||||
"libreoffice-convert": "^1.8.1",
|
||||
"mailgun.js": "^12.7.0",
|
||||
"mailgun.js": "^12.7.1",
|
||||
"moment": "^2.30.1",
|
||||
"mongodb": "^7.1.0",
|
||||
"multer": "^2.0.2",
|
||||
"multer": "^2.1.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.3",
|
||||
"nodemailer": "^8.0.1",
|
||||
"nodemailer": "^8.0.2",
|
||||
"p-limit": "^7.3.0",
|
||||
"parse": "^8.1.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^8.6.3",
|
||||
"parse-server": "^8.6.40",
|
||||
"parse-server-api-mail-adapter": "^5.0.5",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"posthog-node": "^5.21.2",
|
||||
@@ -60,7 +60,7 @@
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.28.6",
|
||||
"eslint": "^9.39.3",
|
||||
"eslint": "^9.39.4",
|
||||
"jasmine": "^6.1.0",
|
||||
"mongodb-runner": "^6.7.1",
|
||||
"nodemon": "^3.1.14",
|
||||
|
||||
Reference in New Issue
Block a user