mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-03 16:28:56 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8ecd34bd3 | ||
|
|
d4a4eb3248 | ||
|
|
f782733397 | ||
|
|
7720eddc47 | ||
|
|
71170a373a | ||
|
|
8f080080a7 | ||
|
|
71b2958c14 | ||
|
|
f69aafcbfa | ||
|
|
5f6a423697 | ||
|
|
5d245927c4 | ||
|
|
17c96fb849 | ||
|
|
b7c28cdf36 | ||
|
|
d05aa0d350 | ||
|
|
875dc086bf | ||
|
|
4719ebd52a | ||
|
|
c8a436a7df | ||
|
|
8d6f4dd64e | ||
|
|
63486927d6 | ||
|
|
c36a70f8d3 | ||
|
|
6cb30a6597 | ||
|
|
bc89b33468 | ||
|
|
ff2f7f6586 | ||
|
|
93f33d73d9 | ||
|
|
402c176277 |
Generated
+69
-42
@@ -77,7 +77,7 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"lint-staged": "^15.2.8",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"postcss": "^8.4.45",
|
||||
"prettier": "^3.3.3",
|
||||
@@ -7592,9 +7592,9 @@
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
@@ -7604,7 +7604,7 @@
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
@@ -11040,36 +11040,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.2",
|
||||
"body-parser": "1.20.3",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
@@ -11099,15 +11099,23 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/express/node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
|
||||
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
|
||||
},
|
||||
"node_modules/express/node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
@@ -11374,12 +11382,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
@@ -11398,6 +11406,14 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -16287,9 +16303,9 @@
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "15.2.9",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.9.tgz",
|
||||
"integrity": "sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ==",
|
||||
"version": "15.2.10",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz",
|
||||
"integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "~5.3.0",
|
||||
@@ -16298,7 +16314,7 @@
|
||||
"execa": "~8.0.1",
|
||||
"lilconfig": "~3.1.2",
|
||||
"listr2": "~8.2.4",
|
||||
"micromatch": "~4.0.7",
|
||||
"micromatch": "~4.0.8",
|
||||
"pidtree": "~0.6.0",
|
||||
"string-argv": "~0.3.2",
|
||||
"yaml": "~2.5.0"
|
||||
@@ -16908,9 +16924,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-refs": {
|
||||
"version": "1.3.0",
|
||||
@@ -19758,11 +19777,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@@ -22319,9 +22338,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -22504,19 +22523,27 @@
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static/node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/serve/node_modules/ajv": {
|
||||
"version": "8.12.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"lint-staged": "^15.2.8",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"postcss": "^8.4.45",
|
||||
"prettier": "^3.3.3",
|
||||
|
||||
@@ -635,7 +635,9 @@
|
||||
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
|
||||
"quota-mail-tip":"Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
|
||||
"quota-mail-head":"Quota Reached",
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.",
|
||||
"sent-this-month":"Sent this month"
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month":"Sent this month",
|
||||
"available-seats":"Available seats",
|
||||
"buy-users":"Buy more users"
|
||||
|
||||
}
|
||||
|
||||
@@ -634,7 +634,8 @@
|
||||
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
|
||||
"quota-mail-tip-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
|
||||
"quota-mail-head":"Quota atteint",
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.",
|
||||
"sent-this-month":"envoyé ce mois-ci"
|
||||
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month":"envoyé ce mois-ci",
|
||||
"available-seats":"Disponible sièges",
|
||||
"buy-users":"Acheter plus d'utilisateurs"
|
||||
}
|
||||
|
||||
@@ -301,6 +301,9 @@ const AddUser = (props) => {
|
||||
setAllowedUser(amount.quantity);
|
||||
setPlanInfo((obj) => ({ ...obj, totalAllowedUser: _resAddon.addon }));
|
||||
}
|
||||
if (props.handleBuyUsers) {
|
||||
props.handleBuyUsers(amount.quantity, _resAddon.addon);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
@@ -495,8 +498,7 @@ const AddUser = (props) => {
|
||||
{planInfo.adminId !== extUser?.objectId ? (
|
||||
<div className="mb-3 mt-1 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", {
|
||||
adminName: extUser?.CreatedBy?.name,
|
||||
adminEmail: extUser?.CreatedBy?.email
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -31,7 +31,6 @@ const BulkSendUi = (props) => {
|
||||
const [isFreePlan, setIsFreePlan] = useState(false);
|
||||
const [admin, setAdmin] = useState({
|
||||
objectId: "",
|
||||
name: "",
|
||||
email: "",
|
||||
isAdmin: true
|
||||
});
|
||||
@@ -63,8 +62,7 @@ const BulkSendUi = (props) => {
|
||||
setAdmin((obj) => ({
|
||||
...obj,
|
||||
isAdmin: false,
|
||||
name: extUser?.CreatedBy?.name,
|
||||
email: extUser?.CreatedBy?.email
|
||||
email: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
}
|
||||
setAdmin((obj) => ({ ...obj, objectId: subscription?.adminId }));
|
||||
@@ -491,10 +489,7 @@ const BulkSendUi = (props) => {
|
||||
</form>
|
||||
) : (
|
||||
<div className="mx-8 mt-4 mb-8 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", {
|
||||
adminName: admin?.name,
|
||||
adminEmail: admin?.email
|
||||
})}
|
||||
{t("unauthorized-modal", { adminEmail: admin?.email })}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -252,9 +252,9 @@ const HomeLayout = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
const handleLoginBtn = async () => {
|
||||
try {
|
||||
Parse?.User?.logOut();
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
|
||||
@@ -28,7 +28,6 @@ function GenerateToken() {
|
||||
plan: "",
|
||||
isValid: false,
|
||||
adminId: "",
|
||||
adminName: "",
|
||||
adminEmail: ""
|
||||
});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
@@ -154,8 +153,7 @@ function GenerateToken() {
|
||||
} else {
|
||||
setIsSubscribe((obj) => ({
|
||||
...obj,
|
||||
adminName: extUser?.CreatedBy?.name,
|
||||
adminEmail: extUser?.CreatedBy?.email
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
setIsModal((obj) => ({ ...obj, unauthorized: !obj.unauthorized }));
|
||||
}
|
||||
@@ -447,7 +445,6 @@ function GenerateToken() {
|
||||
>
|
||||
<div className="m-8 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", {
|
||||
adminName: isSubscribe?.adminName,
|
||||
adminEmail: isSubscribe?.adminEmail
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -121,9 +121,9 @@ const PlanSubscriptions = () => {
|
||||
};
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
Parse?.User?.logOut();
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
|
||||
@@ -9,7 +9,7 @@ import Tooltip from "../primitives/Tooltip";
|
||||
import AddUser from "../components/AddUser";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import { fetchSubscriptionInfo } from "../constant/Utils";
|
||||
import Title from "../components/Title";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -19,7 +19,11 @@ const UserList = () => {
|
||||
const { t } = useTranslation();
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isModal, setIsModal] = useState({
|
||||
form: false,
|
||||
addseats: false,
|
||||
options: false
|
||||
});
|
||||
const location = useLocation();
|
||||
const isDashboard =
|
||||
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
|
||||
@@ -27,9 +31,16 @@ const UserList = () => {
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
|
||||
const [isSubscribe, setIsSubscribe] = useState({
|
||||
plan: "",
|
||||
isValid: false,
|
||||
priceperUser: 0
|
||||
});
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [formHeader, setFormHeader] = useState(t("add-user"));
|
||||
const [usersCount, setUserCounts] = useState({ allowed: 0, totalAllowed: 0 });
|
||||
const [amount, setAmount] = useState({ quantity: 1, price: 0 });
|
||||
const [isBuyLoader, setIsBuyLoader] = useState(false);
|
||||
const recordperPage = 10;
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
|
||||
@@ -81,7 +92,6 @@ const UserList = () => {
|
||||
pages.push(lastPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
const pageNumbers = getPaginationRange();
|
||||
@@ -96,13 +106,43 @@ const UserList = () => {
|
||||
async function fetchUserList() {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (isEnableSubscription) {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
const subscribe = await fetchSubscriptionInfo();
|
||||
if (subscribe?.plan_code?.includes("team")) {
|
||||
const isSupAdmin =
|
||||
subscribe?.adminId && extUser?.objectId === subscribe?.adminId;
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: true,
|
||||
adminId: subscribe.adminId,
|
||||
priceperUser: subscribe.price,
|
||||
isSuperAdmin: isSupAdmin
|
||||
});
|
||||
setAmount((prev) => ({ ...prev, price: subscribe.price }));
|
||||
try {
|
||||
const res = await Parse.Cloud.run("allowedusers");
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(res),
|
||||
totalAllowed: parseInt(subscribe?.totalAllowedUser) || 0
|
||||
}));
|
||||
} catch (err) {
|
||||
console.log("err while get users", err);
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: false,
|
||||
adminId: subscribe.adminId
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe({ plan: "teams-yearly", isValid: true });
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
|
||||
if (extUser) {
|
||||
const admin =
|
||||
extUser?.UserRole &&
|
||||
@@ -121,14 +161,12 @@ const UserList = () => {
|
||||
console.log("Err in fetch userlist", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
const handleModal = (modalName) => {
|
||||
setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] }));
|
||||
};
|
||||
|
||||
// Change page
|
||||
@@ -146,7 +184,13 @@ const UserList = () => {
|
||||
};
|
||||
|
||||
const handleUserData = (userData) => {
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
if (userData) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: obj?.allowed > 0 ? obj.allowed - 1 : 1
|
||||
}));
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
}
|
||||
};
|
||||
// `formatRow` is used to show data in poper manner like
|
||||
// if data is of array type then it will join array items with ","
|
||||
@@ -162,9 +206,8 @@ const UserList = () => {
|
||||
return "-";
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsActiveModal({});
|
||||
};
|
||||
const handleClose = () => setIsActiveModal({});
|
||||
|
||||
const handleToggleSubmit = async (user) => {
|
||||
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
|
||||
if (index !== -1) {
|
||||
@@ -196,6 +239,49 @@ const UserList = () => {
|
||||
const handleToggleBtn = (user) => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
|
||||
const handleAddOnSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsBuyLoader(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buyaddonusers", {
|
||||
users: parseInt(amount.quantity)
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(amount.quantity),
|
||||
totalAllowed: parseInt(_resAddon.addon)
|
||||
}));
|
||||
setAmount((obj) => ({ ...obj, quantity: 1 }));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
setIsBuyLoader(false);
|
||||
handleModal("addseats");
|
||||
}
|
||||
};
|
||||
const handlePricePerUser = (e) => {
|
||||
const quantity = e.target.value;
|
||||
const price = e.target?.value > 0 ? isSubscribe.priceperUser * quantity : 0;
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleBuyUsers = (allowed, totalAllowed) => {
|
||||
if (allowed && totalAllowed) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(allowed),
|
||||
totalAllowed: parseInt(totalAllowed)
|
||||
}));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={isAdmin ? "Users" : "Page not found"} />
|
||||
@@ -205,7 +291,7 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
{Object.keys(isActLoader)?.length > 0 && (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
@@ -216,19 +302,46 @@ const UserList = () => {
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
{t("report-name.Users")}
|
||||
{t("report-name.Users")}{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={t("users-from-teams")} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleFormModal()}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="hidden md:flex op-btn op-btn-sm h-[35px] op-btn-primary op-btn-outline text-xs mb-0.5"
|
||||
onClick={() => handleModal("addseats")}
|
||||
>
|
||||
{t("buy-users")}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModal("form")}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="cursor-pointer relative md:hidden mb-0.5"
|
||||
onClick={() => handleModal("options")}
|
||||
>
|
||||
<i className="fa-light fa-ellipsis-vertical fa-xl"></i>
|
||||
{isModal?.options && (
|
||||
<ul className="absolute -right-3 top-auto z-[70] w-max op-menu op-menu-xs shadow bg-base-100 text-base-content rounded-box border">
|
||||
<li onClick={() => handleModal("addseats")}>
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t("buy-users")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={` overflow-x-auto w-full`}>
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
@@ -313,34 +426,42 @@ const UserList = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
<div className="flex flex-row justify-between items-center text-xs font-medium">
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div>
|
||||
{t("available-seats")}: {usersCount.allowed}/
|
||||
{usersCount.totalAllowed}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
@@ -362,17 +483,60 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
isOpen={isModal.form}
|
||||
title={formHeader}
|
||||
handleClose={handleFormModal}
|
||||
handleClose={() => handleModal("form")}
|
||||
>
|
||||
<AddUser
|
||||
setIsAlert={setIsAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={handleFormModal}
|
||||
handleBuyUsers={handleBuyUsers}
|
||||
closePopup={() => handleModal("form")}
|
||||
setFormHeader={setFormHeader}
|
||||
/>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isModal.addseats}
|
||||
title="Add Seats"
|
||||
handleClose={() => handleModal("addseats")}
|
||||
>
|
||||
{isBuyLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleAddOnSubmit} className=" px-3 pb-6 pt-6">
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label
|
||||
htmlFor="quantity"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Quantity-of-users")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value={amount.quantity}
|
||||
onChange={(e) => handlePricePerUser(e)}
|
||||
className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("Price")} (1 * {isSubscribe.priceperUser})
|
||||
</label>
|
||||
<div className="w-1/4 flex justify-center items-center text-sm">
|
||||
USD {amount.price}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-base-content mb-3" />
|
||||
<button className="op-btn op-btn-primary w-full">
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
|
||||
@@ -632,7 +632,9 @@
|
||||
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
|
||||
"quota-mail-tip":"Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
|
||||
"quota-mail-head":"Quota Reached",
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminName}}<{{adminEmail}}>.",
|
||||
"sent-this-month":"Sent this month"
|
||||
"unauthorized-modal":"You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month":"Sent this month",
|
||||
"available-seats":"Available seats",
|
||||
"buy-users":"Buy more users"
|
||||
|
||||
}
|
||||
|
||||
@@ -631,8 +631,9 @@
|
||||
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
|
||||
"quota-mail-tip":"Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
|
||||
"quota-mail-head":"Quota atteint",
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminName}}<{{adminEmail}}>.",
|
||||
"sent-this-month":"envoyé ce mois-ci"
|
||||
|
||||
"unauthorized-modal":"Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month":"envoyé ce mois-ci",
|
||||
"available-seats":"Disponible sièges",
|
||||
"buy-users":"Acheter plus d'utilisateurs"
|
||||
|
||||
}
|
||||
|
||||
@@ -42,36 +42,38 @@ export function replaceMailVaribles(subject, body, variables) {
|
||||
export const saveFileUsage = async (size, fileUrl, userId) => {
|
||||
//checking server url and save file's size
|
||||
try {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
if (userId) {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in fetch tenant Id', err);
|
||||
|
||||
@@ -12,6 +12,8 @@ async function getUserDetails(request) {
|
||||
}
|
||||
userQuery.include('TenantId');
|
||||
userQuery.include('UserId');
|
||||
userQuery.include('CreatedBy');
|
||||
userQuery.exclude('CreatedBy.authData');
|
||||
userQuery.exclude('google_refresh_token');
|
||||
if (userId) {
|
||||
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
|
||||
@@ -29,11 +29,7 @@ async function uploadFile(pdfName, filepath) {
|
||||
// `updateDoc` is used to update signedUrl, AuditTrail, Iscompleted in document
|
||||
async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
|
||||
try {
|
||||
const UserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: className,
|
||||
objectId: userId,
|
||||
};
|
||||
const UserPtr = { __type: 'Pointer', className: className, objectId: userId };
|
||||
const obj = {
|
||||
UserPtr: UserPtr,
|
||||
SignedUrl: url,
|
||||
@@ -66,11 +62,7 @@ async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
|
||||
} else {
|
||||
isCompleted = true;
|
||||
}
|
||||
const body = {
|
||||
SignedUrl: url,
|
||||
AuditTrail: updateAuditTrail,
|
||||
IsCompleted: isCompleted,
|
||||
};
|
||||
const body = { SignedUrl: url, AuditTrail: updateAuditTrail, IsCompleted: isCompleted };
|
||||
const signedRes = await axios.put(serverUrl + '/classes/contracts_Document/' + docId, body, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -164,29 +156,16 @@ async function sendCompletedMail(obj) {
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
// console.log('Res ', res);
|
||||
}
|
||||
|
||||
// `sendDoctoWebhook` is used to send res data of document on webhook
|
||||
async function sendDoctoWebhook(doc, Url, event, signUser, certificateUrl) {
|
||||
let signers = [];
|
||||
if (signUser) {
|
||||
signers = {
|
||||
name: signUser?.Name,
|
||||
email: signUser?.Email,
|
||||
phone: signUser?.Phone,
|
||||
};
|
||||
signers = { name: signUser?.Name, email: signUser?.Email, phone: signUser?.Phone };
|
||||
} else {
|
||||
signers = doc?.Signers?.map(x => ({
|
||||
name: x.Name,
|
||||
email: x.Email,
|
||||
phone: x.Phone,
|
||||
})) || [
|
||||
{
|
||||
name: doc?.ExtUserPtr?.Name,
|
||||
email: doc?.ExtUserPtr?.Email,
|
||||
phone: doc?.ExtUserPtr?.Phone,
|
||||
},
|
||||
signers = doc?.Signers?.map(x => ({ name: x.Name, email: x.Email, phone: x.Phone })) || [
|
||||
{ name: doc?.ExtUserPtr?.Name, email: doc?.ExtUserPtr?.Email, phone: doc?.ExtUserPtr?.Phone },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -213,7 +192,6 @@ async function sendDoctoWebhook(doc, Url, event, signUser, certificateUrl) {
|
||||
})
|
||||
.then(res => {
|
||||
try {
|
||||
// console.log('res ', res);
|
||||
const webhook = new Parse.Object('contracts_Webhook');
|
||||
webhook.set('Log', res?.status);
|
||||
webhook.set('UserId', {
|
||||
@@ -241,7 +219,6 @@ async function sendDoctoWebhook(doc, Url, event, signUser, certificateUrl) {
|
||||
console.log('err save in contracts_Webhook', err.message);
|
||||
}
|
||||
});
|
||||
// console.log('res ', res.data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,15 +258,9 @@ const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailPro
|
||||
if (doc.IsSendMail === false) {
|
||||
console.log("don't send mail");
|
||||
} else {
|
||||
const mailObj = {
|
||||
url: url,
|
||||
isCustomMail: isCustomMail,
|
||||
doc: doc,
|
||||
mailProvider: mailProvider,
|
||||
};
|
||||
const mailObj = { url: url, isCustomMail: isCustomMail, doc: doc, mailProvider: mailProvider };
|
||||
sendCompletedMail(mailObj);
|
||||
}
|
||||
|
||||
saveFileUsage(CertificateBuffer.length, file.imageUrl, userId);
|
||||
sendDoctoWebhook(doc, url, 'completed', '', file.imageUrl);
|
||||
};
|
||||
@@ -304,8 +275,7 @@ async function PDF(req) {
|
||||
if (!req?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
} else {
|
||||
const password = '';
|
||||
const authUser = req?.user?.toJSON();
|
||||
const userIP = req.headers['x-real-ip']; // client IPaddress
|
||||
const docId = req.params.docId;
|
||||
const reqUserId = req.params.userId;
|
||||
const isCustomMail = req.params.isCustomCompletionMail || false;
|
||||
@@ -320,7 +290,6 @@ async function PDF(req) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
|
||||
}
|
||||
const _resDoc = resDoc?.toJSON();
|
||||
|
||||
let signUser;
|
||||
let className;
|
||||
// `reqUserId` is send throught pdfrequest signing flow
|
||||
@@ -346,18 +315,8 @@ async function PDF(req) {
|
||||
// const P12Buffer = fs.readFileSync();
|
||||
const P12Buffer = Buffer.from(pfxFile, 'base64');
|
||||
const p12Cert = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
|
||||
|
||||
const UserPtr = {
|
||||
__type: 'Pointer',
|
||||
className: className,
|
||||
objectId: signUser.objectId,
|
||||
};
|
||||
const obj = {
|
||||
UserPtr: UserPtr,
|
||||
SignedUrl: '',
|
||||
Activity: 'Signed',
|
||||
ipAddress: req.headers['x-real-ip'],
|
||||
};
|
||||
const UserPtr = { __type: 'Pointer', className: className, objectId: signUser.objectId };
|
||||
const obj = { UserPtr: UserPtr, SignedUrl: '', Activity: 'Signed', ipAddress: userIP };
|
||||
let updateAuditTrail;
|
||||
if (_resDoc.AuditTrail && _resDoc.AuditTrail.length > 0) {
|
||||
updateAuditTrail = [..._resDoc.AuditTrail, obj];
|
||||
@@ -430,13 +389,13 @@ async function PDF(req) {
|
||||
req.params.docId, //docId
|
||||
data.imageUrl, // url
|
||||
signUser.objectId, // userID
|
||||
req.headers['x-real-ip'], // client ipAddress,
|
||||
userIP, // client ipAddress,
|
||||
_resDoc, // auditTrail, signers, etc data
|
||||
className,
|
||||
sign
|
||||
className, // className based on flow
|
||||
sign // sign base64
|
||||
);
|
||||
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
|
||||
saveFileUsage(pdfSize, data.imageUrl, authUser.objectId);
|
||||
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
|
||||
if (updatedDoc && updatedDoc.isCompleted) {
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail };
|
||||
sendMailsaveCertifcate(
|
||||
@@ -445,7 +404,7 @@ async function PDF(req) {
|
||||
data.imageUrl,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
authUser.objectId
|
||||
_resDoc?.CreatedBy?.objectId
|
||||
);
|
||||
}
|
||||
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
|
||||
|
||||
Generated
+66
-93
@@ -19,17 +19,17 @@
|
||||
"axios": "^1.7.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.20.0",
|
||||
"express": "^4.21.0",
|
||||
"form-data": "^4.0.0",
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^140.0.1",
|
||||
"jsonschema": "^1.4.1",
|
||||
"mailgun.js": "^10.2.3",
|
||||
"mongoose": "^8.6.0",
|
||||
"mongoose": "^8.6.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"nodemailer": "^6.9.14",
|
||||
"nodemailer": "^6.9.15",
|
||||
"parse": "^5.3.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^7.2.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"eslint": "^9.9.1",
|
||||
"jasmine": "^5.2.0",
|
||||
"jasmine": "^5.3.0",
|
||||
"mongodb-runner": "^5.6.6",
|
||||
"nodemon": "^3.1.4",
|
||||
"nyc": "^17.0.0",
|
||||
@@ -5201,9 +5201,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dset": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz",
|
||||
"integrity": "sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz",
|
||||
"integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -5746,9 +5746,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.20.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.20.0.tgz",
|
||||
"integrity": "sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==",
|
||||
"version": "4.21.0",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -5762,7 +5762,7 @@
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"finalhandler": "1.3.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
@@ -5771,11 +5771,11 @@
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.10",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"qs": "6.13.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.19.0",
|
||||
"serve-static": "1.16.0",
|
||||
"serve-static": "1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
@@ -5820,20 +5820,6 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser/node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -5850,11 +5836,42 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/finalhandler": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/express/node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
@@ -7426,22 +7443,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jasmine": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.2.0.tgz",
|
||||
"integrity": "sha512-il+noV96N1BGU9/FMmc8QtAMxC8lPnXUiAvgb0o9MDZATRdxglTQe9wo6UdL049ropQL6MopDYwDlludKR6wJQ==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.3.0.tgz",
|
||||
"integrity": "sha512-Vrv5VWTXVZ/5xcNawlYCmE24pOaZu3KduLr9iAaENoMJ8W8Ryvhfpw2cf3rI4Unc2ajvu2t4tCKjS72TnraBGQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^10.2.2",
|
||||
"jasmine-core": "~5.2.0"
|
||||
"jasmine-core": "~5.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"jasmine": "bin/jasmine.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jasmine-core": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz",
|
||||
"integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.3.0.tgz",
|
||||
"integrity": "sha512-zsOmeBKESky4toybvWEikRiZ0jHoBEu79wNArLfMdSnlLMZx3Xcp6CSm2sUcYyoJC+Uyj8LBJap/MUbVSfJ27g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jmespath": {
|
||||
@@ -8276,9 +8293,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mongoose": {
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.0.tgz",
|
||||
"integrity": "sha512-p6VSbYKvD4ZIabqo8C0kS5eKX1Xpji+opTAIJ9wyuPJ8Y/FblgXSMnFRXnB40bYZLKPQT089K5KU8+bqIXtFdw==",
|
||||
"version": "8.6.1",
|
||||
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.1.tgz",
|
||||
"integrity": "sha512-dppGcYqvsdg+VcnqXR5b467V4a+iNhmvkfYNpEPi6AjaUxnz6ioEDmrMLOi+sOWjvoHapuwPOigV4f2l7HC6ag==",
|
||||
"dependencies": {
|
||||
"bson": "^6.7.0",
|
||||
"kareem": "2.6.3",
|
||||
@@ -8596,9 +8613,9 @@
|
||||
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "6.9.14",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.14.tgz",
|
||||
"integrity": "sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==",
|
||||
"version": "6.9.15",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz",
|
||||
"integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
@@ -10932,69 +10949,25 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.0.tgz",
|
||||
"integrity": "sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==",
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
"send": "0.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static/node_modules/debug/node_modules/ms": {
|
||||
"node_modules/serve-static/node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/serve-static/node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static/node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/set-blocking": {
|
||||
|
||||
@@ -28,17 +28,17 @@
|
||||
"axios": "^1.7.7",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.20.0",
|
||||
"express": "^4.21.0",
|
||||
"form-data": "^4.0.0",
|
||||
"generate-api-key": "^1.0.2",
|
||||
"googleapis": "^140.0.1",
|
||||
"jsonschema": "^1.4.1",
|
||||
"mailgun.js": "^10.2.3",
|
||||
"mongoose": "^8.6.0",
|
||||
"mongoose": "^8.6.1",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"node-forge": "^1.3.1",
|
||||
"nodemailer": "^6.9.14",
|
||||
"nodemailer": "^6.9.15",
|
||||
"parse": "^5.3.0",
|
||||
"parse-dbtool": "^1.2.0",
|
||||
"parse-server": "^7.2.0",
|
||||
@@ -51,7 +51,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"eslint": "^9.9.1",
|
||||
"jasmine": "^5.2.0",
|
||||
"jasmine": "^5.3.0",
|
||||
"mongodb-runner": "^5.6.6",
|
||||
"nodemon": "^3.1.4",
|
||||
"nyc": "^17.0.0",
|
||||
|
||||
Reference in New Issue
Block a user