355
client/package-lock.json
generated
355
client/package-lock.json
generated
@@ -13,19 +13,19 @@
|
||||
"@apollo/client": "^3.13.6",
|
||||
"@emotion/is-prop-valid": "^1.3.1",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
||||
"@firebase/analytics": "^0.10.13",
|
||||
"@firebase/app": "^0.12.1",
|
||||
"@firebase/auth": "^1.10.2",
|
||||
"@firebase/firestore": "^4.7.12",
|
||||
"@firebase/messaging": "^0.12.18",
|
||||
"@firebase/analytics": "^0.10.16",
|
||||
"@firebase/app": "^0.13.0",
|
||||
"@firebase/auth": "^1.10.5",
|
||||
"@firebase/firestore": "^4.7.15",
|
||||
"@firebase/messaging": "^0.12.21",
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@reduxjs/toolkit": "^2.8.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@sentry/cli": "^2.45.0",
|
||||
"@sentry/react": "^9.18.0",
|
||||
"@sentry/vite-plugin": "^3.4.0",
|
||||
"@sentry/react": "^9.22.0",
|
||||
"@sentry/vite-plugin": "^3.5.0",
|
||||
"@splitsoftware/splitio-react": "^2.1.1",
|
||||
"@tanem/react-nprogress": "^5.0.53",
|
||||
"antd": "^5.25.1",
|
||||
"antd": "^5.25.2",
|
||||
"apollo-link-logger": "^2.0.1",
|
||||
"apollo-link-sentry": "^4.3.0",
|
||||
"autosize": "^6.0.1",
|
||||
@@ -49,7 +49,7 @@
|
||||
"normalize-url": "^8.0.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^9.1.2",
|
||||
"query-string": "^9.2.0",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.18.0",
|
||||
@@ -78,7 +78,7 @@
|
||||
"redux-saga": "^1.3.0",
|
||||
"redux-state-sync": "^3.1.4",
|
||||
"reselect": "^5.1.1",
|
||||
"sass": "^1.88.0",
|
||||
"sass": "^1.89.0",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"styled-components": "^6.1.18",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
@@ -90,12 +90,12 @@
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@dotenvx/dotenvx": "^1.44.0",
|
||||
"@dotenvx/dotenvx": "^1.44.1",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@sentry/webpack-plugin": "^3.4.0",
|
||||
"@sentry/webpack-plugin": "^3.5.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
@@ -108,7 +108,7 @@
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^15.15.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"memfs": "^4.17.1",
|
||||
"memfs": "^4.17.2",
|
||||
"os-browserify": "^0.3.0",
|
||||
"playwright": "^1.51.1",
|
||||
"react-error-overlay": "^6.1.0",
|
||||
@@ -120,7 +120,7 @@
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-pwa": "^1.0.0",
|
||||
"vite-plugin-style-import": "^2.0.0",
|
||||
"vitest": "^3.1.3",
|
||||
"vitest": "^3.1.4",
|
||||
"workbox-window": "^7.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2587,9 +2587,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@dotenvx/dotenvx": {
|
||||
"version": "1.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.44.0.tgz",
|
||||
"integrity": "sha512-18Aa+7KP/L2Kj9lxmT4EJZnsCq/xGIHgzU26rdzsKMhjpeT3YY+qin/dNAnIaVHPZnee7kXpZL55M9htd30r7Q==",
|
||||
"version": "1.44.1",
|
||||
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.44.1.tgz",
|
||||
"integrity": "sha512-j1QImCqf/XJmhIjC1OPpgiZV9g370HG9MNT9s/CDwCKsoYzNCPEKK+GfsidahJx7yIlBbm+4dPLlGec+bKn7oA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -2912,13 +2912,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz",
|
||||
"integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==",
|
||||
"version": "9.27.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
||||
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@fingerprintjs/fingerprintjs": {
|
||||
@@ -2931,15 +2934,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/analytics": {
|
||||
"version": "0.10.13",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.13.tgz",
|
||||
"integrity": "sha512-X+6wMOPgA9l0AeeMdMcMfaCP4XKPvrhx55MGuMrfHvUrOvFKldpzBum7KkoGJMoexKmqmKP+mCmJMY9Fb8K6Hw==",
|
||||
"version": "0.10.16",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.16.tgz",
|
||||
"integrity": "sha512-cMtp19He7Fd6uaj/nDEul+8JwvJsN8aRSJyuA1QN3QrKvfDDp+efjVurJO61sJpkVftw9O9nNMdhFbRcTmTfRQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/installations": "0.6.14",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/installations": "0.6.17",
|
||||
"@firebase/logger": "0.4.4",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -2947,14 +2950,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/app": {
|
||||
"version": "0.12.1",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.12.1.tgz",
|
||||
"integrity": "sha512-ASExOlmmjRMdwOQ65Oj6R9JBqa7iiT1/LgZjtbU7FqxoJZNWHrt39NJ/z2bjyYDdAHX8jkY7muFqzahScCXgfA==",
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz",
|
||||
"integrity": "sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/logger": "0.4.4",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"idb": "7.1.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -2963,14 +2966,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/auth": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.2.tgz",
|
||||
"integrity": "sha512-HHudcj3CJyXpoMKslNOVHGSNJdAUjvy5xBA/G/uPb32QFqvx5F3EW9RDYvve2IHEN7Vpc1QTkk/28J32x83UGA==",
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.10.5.tgz",
|
||||
"integrity": "sha512-6wF/NdMTwObL4RNQePunuzMr9O3gyftisvFZFFKf57D2HONXo87YymogRV8d+Z7SLA0rcNBN1gLJVk2D0y97gA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/logger": "0.4.4",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -2987,12 +2990,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/component": {
|
||||
"version": "0.6.14",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.14.tgz",
|
||||
"integrity": "sha512-kf/zAT8GQJ9nYoHuj0mv7twp1QzifKYrO+GsmsVHHM+Hi9KkmI7E3B3J0CtihHpb34vinl4gbJrYJ2p2wfvc9A==",
|
||||
"version": "0.6.17",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.17.tgz",
|
||||
"integrity": "sha512-M6DOg7OySrKEFS8kxA3MU5/xc37fiOpKPMz6cTsMUcsuKB6CiZxxNAvgFta8HGRgEpZbi8WjGIj6Uf+TpOhyzg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -3000,14 +3003,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/firestore": {
|
||||
"version": "4.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.12.tgz",
|
||||
"integrity": "sha512-50KRdSp8xA7+G0wfWxlnCoEN951mt8BVdLMxeP57Rehj2DqIb41q6Fc6JH0dfQ4TlMqWua1YfVY1jPEAaHVF9w==",
|
||||
"version": "4.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.15.tgz",
|
||||
"integrity": "sha512-FgWTmkNBEXdKCoN2ngBNjrMaXuBx6QwjiZZVnOGg+VjUmiBq5gAqlDIW5bZY6i/NYvLUrWugdqIs7y9GHEqwww==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/logger": "0.4.4",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"@firebase/webchannel-wrapper": "1.0.3",
|
||||
"@grpc/grpc-js": "~1.9.0",
|
||||
"@grpc/proto-loader": "^0.7.8",
|
||||
@@ -3021,13 +3024,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/installations": {
|
||||
"version": "0.6.14",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.14.tgz",
|
||||
"integrity": "sha512-uE837g9+sv6PfjWPgOfG3JtjZ+hJ7KBHO4UVenVsvhzgOxFkvLjO/bgE7fyvsaD3fOHSXunx3adRIg4eUEMPyA==",
|
||||
"version": "0.6.17",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.17.tgz",
|
||||
"integrity": "sha512-zfhqCNJZRe12KyADtRrtOj+SeSbD1H/K8J24oQAJVv/u02eQajEGlhZtcx9Qk7vhGWF5z9dvIygVDYqLL4o1XQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/util": "1.12.0",
|
||||
"idb": "7.1.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -3048,15 +3051,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/messaging": {
|
||||
"version": "0.12.18",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.18.tgz",
|
||||
"integrity": "sha512-2MGhUGoCZloB7ysoYzG/T2nnRmHYLT+AcqYouZuD6APabpkDhF8lHsmSQq4MFSlXhI3DKFOXxjuvbY8ec4C2JQ==",
|
||||
"version": "0.12.21",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.21.tgz",
|
||||
"integrity": "sha512-bYJ2Evj167Z+lJ1ach6UglXz5dUKY1zrJZd15GagBUJSR7d9KfiM1W8dsyL0lDxcmhmA/sLaBYAAhF1uilwN0g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.14",
|
||||
"@firebase/installations": "0.6.14",
|
||||
"@firebase/component": "0.6.17",
|
||||
"@firebase/installations": "0.6.17",
|
||||
"@firebase/messaging-interop-types": "0.2.3",
|
||||
"@firebase/util": "1.11.1",
|
||||
"@firebase/util": "1.12.0",
|
||||
"idb": "7.1.1",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
@@ -3071,9 +3074,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@firebase/util": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.11.1.tgz",
|
||||
"integrity": "sha512-RXg4WE8C2LUrvoV/TMGRTu223zZf9Dq9MR8yHZio9nF9TpLnpCPURw9VWWB2WATDl6HfIdWfl2x2SJYtHkN4hw==",
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz",
|
||||
"integrity": "sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -3836,9 +3839,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.1.tgz",
|
||||
"integrity": "sha512-GLjHS13LiBdiuxSJvfWs3+Cx5yt97mCbuVlDteTusS6VRksPhoWviO8L1e3Re1G94m6lkw/l4pjEEyyNaGf19g==",
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
|
||||
"integrity": "sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
@@ -4458,88 +4461,88 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sentry-internal/browser-utils": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.18.0.tgz",
|
||||
"integrity": "sha512-TwSlmgYpHhe55JpOcVApkM0XcXZh1/cYuEPKPFgeaaPD8BrQrLJJvwKxnonSWXOhdnkJxi4GgK7j7mw57PS4aA==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.22.0.tgz",
|
||||
"integrity": "sha512-Ou1tBnVxFAIn8i9gvrWzRotNJQYiu3awNXpsFCw6qFwmiKAVPa6b13vCdolhXnrIiuR77jY1LQnKh9hXpoRzsg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "9.18.0"
|
||||
"@sentry/core": "9.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/feedback": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.18.0.tgz",
|
||||
"integrity": "sha512-QlrB8oQK+5bfhbgK6yHF6rLwLNJ9XuGblTc51yVkm4d4jn4W/HDyaNqMfQF+JXdTiFatl8oz2xdKR8kGK8kXyg==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.22.0.tgz",
|
||||
"integrity": "sha512-zgMVkoC61fgi41zLcSZA59vOtKxcLrKBo1ECYhPD1hxEaneNqY5fhXDwlQBw96P5l2yqkgfX6YZtSdU4ejI9yA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/core": "9.18.0"
|
||||
"@sentry/core": "9.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.18.0.tgz",
|
||||
"integrity": "sha512-2A32FFwrlZtdpBruvpcLEfucu6BpyqOk3F4Bo5smM/5q7u0pa7q5d9FSY5l3nwKEAFAoLGv3hcCb+8wxMm50xA==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.22.0.tgz",
|
||||
"integrity": "sha512-9GOycoKbrclcRXfcbNV8svbmAsOS5R4wXBQmKF4pFLkmFA/lJv9kdZSNYkRvkrxdNfbMIJXP+DV9EqTZcryXig==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "9.18.0",
|
||||
"@sentry/core": "9.18.0"
|
||||
"@sentry-internal/browser-utils": "9.22.0",
|
||||
"@sentry/core": "9.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry-internal/replay-canvas": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.18.0.tgz",
|
||||
"integrity": "sha512-3DEyQLmHcYgcwJ8n8eMhI6bhhawPuMc2xTT+Az8gXMqCO/X9ZACpipAmhXFjYP9Ptl+w0Vh3nllJw+gXc/DOsg==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.22.0.tgz",
|
||||
"integrity": "sha512-EcG9IMSEalFe49kowBTJObWjof/iHteDwpyuAszsFDdQUYATrVUtwpwN7o52vDYWJud4arhjrQnMamIGxa79eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/replay": "9.18.0",
|
||||
"@sentry/core": "9.18.0"
|
||||
"@sentry-internal/replay": "9.22.0",
|
||||
"@sentry/core": "9.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/babel-plugin-component-annotate": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.4.0.tgz",
|
||||
"integrity": "sha512-tSzfc3aE7m0PM0Aj7HBDet5llH9AB9oc+tBQ8AvOqUSnWodLrNCuWeQszJ7mIBovD3figgCU3h0cvI6U5cDtsg==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.5.0.tgz",
|
||||
"integrity": "sha512-s2go8w03CDHbF9luFGtBHKJp4cSpsQzNVqgIa9Pfa4wnjipvrK6CxVT4icpLA3YO6kg5u622Yoa5GF3cJdippw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.18.0.tgz",
|
||||
"integrity": "sha512-0SWfp4J2+mH4lZOcHfyIwt9VoGD7yCGQE1cm0BPcLwKnrVQeXHtUXNYNy8HTHSjTGyoFDhEAYelE/tdA3OLcWQ==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.22.0.tgz",
|
||||
"integrity": "sha512-3TeRm74dvX0JdjX0AgkQa+22iUHwHnY+Q6M05NZ+tDeCNHGK/mEBTeqquS1oQX67jWyuvYmG3VV6RJUxtG9Paw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry-internal/browser-utils": "9.18.0",
|
||||
"@sentry-internal/feedback": "9.18.0",
|
||||
"@sentry-internal/replay": "9.18.0",
|
||||
"@sentry-internal/replay-canvas": "9.18.0",
|
||||
"@sentry/core": "9.18.0"
|
||||
"@sentry-internal/browser-utils": "9.22.0",
|
||||
"@sentry-internal/feedback": "9.22.0",
|
||||
"@sentry-internal/replay": "9.22.0",
|
||||
"@sentry-internal/replay-canvas": "9.22.0",
|
||||
"@sentry/core": "9.22.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.4.0.tgz",
|
||||
"integrity": "sha512-X1Q41AsQ6xcT6hB4wYmBDBukndKM/inT4IsR7pdKLi7ICpX2Qq6lisamBAEPCgEvnLpazSFguaiC0uiwMKAdqw==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.5.0.tgz",
|
||||
"integrity": "sha512-zDzPrhJqAAy2VzV4g540qAZH4qxzisstK2+NIJPZUUKztWRWUV2cMHsyUtdctYgloGkLyGpZJBE3RE6dmP/xqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@sentry/babel-plugin-component-annotate": "3.4.0",
|
||||
"@sentry/babel-plugin-component-annotate": "3.5.0",
|
||||
"@sentry/cli": "2.42.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"find-up": "^5.0.0",
|
||||
@@ -4899,22 +4902,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.18.0.tgz",
|
||||
"integrity": "sha512-kRVH8BqMiaU2FTHYa68zNlAloS43jl4XtIEHkLKVH/7gUtwRmM4Gqj8P7RTrZdO1Lo7ksYnGj+AG05Z09CRbOw==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.22.0.tgz",
|
||||
"integrity": "sha512-ixvtKmPF42Y6ckGUbFlB54OWI75H2gO5UYHojO6eXFpS7xO3ZGgV/QH6wb40mWK+0w5XZ0233FuU9VpsuE6mKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/react": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.18.0.tgz",
|
||||
"integrity": "sha512-1cCLYZrZ2gu6H8eE83DC47kLf+pzD1Rim3dDoOEvwt1F5cD3K/DBeIhoHZaXqBeQxuVyHXOOLXSAC/CIuas5Aw==",
|
||||
"version": "9.22.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.22.0.tgz",
|
||||
"integrity": "sha512-mI43NnioBYdG5TiXqRlhV1feZs9bnrrl+k5HOHBK7VQtymaXO0fkcsRLZTkdSgLRLMJGasZuvVhq2xK+18QyWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "9.18.0",
|
||||
"@sentry/core": "9.18.0",
|
||||
"@sentry/browser": "9.22.0",
|
||||
"@sentry/core": "9.22.0",
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4925,12 +4928,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vite-plugin": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-3.4.0.tgz",
|
||||
"integrity": "sha512-pUFBGrKsHuc8K6A7B1wU2nx65n9aIzvTlcHX9yZ1qvjEO0cZFih0JCwu1Fcav/yrtT9RMN44L/ugu/kMBHQhjQ==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-3.5.0.tgz",
|
||||
"integrity": "sha512-jUnpTdpicG8wefamw7eNo2uO+Q3KCbOAiF76xH4gfNHSW6TN2hBfOtmLu7J+ive4c0Al3+NEHz19bIPR0lkwWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "3.4.0",
|
||||
"@sentry/bundler-plugin-core": "3.5.0",
|
||||
"unplugin": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -4938,13 +4941,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/webpack-plugin": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-3.4.0.tgz",
|
||||
"integrity": "sha512-i+nAxxniJV5ovijojjTF5n+Yj08Xk8my+vm8+oo0C0I7xcnI2gOKft6B0sJOq01CNbo85X5m/3/edL0PKoWE9w==",
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-3.5.0.tgz",
|
||||
"integrity": "sha512-xvclj0QY2HyU7uJLzOlHSrZQBDwfnGKJxp8mmlU4L7CwmK+8xMCqlO7tYZoqE4K/wU3c2xpXql70x8qmvNMxzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "3.4.0",
|
||||
"@sentry/bundler-plugin-core": "3.5.0",
|
||||
"unplugin": "1.0.1",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
@@ -5810,14 +5813,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz",
|
||||
"integrity": "sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz",
|
||||
"integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.1.3",
|
||||
"@vitest/utils": "3.1.3",
|
||||
"@vitest/spy": "3.1.4",
|
||||
"@vitest/utils": "3.1.4",
|
||||
"chai": "^5.2.0",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -5826,13 +5829,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.3.tgz",
|
||||
"integrity": "sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz",
|
||||
"integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.1.3",
|
||||
"@vitest/spy": "3.1.4",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.17"
|
||||
},
|
||||
@@ -5873,9 +5876,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.3.tgz",
|
||||
"integrity": "sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz",
|
||||
"integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5886,13 +5889,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.3.tgz",
|
||||
"integrity": "sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz",
|
||||
"integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "3.1.3",
|
||||
"@vitest/utils": "3.1.4",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
@@ -5907,13 +5910,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.3.tgz",
|
||||
"integrity": "sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz",
|
||||
"integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.1.3",
|
||||
"@vitest/pretty-format": "3.1.4",
|
||||
"magic-string": "^0.30.17",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
@@ -5939,9 +5942,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.3.tgz",
|
||||
"integrity": "sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz",
|
||||
"integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5952,13 +5955,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.3.tgz",
|
||||
"integrity": "sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz",
|
||||
"integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.1.3",
|
||||
"@vitest/pretty-format": "3.1.4",
|
||||
"loupe": "^3.1.3",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -6088,9 +6091,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/antd": {
|
||||
"version": "5.25.1",
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.25.1.tgz",
|
||||
"integrity": "sha512-4KC7KuPCjr0z3Vuw9DsF+ceqJaPLbuUI3lOX1sY8ix25ceamp+P8yxOmk3Y2JHCD2ZAhq+5IQ/DTJRN2adWYKQ==",
|
||||
"version": "5.25.2",
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.25.2.tgz",
|
||||
"integrity": "sha512-7R2nUvlHhey7Trx64+hCtGXOiy+DTUs1Lv5bwbV1LzEIZIhWb0at1AM6V3K108a5lyoR9n7DX3ptlLF7uYV/DQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.2.0",
|
||||
@@ -6128,11 +6131,11 @@
|
||||
"rc-rate": "~2.13.1",
|
||||
"rc-resize-observer": "^1.4.3",
|
||||
"rc-segmented": "~2.7.0",
|
||||
"rc-select": "~14.16.7",
|
||||
"rc-select": "~14.16.8",
|
||||
"rc-slider": "~11.1.8",
|
||||
"rc-steps": "~6.0.1",
|
||||
"rc-switch": "~4.1.0",
|
||||
"rc-table": "~7.50.4",
|
||||
"rc-table": "~7.50.5",
|
||||
"rc-tabs": "~15.6.1",
|
||||
"rc-textarea": "~1.10.0",
|
||||
"rc-tooltip": "~6.4.0",
|
||||
@@ -11833,9 +11836,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "4.17.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.1.tgz",
|
||||
"integrity": "sha512-thuTRd7F4m4dReCIy7vv4eNYnU6XI/tHMLSMMHLiortw/Y0QxqKtinG523U2aerzwYWGi606oBP4oMPy4+edag==",
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz",
|
||||
"integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -13569,9 +13572,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/query-string": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.2.tgz",
|
||||
"integrity": "sha512-s3UlTyjxRux4KjwWaJsjh1Mp8zoCkSGKirbD9H89pEM9UOZsfpRZpdfzvsy2/mGlLfC3NnYVpy2gk7jXITHEtA==",
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-9.2.0.tgz",
|
||||
"integrity": "sha512-YIRhrHujoQxhexwRLxfy3VSjOXmvZRd2nyw1PwL1UUqZ/ys1dEZd1+NSgXkne2l/4X/7OXkigEAuhTX0g/ivJQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decode-uri-component": "^0.4.1",
|
||||
@@ -14024,9 +14027,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rc-select": {
|
||||
"version": "14.16.7",
|
||||
"resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.7.tgz",
|
||||
"integrity": "sha512-lT9kO5gFHQdJzu9a0btcOtNaJHkhenSl8H5mcpgXN9VIMXP59rnkpbdHmPrteixWs1D5zFOTyoTYX3b7joADIQ==",
|
||||
"version": "14.16.8",
|
||||
"resolved": "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz",
|
||||
"integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
@@ -14097,9 +14100,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rc-table": {
|
||||
"version": "7.50.4",
|
||||
"resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.50.4.tgz",
|
||||
"integrity": "sha512-Y+YuncnQqoS5e7yHvfvlv8BmCvwDYDX/2VixTBEhkMDk9itS9aBINp4nhzXFKiBP/frG4w0pS9d9Rgisl0T1Bw==",
|
||||
"version": "7.50.5",
|
||||
"resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.50.5.tgz",
|
||||
"integrity": "sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.1",
|
||||
@@ -15375,9 +15378,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.88.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
|
||||
"integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
|
||||
"version": "1.89.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz",
|
||||
"integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.0",
|
||||
@@ -17533,9 +17536,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.3.tgz",
|
||||
"integrity": "sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz",
|
||||
"integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -17731,19 +17734,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.3.tgz",
|
||||
"integrity": "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz",
|
||||
"integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "3.1.3",
|
||||
"@vitest/mocker": "3.1.3",
|
||||
"@vitest/pretty-format": "^3.1.3",
|
||||
"@vitest/runner": "3.1.3",
|
||||
"@vitest/snapshot": "3.1.3",
|
||||
"@vitest/spy": "3.1.3",
|
||||
"@vitest/utils": "3.1.3",
|
||||
"@vitest/expect": "3.1.4",
|
||||
"@vitest/mocker": "3.1.4",
|
||||
"@vitest/pretty-format": "^3.1.4",
|
||||
"@vitest/runner": "3.1.4",
|
||||
"@vitest/snapshot": "3.1.4",
|
||||
"@vitest/spy": "3.1.4",
|
||||
"@vitest/utils": "3.1.4",
|
||||
"chai": "^5.2.0",
|
||||
"debug": "^4.4.0",
|
||||
"expect-type": "^1.2.1",
|
||||
@@ -17756,7 +17759,7 @@
|
||||
"tinypool": "^1.0.2",
|
||||
"tinyrainbow": "^2.0.0",
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vite-node": "3.1.3",
|
||||
"vite-node": "3.1.4",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
@@ -17772,8 +17775,8 @@
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||
"@vitest/browser": "3.1.3",
|
||||
"@vitest/ui": "3.1.3",
|
||||
"@vitest/browser": "3.1.4",
|
||||
"@vitest/ui": "3.1.4",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
"@apollo/client": "^3.13.6",
|
||||
"@emotion/is-prop-valid": "^1.3.1",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
||||
"@firebase/analytics": "^0.10.13",
|
||||
"@firebase/app": "^0.12.1",
|
||||
"@firebase/auth": "^1.10.2",
|
||||
"@firebase/firestore": "^4.7.12",
|
||||
"@firebase/messaging": "^0.12.18",
|
||||
"@firebase/analytics": "^0.10.16",
|
||||
"@firebase/app": "^0.13.0",
|
||||
"@firebase/auth": "^1.10.5",
|
||||
"@firebase/firestore": "^4.7.15",
|
||||
"@firebase/messaging": "^0.12.21",
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@reduxjs/toolkit": "^2.8.1",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@sentry/cli": "^2.45.0",
|
||||
"@sentry/react": "^9.18.0",
|
||||
"@sentry/vite-plugin": "^3.4.0",
|
||||
"@sentry/react": "^9.22.0",
|
||||
"@sentry/vite-plugin": "^3.5.0",
|
||||
"@splitsoftware/splitio-react": "^2.1.1",
|
||||
"@tanem/react-nprogress": "^5.0.53",
|
||||
"antd": "^5.25.1",
|
||||
"antd": "^5.25.2",
|
||||
"apollo-link-logger": "^2.0.1",
|
||||
"apollo-link-sentry": "^4.3.0",
|
||||
"autosize": "^6.0.1",
|
||||
@@ -48,7 +48,7 @@
|
||||
"normalize-url": "^8.0.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^9.1.2",
|
||||
"query-string": "^9.2.0",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.18.0",
|
||||
@@ -77,7 +77,7 @@
|
||||
"redux-saga": "^1.3.0",
|
||||
"redux-state-sync": "^3.1.4",
|
||||
"reselect": "^5.1.1",
|
||||
"sass": "^1.88.0",
|
||||
"sass": "^1.89.0",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"styled-components": "^6.1.18",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
@@ -130,12 +130,12 @@
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@babel/preset-react": "^7.27.1",
|
||||
"@dotenvx/dotenvx": "^1.44.0",
|
||||
"@dotenvx/dotenvx": "^1.44.1",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@sentry/webpack-plugin": "^3.4.0",
|
||||
"@sentry/webpack-plugin": "^3.5.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
@@ -148,7 +148,7 @@
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^15.15.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"memfs": "^4.17.1",
|
||||
"memfs": "^4.17.2",
|
||||
"os-browserify": "^0.3.0",
|
||||
"playwright": "^1.51.1",
|
||||
"react-error-overlay": "^6.1.0",
|
||||
@@ -160,7 +160,7 @@
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-pwa": "^1.0.0",
|
||||
"vite-plugin-style-import": "^2.0.0",
|
||||
"vitest": "^3.1.3",
|
||||
"vitest": "^3.1.4",
|
||||
"workbox-window": "^7.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,16 +34,14 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
|
||||
|
||||
SubscribeToTopicForFCMNotification();
|
||||
|
||||
//Register WS handlers
|
||||
// Register WebSocket handlers
|
||||
if (socket && socket.connected) {
|
||||
registerMessagingHandlers({ socket, client });
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (socket && socket.connected) {
|
||||
return () => {
|
||||
unregisterMessagingHandlers({ socket });
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}, [bodyshop, socket, t, client]);
|
||||
|
||||
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Badge, Card, List, Space, Tag } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -10,35 +10,60 @@ import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
import _ from "lodash";
|
||||
import "./chat-conversation-list.styles.scss";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries.js";
|
||||
import { phone } from "phone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
selectedConversation: selectSelectedConversation
|
||||
selectedConversation: selectSelectedConversation,
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setSelectedConversation: (conversationId) => dispatch(setSelectedConversation(conversationId))
|
||||
});
|
||||
|
||||
function ChatConversationListComponent({ conversationList, selectedConversation, setSelectedConversation }) {
|
||||
// That comma is there for a reason, do not remove it
|
||||
function ChatConversationListComponent({ conversationList, selectedConversation, setSelectedConversation, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const [, forceUpdate] = useState(false);
|
||||
|
||||
// Re-render every minute
|
||||
const phoneNumbers = conversationList.map((item) => phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, ""));
|
||||
|
||||
const { data: optOutData } = useQuery(GET_PHONE_NUMBER_OPT_OUTS, {
|
||||
variables: {
|
||||
bodyshopid: bodyshop.id,
|
||||
phone_numbers: phoneNumbers
|
||||
},
|
||||
skip: !conversationList.length,
|
||||
fetchPolicy: "cache-and-network"
|
||||
});
|
||||
|
||||
const optOutMap = useMemo(() => {
|
||||
const map = new Map();
|
||||
optOutData?.phone_number_opt_out?.forEach((optOut) => {
|
||||
map.set(optOut.phone_number, true);
|
||||
});
|
||||
return map;
|
||||
}, [optOutData?.phone_number_opt_out]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
forceUpdate((prev) => !prev); // Toggle state to trigger re-render
|
||||
}, 60000); // 1 minute in milliseconds
|
||||
|
||||
return () => clearInterval(interval); // Cleanup on unmount
|
||||
forceUpdate((prev) => !prev);
|
||||
}, 60000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Memoize the sorted conversation list
|
||||
const sortedConversationList = React.useMemo(() => {
|
||||
const sortedConversationList = useMemo(() => {
|
||||
return _.orderBy(conversationList, ["updated_at"], ["desc"]);
|
||||
}, [conversationList]);
|
||||
|
||||
const renderConversation = (index) => {
|
||||
const renderConversation = (index, t) => {
|
||||
const item = sortedConversationList[index];
|
||||
const normalizedPhone = phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, "");
|
||||
const hasOptOutEntry = optOutMap.has(normalizedPhone);
|
||||
|
||||
const cardContentRight = <TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>;
|
||||
const cardContentLeft =
|
||||
item.job_conversations.length > 0
|
||||
@@ -60,7 +85,12 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
|
||||
</>
|
||||
);
|
||||
|
||||
const cardExtra = <Badge count={item.messages_aggregate.aggregate.count} />;
|
||||
const cardExtra = (
|
||||
<>
|
||||
<Badge count={item.messages_aggregate.aggregate.count} />
|
||||
{hasOptOutEntry && <Tag color="red">{t("messaging.labels.no_consent")}</Tag>}
|
||||
</>
|
||||
);
|
||||
|
||||
const getCardStyle = () =>
|
||||
item.id === selectedConversation
|
||||
@@ -73,9 +103,25 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
|
||||
onClick={() => setSelectedConversation(item.id)}
|
||||
className={`chat-list-item ${item.id === selectedConversation ? "chat-list-selected-conversation" : ""}`}
|
||||
>
|
||||
<Card style={getCardStyle()} bordered={false} size="small" extra={cardExtra} title={cardTitle}>
|
||||
<div style={{ display: "inline-block", width: "70%", textAlign: "left" }}>{cardContentLeft}</div>
|
||||
<div style={{ display: "inline-block", width: "30%", textAlign: "right" }}>{cardContentRight}</div>
|
||||
<Card style={getCardStyle()} variant={true} size="small" extra={cardExtra} title={cardTitle}>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "70%",
|
||||
textAlign: "left"
|
||||
}}
|
||||
>
|
||||
{cardContentLeft}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "inline-block",
|
||||
width: "30%",
|
||||
textAlign: "right"
|
||||
}}
|
||||
>
|
||||
{cardContentRight}
|
||||
</div>
|
||||
</Card>
|
||||
</List.Item>
|
||||
);
|
||||
@@ -85,7 +131,7 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
|
||||
<div className="chat-list-container">
|
||||
<Virtuoso
|
||||
data={sortedConversationList}
|
||||
itemContent={(index) => renderConversation(index)}
|
||||
itemContent={(index) => renderConversation(index, t)}
|
||||
style={{ height: "100%", width: "100%" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
/* Add spacing and better alignment for items */
|
||||
.chat-list-item {
|
||||
padding: 0.5rem 0; /* Add spacing between list items */
|
||||
padding: 0.2rem 0; /* Add spacing between list items */
|
||||
|
||||
.ant-card {
|
||||
border-radius: 8px; /* Slight rounding for card edges */
|
||||
|
||||
@@ -37,7 +37,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
jobId: conversation.job_conversations[0] && conversation.job_conversations[0].jobid
|
||||
jobId: conversation.job_conversations[0] && conversation.job_conversations[0]?.jobid
|
||||
},
|
||||
|
||||
skip: !open || !conversation.job_conversations || conversation.job_conversations.length === 0
|
||||
@@ -67,14 +67,14 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
|
||||
<>
|
||||
{!bodyshop.uselocalmediaserver && (
|
||||
<JobsDocumentImgproxyGalleryExternal
|
||||
jobId={conversation.job_conversations[0].jobid}
|
||||
jobId={conversation.job_conversations[0]?.jobid}
|
||||
externalMediaState={[selectedMedia, setSelectedMedia]}
|
||||
/>
|
||||
)}
|
||||
{bodyshop.uselocalmediaserver && open && (
|
||||
<JobDocumentsLocalGalleryExternal
|
||||
externalMediaState={[selectedMedia, setSelectedMedia]}
|
||||
jobId={conversation.job_conversations[0] && conversation.job_conversations[0].jobid}
|
||||
jobId={conversation.job_conversations[0] && conversation.job_conversations[0]?.jobid}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -89,7 +89,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
|
||||
{bodyshop.uselocalmediaserver && open && (
|
||||
<JobDocumentsLocalGalleryExternal
|
||||
externalMediaState={[selectedMedia, setSelectedMedia]}
|
||||
jobId={conversation.job_conversations[0] && conversation.job_conversations[0].jobid}
|
||||
jobId={conversation.job_conversations[0] && conversation.job_conversations[0]?.jobid}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LoadingOutlined, SendOutlined } from "@ant-design/icons";
|
||||
import { Input, Spin } from "antd";
|
||||
import { Alert, Input, Spin } from "antd";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -10,6 +10,9 @@ import { selectIsSending, selectMessage } from "../../redux/messaging/messaging.
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ChatMediaSelector from "../chat-media-selector/chat-media-selector.component";
|
||||
import ChatPresetsComponent from "../chat-presets/chat-presets.component";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { phone } from "phone";
|
||||
import { GET_PHONE_NUMBER_OPT_OUT } from "../../graphql/phone-number-opt-out.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -25,16 +28,24 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSending, message, setMessage }) {
|
||||
const inputArea = useRef(null);
|
||||
const [selectedMedia, setSelectedMedia] = useState([]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const normalizedPhone = phone(conversation.phone_num, "CA").phoneNumber.replace(/^\+1/, "");
|
||||
const { data: optOutData } = useQuery(GET_PHONE_NUMBER_OPT_OUT, {
|
||||
variables: { bodyshopid: bodyshop.id, phone_number: normalizedPhone },
|
||||
fetchPolicy: "cache-and-network"
|
||||
});
|
||||
|
||||
const isOptedOut = !!optOutData?.phone_number_opt_out?.[0];
|
||||
|
||||
useEffect(() => {
|
||||
inputArea.current.focus();
|
||||
}, [isSending, setMessage]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleEnter = () => {
|
||||
const selectedImages = selectedMedia.filter((i) => i.isSelected);
|
||||
if ((message === "" || !message) && selectedImages.length === 0) return;
|
||||
if (isOptedOut) return; // Prevent sending if phone number is opted out
|
||||
logImEXEvent("messaging_send_message");
|
||||
|
||||
if (selectedImages.length < 11) {
|
||||
@@ -44,7 +55,8 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
|
||||
messagingServiceSid: bodyshop.messagingservicesid,
|
||||
conversationid: conversation.id,
|
||||
selectedMedia: selectedImages,
|
||||
imexshopid: bodyshop.imexshopid
|
||||
imexshopid: bodyshop.imexshopid,
|
||||
bodyshopid: bodyshop.id
|
||||
};
|
||||
sendMessage(newMessage);
|
||||
setSelectedMedia(
|
||||
@@ -57,6 +69,7 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
|
||||
|
||||
return (
|
||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||
{isOptedOut && <Alert message={t("messaging.errors.no_consent")} type="warning" style={{ marginBottom: 8 }} />}
|
||||
<ChatPresetsComponent className="imex-flex-row__margin" />
|
||||
<ChatMediaSelector
|
||||
conversation={conversation}
|
||||
@@ -71,18 +84,18 @@ function ChatSendMessageComponent({ conversation, bodyshop, sendMessage, isSendi
|
||||
ref={inputArea}
|
||||
autoSize={{ minRows: 1, maxRows: 4 }}
|
||||
value={message}
|
||||
disabled={isSending}
|
||||
disabled={isSending || isOptedOut}
|
||||
placeholder={t("messaging.labels.typeamessage")}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
onPressEnter={(event) => {
|
||||
event.preventDefault();
|
||||
if (!!!event.shiftKey) handleEnter();
|
||||
if (!event.shiftKey && !isOptedOut) handleEnter();
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
<SendOutlined
|
||||
className="chat-send-message-button"
|
||||
// disabled={message === "" || !message}
|
||||
disabled={isOptedOut || message === "" || !message}
|
||||
onClick={handleEnter}
|
||||
/>
|
||||
<Spin
|
||||
|
||||
@@ -81,8 +81,9 @@ export function HasFeatureAccess({ featureName, bodyshop, bypass, debug = false
|
||||
}
|
||||
return (
|
||||
bodyshop?.features?.allAccess ||
|
||||
bodyshop?.features?.[featureName] ||
|
||||
dayjs(bodyshop?.features[featureName]).isAfter(dayjs())
|
||||
(typeof bodyshop?.features?.[featureName] === "boolean"
|
||||
? bodyshop?.features?.[featureName]
|
||||
: dayjs(bodyshop?.features?.[featureName]).isAfter(dayjs()))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ import { Col, Row } from "antd";
|
||||
import Axios from "axios";
|
||||
import _ from "lodash";
|
||||
import queryString from "query-string";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_AVAILABLE_JOB,
|
||||
@@ -33,7 +34,6 @@ import OwnerFindModalContainer from "../owner-find-modal/owner-find-modal.contai
|
||||
import { GetSupplementDelta } from "./jobs-available-supplement.estlines.util";
|
||||
import HeaderFields from "./jobs-available-supplement.headerfields";
|
||||
import JobsAvailableTableComponent from "./jobs-available-table.component";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -195,7 +195,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
||||
|
||||
await deleteJob({
|
||||
variables: { id: estData.id }
|
||||
}).then((r) => {
|
||||
}).then(() => {
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
});
|
||||
@@ -315,7 +315,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
||||
|
||||
deleteJob({
|
||||
variables: { id: estData.id }
|
||||
}).then((r) => {
|
||||
}).then(() => {
|
||||
refetch();
|
||||
setInsertLoading(false);
|
||||
});
|
||||
@@ -372,7 +372,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
||||
loadEstData({ variables: { id: record.id } });
|
||||
modalSearchState[1](record.clm_no);
|
||||
setJobModalVisible(true);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -456,7 +456,7 @@ function replaceEmpty(someObj, replaceValue = null) {
|
||||
return JSON.parse(temp);
|
||||
}
|
||||
|
||||
async function CheckTaxRatesUSA(estData, bodyshop) {
|
||||
async function CheckTaxRatesUSA(estData) {
|
||||
if (!estData.parts_tax_rates?.PAM) {
|
||||
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
|
||||
}
|
||||
@@ -568,7 +568,7 @@ async function CheckTaxRates(estData, bodyshop) {
|
||||
});
|
||||
//}
|
||||
}
|
||||
function ResolveCCCLineIssues(estData, bodyshop) {
|
||||
function ResolveCCCLineIssues(estData) {
|
||||
//Find all misc amounts, populate them to the act price.
|
||||
//This needs to be done before cleansing unq_seq since some misc prices could move over.
|
||||
estData.joblines.data.forEach((line) => {
|
||||
@@ -585,6 +585,9 @@ function ResolveCCCLineIssues(estData, bodyshop) {
|
||||
// line.notes += ` | ET/UT Update (prev = ${line.mod_lbr_ty})`;
|
||||
line.mod_lbr_ty = "LAR";
|
||||
}
|
||||
if (line.mod_lbr_ty === "OTSL") {
|
||||
line.mod_lbr_ty = line.mod_lbr_hrs === 0 ? null : "LAB";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Input, Table } from "antd";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import { GET_PHONE_NUMBER_OPT_OUTS } from "../../graphql/phone-number-opt-out.queries";
|
||||
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser
|
||||
});
|
||||
|
||||
const mapDispatchToProps = () => ({});
|
||||
|
||||
function PhoneNumberConsentList({ bodyshop, currentUser }) {
|
||||
const { t } = useTranslation();
|
||||
const [search, setSearch] = useState("");
|
||||
const { loading, data } = useQuery(GET_PHONE_NUMBER_OPT_OUTS, {
|
||||
variables: { bodyshopid: bodyshop.id, search: search ? `%${search}%` : undefined },
|
||||
fetchPolicy: "network-only"
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("consent.phone_number"),
|
||||
dataIndex: "phone_number",
|
||||
render: (text) => <PhoneNumberFormatter>{text}</PhoneNumberFormatter>,
|
||||
sorter: (a, b) => a.phone_number.localeCompare(b.phone_number)
|
||||
},
|
||||
{
|
||||
title: t("consent.created_at"),
|
||||
dataIndex: "created_at",
|
||||
render: (text) => <TimeAgoFormatter>{text}</TimeAgoFormatter>,
|
||||
sorter: (a, b) => new Date(a.created_at) - new Date(b.created_at)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onSearch={(value) => setSearch(value)}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={data?.phone_number_opt_out}
|
||||
loading={loading}
|
||||
rowKey="id"
|
||||
style={{ marginTop: 16 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PhoneNumberConsentList);
|
||||
@@ -1,7 +1,15 @@
|
||||
import { Card, Col, Form, Radio, Row } from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../../redux/user/user.selectors";
|
||||
import { HasFeatureAccess } from "../../feature-wrapper/feature-wrapper.component";
|
||||
|
||||
const LayoutSettings = ({ t }) => (
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
const LayoutSettings = ({ t, bodyshop }) => (
|
||||
<Card title={t("production.settings.layout")} style={{ maxWidth: "100%", overflowX: "auto" }}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{[
|
||||
@@ -30,14 +38,18 @@ const LayoutSettings = ({ t }) => (
|
||||
{ value: false, label: t("production.labels.wide") }
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "cardcolor",
|
||||
label: t("production.labels.cardcolor"),
|
||||
options: [
|
||||
{ value: true, label: t("production.labels.on") },
|
||||
{ value: false, label: t("production.labels.off") }
|
||||
]
|
||||
},
|
||||
...(HasFeatureAccess({ bodyshop, featureName: "smartscheduling" })
|
||||
? [
|
||||
{
|
||||
name: "cardcolor",
|
||||
label: t("production.labels.cardcolor"),
|
||||
options: [
|
||||
{ value: true, label: t("production.labels.on") },
|
||||
{ value: false, label: t("production.labels.off") }
|
||||
]
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
name: "kiosk",
|
||||
label: t("production.labels.kiosk_mode"),
|
||||
@@ -67,4 +79,4 @@ LayoutSettings.propTypes = {
|
||||
t: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default LayoutSettings;
|
||||
export default connect(mapStateToProps)(LayoutSettings);
|
||||
|
||||
@@ -6,6 +6,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
import { QUERY_ACTIVE_EMPLOYEES, QUERY_ACTIVE_EMPLOYEES_WITH_EMAIL } from "../../graphql/employees.queries";
|
||||
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
||||
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
||||
@@ -18,11 +19,10 @@ import EmployeeSearchSelectEmail from "../employee-search-select/employee-search
|
||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||
import BlurWrapperComponent from "../feature-wrapper/blur-wrapper.component";
|
||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||
import LockWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||
import "./report-center-modal.styles.scss";
|
||||
import LockWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
reportCenterModal: selectReportCenter,
|
||||
@@ -389,5 +389,7 @@ const restrictedReports = [
|
||||
{ key: "job_costing_ro_date_detail", days: 183 },
|
||||
{ key: "job_costing_ro_estimator", days: 183 },
|
||||
{ key: "job_lifecycle_date_detail", days: 183 },
|
||||
{ key: "job_lifecycle_date_summary", days: 183 }
|
||||
{ key: "job_lifecycle_date_summary", days: 183 },
|
||||
{ key: "customer_list", days: 183 },
|
||||
{ key: "customer_list_excel", days: 183 }
|
||||
];
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Typography } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import PhoneNumberConsentList from "../phone-number-consent/phone-number-consent.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
function ShopInfoConsentComponent({ bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={4}>{t("settings.title")}</Typography.Title>
|
||||
{<PhoneNumberConsentList bodyshop={bodyshop} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoConsentComponent);
|
||||
28
client/src/graphql/phone-number-opt-out.queries.js
Normal file
28
client/src/graphql/phone-number-opt-out.queries.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
export const GET_PHONE_NUMBER_OPT_OUT = gql`
|
||||
query GET_PHONE_NUMBER_OPT_OUT($bodyshopid: uuid!, $phone_number: String!) {
|
||||
phone_number_opt_out(where: { bodyshopid: { _eq: $bodyshopid }, phone_number: { _eq: $phone_number } }) {
|
||||
id
|
||||
bodyshopid
|
||||
phone_number
|
||||
created_at
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_PHONE_NUMBER_OPT_OUTS = gql`
|
||||
query GET_PHONE_NUMBER_OPT_OUTS($bodyshopid: uuid!, $search: String) {
|
||||
phone_number_opt_out(
|
||||
where: { bodyshopid: { _eq: $bodyshopid }, phone_number: { _ilike: $search } }
|
||||
order_by: [{ phone_number: asc }, { updated_at: desc }]
|
||||
) {
|
||||
id
|
||||
bodyshopid
|
||||
phone_number
|
||||
created_at
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -10,10 +10,10 @@ import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.comp
|
||||
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
||||
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
||||
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
||||
import ShopInfoConsentComponent from "../../components/shop-info/shop-info.consent.component";
|
||||
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
||||
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
||||
|
||||
@@ -91,6 +91,14 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
||||
children: <ShopCsiConfig />
|
||||
});
|
||||
}
|
||||
|
||||
// Add Consent Settings tab
|
||||
items.push({
|
||||
key: "consent",
|
||||
label: t("bodyshop.labels.consent_settings"),
|
||||
children: <ShopInfoConsentComponent bodyshop={bodyshop} />
|
||||
});
|
||||
|
||||
return (
|
||||
<RbacWrapper action="shop:config">
|
||||
<Tabs activeKey={search.tab} onChange={(key) => history({ search: `?tab=${key}` })} items={items} />
|
||||
|
||||
@@ -105,7 +105,6 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
||||
...action.payload //Spread current user details in.
|
||||
}
|
||||
};
|
||||
|
||||
case UserActionTypes.SET_SHOP_DETAILS:
|
||||
return {
|
||||
...state,
|
||||
@@ -126,6 +125,7 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
||||
...state,
|
||||
imexshopid: action.payload
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -656,6 +656,7 @@
|
||||
}
|
||||
},
|
||||
"labels": {
|
||||
"consent_settings": "Phone Number Opt-Out List",
|
||||
"2tiername": "Name => RO",
|
||||
"2tiersetup": "2 Tier Setup",
|
||||
"2tiersource": "Source => RO",
|
||||
@@ -2377,7 +2378,8 @@
|
||||
"errors": {
|
||||
"invalidphone": "The phone number is invalid. Unable to open conversation. ",
|
||||
"noattachedjobs": "No Jobs have been associated to this conversation. ",
|
||||
"updatinglabel": "Error updating label. {{error}}"
|
||||
"updatinglabel": "Error updating label. {{error}}",
|
||||
"no_consent": "This phone number has not consented to receive messages."
|
||||
},
|
||||
"labels": {
|
||||
"addlabel": "Add a label to this conversation.",
|
||||
@@ -2393,7 +2395,8 @@
|
||||
"selectmedia": "Select Media",
|
||||
"sentby": "Sent by {{by}} at {{time}}",
|
||||
"typeamessage": "Send a message...",
|
||||
"unarchive": "Unarchive"
|
||||
"unarchive": "Unarchive",
|
||||
"no_consent": "No Consent"
|
||||
},
|
||||
"render": {
|
||||
"conversation_list": "Conversation List"
|
||||
@@ -3099,6 +3102,7 @@
|
||||
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
|
||||
"csi": "CSI Responses",
|
||||
"customer_list": "Customer List",
|
||||
"customer_list_excel": "Customer List - Excel",
|
||||
"cycle_time_analysis": "Cycle Time Analysis",
|
||||
"estimates_written_converted": "Estimates Written/Converted",
|
||||
"estimator_detail": "Jobs by Estimator (Detail)",
|
||||
@@ -3862,6 +3866,14 @@
|
||||
"validation": {
|
||||
"unique_vendor_name": "You must enter a unique vendor name."
|
||||
}
|
||||
},
|
||||
"consent": {
|
||||
"phone_number": "Phone Number",
|
||||
"status": "Consent Status",
|
||||
"created_at": "Created At"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Phone Number Opt-Out List"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3100,6 +3100,7 @@
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"customer_list_excel": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
|
||||
@@ -3100,6 +3100,7 @@
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"customer_list_excel": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
|
||||
@@ -2004,6 +2004,18 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "customers"
|
||||
},
|
||||
customer_list_excel: {
|
||||
title: i18n.t("reportcenter.templates.customer_list_excel"),
|
||||
subject: i18n.t("reportcenter.templates.customer_list_excel"),
|
||||
key: "customer_list_excel",
|
||||
reporttype: "excel",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced")
|
||||
},
|
||||
group: "customers"
|
||||
},
|
||||
exported_gsr_by_ro: {
|
||||
title: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
|
||||
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
|
||||
@@ -2241,7 +2253,7 @@ export const TemplateList = (type, context) => {
|
||||
field: i18n.t("bills.fields.date")
|
||||
},
|
||||
group: "purchases"
|
||||
},
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "courtesycarcontract"
|
||||
|
||||
@@ -5861,6 +5861,32 @@
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/opensearch'
|
||||
version: 2
|
||||
- table:
|
||||
name: phone_number_opt_out
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: bodyshop
|
||||
using:
|
||||
foreign_key_constraint_on: bodyshopid
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- phone_number
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
comment: ""
|
||||
- table:
|
||||
name: phonebook
|
||||
schema: public
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."bodyshops" add column "enforce_sms_consent" boolean
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" add column "enforce_sms_consent" boolean
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."phone_number_consent";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE "public"."phone_number_consent" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "bodyshopid" uuid NOT NULL, "phone_number" text NOT NULL, "consent_status" boolean NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "consent_updated_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"), UNIQUE ("bodyshopid", "phone_number"));
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."phone_number_consent_history";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE "public"."phone_number_consent_history" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "phone_number_consent_id" uuid NOT NULL, "old_value" boolean NOT NULL, "new_value" boolean NOT NULL, "reason" text NOT NULL, "changed_at" timestamptz NOT NULL DEFAULT now(), "changed_by" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("phone_number_consent_id") REFERENCES "public"."phone_number_consent"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_consent_history" alter column "old_value" set not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_consent_history" alter column "old_value" drop not null;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- DROP table "public"."phone_number_consent_history";
|
||||
@@ -0,0 +1 @@
|
||||
DROP table "public"."phone_number_consent_history";
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."phone_number_consent" alter column "consent_status" drop not null;
|
||||
alter table "public"."phone_number_consent" add column "consent_status" bool;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_consent" drop column "consent_status" cascade;
|
||||
@@ -0,0 +1,3 @@
|
||||
alter table "public"."phone_number_consent" alter column "consent_updated_at" set default now();
|
||||
alter table "public"."phone_number_consent" alter column "consent_updated_at" drop not null;
|
||||
alter table "public"."phone_number_consent" add column "consent_updated_at" timestamptz;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_consent" drop column "consent_updated_at" cascade;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_opt_out" rename to "phone_number_consent";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."phone_number_consent" rename to "phone_number_opt_out";
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."bodyshops" alter column "enforce_sms_consent" drop not null;
|
||||
alter table "public"."bodyshops" add column "enforce_sms_consent" bool;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."bodyshops" drop column "enforce_sms_consent" cascade;
|
||||
1535
package-lock.json
generated
1535
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -16,14 +16,14 @@
|
||||
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.808.0",
|
||||
"@aws-sdk/client-elasticache": "^3.808.0",
|
||||
"@aws-sdk/client-s3": "^3.808.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.808.0",
|
||||
"@aws-sdk/client-ses": "^3.808.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.808.0",
|
||||
"@aws-sdk/lib-storage": "^3.808.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.808.0",
|
||||
"@aws-sdk/client-cloudwatch-logs": "^3.812.0",
|
||||
"@aws-sdk/client-elasticache": "^3.812.0",
|
||||
"@aws-sdk/client-s3": "^3.812.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.812.0",
|
||||
"@aws-sdk/client-ses": "^3.812.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.812.0",
|
||||
"@aws-sdk/lib-storage": "^3.812.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.812.0",
|
||||
"@opensearch-project/opensearch": "^2.13.0",
|
||||
"@socket.io/admin-ui": "^0.5.1",
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
@@ -31,14 +31,14 @@
|
||||
"aws4": "^1.13.2",
|
||||
"axios": "^1.8.4",
|
||||
"better-queue": "^3.8.12",
|
||||
"bullmq": "^5.52.2",
|
||||
"bullmq": "^5.53.0",
|
||||
"chart.js": "^4.4.8",
|
||||
"cloudinary": "^2.6.1",
|
||||
"compression": "^1.8.0",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "2.8.5",
|
||||
"cors": "^2.8.5",
|
||||
"crisp-status-reporter": "^1.2.2",
|
||||
"dd-trace": "^5.51.0",
|
||||
"dd-trace": "^5.52.0",
|
||||
"dinero.js": "^1.9.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.21.1",
|
||||
@@ -57,7 +57,7 @@
|
||||
"node-persist": "^4.0.4",
|
||||
"nodemailer": "^6.10.0",
|
||||
"phone": "^3.1.58",
|
||||
"query-string": "^9.1.2",
|
||||
"query-string": "7.1.3",
|
||||
"recursive-diff": "^1.0.9",
|
||||
"rimraf": "^6.0.1",
|
||||
"skia-canvas": "^2.0.2",
|
||||
@@ -73,14 +73,14 @@
|
||||
"xmlbuilder2": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.26.0",
|
||||
"eslint": "^9.26.0",
|
||||
"@eslint/js": "^9.27.0",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^15.15.0",
|
||||
"mock-require": "^3.0.3",
|
||||
"p-limit": "^3.1.0",
|
||||
"prettier": "^3.5.3",
|
||||
"supertest": "^7.1.1",
|
||||
"vitest": "^3.1.3"
|
||||
"vitest": "^3.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ require("dotenv").config({
|
||||
function urlBuilder(realmId, object, query = null) {
|
||||
return `https://${
|
||||
process.env.NODE_ENV === "production" ? "" : "sandbox-"
|
||||
}quickbooks.api.intuit.com/v3/company/${realmId}/${object}${query ? `?query=${encodeURIComponent(query)}` : ""}`;
|
||||
}quickbooks.api.intuit.com/v3/company/${realmId}/${object}?minorversion=75${query ? `&query=${encodeURIComponent(query)}` : ""}`;
|
||||
}
|
||||
|
||||
function StandardizeName(str) {
|
||||
|
||||
@@ -4,4 +4,5 @@ exports.chatter = require("./chatter").default;
|
||||
exports.claimscorp = require("./claimscorp").default;
|
||||
exports.kaizen = require("./kaizen").default;
|
||||
exports.usageReport = require("./usageReport").default;
|
||||
exports.podium = require("./podium").default;
|
||||
exports.podium = require("./podium").default;
|
||||
exports.emsUpload = require("./emsUpload").default;
|
||||
22
server/data/emsUpload.js
Normal file
22
server/data/emsUpload.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const moment = require("moment-timezone");
|
||||
const logger = require("../utils/logger");
|
||||
const s3Client = require("../utils/s3"); // Using the S3 client utilities with LocalStack support
|
||||
|
||||
const emsUpload = async (req, res) => {
|
||||
try {
|
||||
const { bodyshopid, ciecaid, clm_no, ownr_ln } = req.body;
|
||||
const presignedUrl = await s3Client.getPresignedUrl({
|
||||
bucketName: process.env.S3_EMS_UPLOAD_BUCKET,
|
||||
key: `${bodyshopid}/${ciecaid}-${clm_no}-${ownr_ln}-${moment().format("YYYY-MM-DD--HH-mm-ss")}.zip`
|
||||
});
|
||||
res.status(200).json({ presignedUrl });
|
||||
} catch (error) {
|
||||
logger.log("ems-upload-presign-error", "ERROR", req?.user?.email, null, {
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
res.status(500).json({ error: error.message, stack: error.stack });
|
||||
}
|
||||
};
|
||||
|
||||
exports.default = emsUpload;
|
||||
@@ -185,7 +185,7 @@ async function uploadViaSFTP(csvObj) {
|
||||
await sftp.connect(ftpSetup);
|
||||
|
||||
try {
|
||||
csvObj.result = await sftp.put(Buffer.from(csvObj.xml), `${csvObj.filename}`);
|
||||
csvObj.result = await sftp.put(Buffer.from(csvObj.csv), `${csvObj.filename}`);
|
||||
logger.log("podium-sftp-upload", "DEBUG", "api", csvObj.bodyshopid, {
|
||||
imexshopid: csvObj.imexshopid,
|
||||
filename: csvObj.filename,
|
||||
|
||||
@@ -1596,6 +1596,7 @@ query QUERY_JOB_COSTING_DETAILS($id: uuid!) {
|
||||
ca_customer_gst
|
||||
dms_allocation
|
||||
cieca_pfl
|
||||
cieca_stl
|
||||
materials
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
@@ -1712,6 +1713,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
|
||||
ca_customer_gst
|
||||
dms_allocation
|
||||
cieca_pfl
|
||||
cieca_stl
|
||||
materials
|
||||
joblines(where: {removed: {_eq: false}}) {
|
||||
id
|
||||
|
||||
@@ -567,6 +567,29 @@ function GenerateCostingData(job) {
|
||||
);
|
||||
}
|
||||
|
||||
if (InstanceManager({ imex: false, rome: true })) {
|
||||
const stlTowing = job.cieca_stl?.data.find((c) => c.ttl_type === "OTTW");
|
||||
const stlStorage = job.cieca_stl?.data.find((c) => c.ttl_type === "OTST");
|
||||
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = Dinero();
|
||||
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["TOW"]] = stlTowing
|
||||
? Dinero({ amount: Math.round(stlTowing.ttl_amt * 100) })
|
||||
: Dinero({
|
||||
amount: Math.round((job.towing_payable || 0) * 100)
|
||||
});
|
||||
|
||||
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]])
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = Dinero();
|
||||
|
||||
jobLineTotalsByProfitCenter.additional[defaultProfits["STO"]] = stlStorage
|
||||
? Dinero({ amount: Math.round(stlStorage.ttl_amt * 100) })
|
||||
: Dinero({
|
||||
amount: Math.round((job.storage_payable || 0) * 100)
|
||||
});
|
||||
}
|
||||
|
||||
//Is it a DMS Setup?
|
||||
const selectedDmsAllocationConfig =
|
||||
(job.bodyshop.md_responsibility_centers.dms_defaults &&
|
||||
|
||||
@@ -138,6 +138,9 @@ router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest);
|
||||
// Alert Check
|
||||
router.post("/alertcheck", eventAuthorizationMiddleware, alertCheck);
|
||||
|
||||
//EMS Upload
|
||||
router.post("/emsupload", validateFirebaseIdTokenMiddleware, data.emsUpload);
|
||||
|
||||
// Redis Cache Routes
|
||||
router.post("/bodyshop-cache", eventAuthorizationMiddleware, updateBodyshopCache);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const { status, markConversationRead } = require("../sms/status");
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
|
||||
// Twilio Webhook Middleware for production
|
||||
// TODO: Look into this because it technically is never validating anything
|
||||
// TODO: This is never actually doing anything, we should probably verify
|
||||
const twilioWebhookMiddleware = twilio.webhook({ validate: process.env.NODE_ENV === "PRODUCTION" });
|
||||
|
||||
router.post("/receive", twilioWebhookMiddleware, receive);
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const queries = require("../graphql-client/queries");
|
||||
const {
|
||||
FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID,
|
||||
UNARCHIVE_CONVERSATION,
|
||||
CREATE_CONVERSATION,
|
||||
INSERT_MESSAGE
|
||||
} = require("../graphql-client/queries");
|
||||
const { phone } = require("phone");
|
||||
const { admin } = require("../firebase/firebase-handler");
|
||||
const logger = require("../utils/logger");
|
||||
const InstanceManager = require("../utils/instanceMgr").default;
|
||||
|
||||
exports.receive = async (req, res) => {
|
||||
/**
|
||||
* Receive SMS messages from Twilio and process them
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
const receive = async (req, res) => {
|
||||
const {
|
||||
logger,
|
||||
ioRedis,
|
||||
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }
|
||||
} = req;
|
||||
@@ -20,7 +26,7 @@ exports.receive = async (req, res) => {
|
||||
msid: req.body.SmsMessageSid,
|
||||
text: req.body.Body,
|
||||
image: !!req.body.MediaUrl0,
|
||||
image_path: generateMediaArray(req.body)
|
||||
image_path: generateMediaArray(req.body, logger)
|
||||
};
|
||||
|
||||
logger.log("sms-inbound", "DEBUG", "api", null, loggerData);
|
||||
@@ -35,7 +41,7 @@ exports.receive = async (req, res) => {
|
||||
|
||||
try {
|
||||
// Step 1: Find the bodyshop and existing conversation
|
||||
const response = await client.request(queries.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, {
|
||||
const response = await client.request(FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, {
|
||||
mssid: req.body.MessagingServiceSid,
|
||||
phone: phone(req.body.From).phoneNumber
|
||||
});
|
||||
@@ -46,7 +52,7 @@ exports.receive = async (req, res) => {
|
||||
|
||||
const bodyshop = response.bodyshops[0];
|
||||
|
||||
// Sort conversations by `updated_at` (or `created_at`) and pick the last one
|
||||
// Step 4: Process conversation
|
||||
const sortedConversations = bodyshop.conversations.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
|
||||
const existingConversation = sortedConversations.length
|
||||
? sortedConversations[sortedConversations.length - 1]
|
||||
@@ -57,25 +63,21 @@ exports.receive = async (req, res) => {
|
||||
msid: req.body.SmsMessageSid,
|
||||
text: req.body.Body,
|
||||
image: !!req.body.MediaUrl0,
|
||||
image_path: generateMediaArray(req.body),
|
||||
image_path: generateMediaArray(req.body, logger),
|
||||
isoutbound: false,
|
||||
userid: null // Add additional fields as necessary
|
||||
userid: null
|
||||
};
|
||||
|
||||
if (existingConversation) {
|
||||
// Use the existing conversation
|
||||
conversationid = existingConversation.id;
|
||||
|
||||
// Unarchive the conversation if necessary
|
||||
if (existingConversation.archived) {
|
||||
await client.request(queries.UNARCHIVE_CONVERSATION, {
|
||||
await client.request(UNARCHIVE_CONVERSATION, {
|
||||
id: conversationid,
|
||||
archived: false
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Create a new conversation
|
||||
const newConversationResponse = await client.request(queries.CREATE_CONVERSATION, {
|
||||
const newConversationResponse = await client.request(CREATE_CONVERSATION, {
|
||||
conversation: {
|
||||
bodyshopid: bodyshop.id,
|
||||
phone_num: phone(req.body.From).phoneNumber,
|
||||
@@ -86,13 +88,12 @@ exports.receive = async (req, res) => {
|
||||
conversationid = createdConversation.id;
|
||||
}
|
||||
|
||||
// Ensure `conversationid` is added to the message
|
||||
newMessage.conversationid = conversationid;
|
||||
|
||||
// Step 3: Insert the message into the conversation
|
||||
const insertresp = await client.request(queries.INSERT_MESSAGE, {
|
||||
// Step 5: Insert the message
|
||||
const insertresp = await client.request(INSERT_MESSAGE, {
|
||||
msg: newMessage,
|
||||
conversationid: conversationid
|
||||
conversationid
|
||||
});
|
||||
|
||||
const message = insertresp?.insert_messages?.returning?.[0];
|
||||
@@ -102,8 +103,7 @@ exports.receive = async (req, res) => {
|
||||
throw new Error("Conversation data is missing from the response.");
|
||||
}
|
||||
|
||||
// Step 4: Notify clients through Redis
|
||||
const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id);
|
||||
// Step 6: Notify clients
|
||||
const conversationRoom = getBodyshopConversationRoom({
|
||||
bodyshopId: conversation.bodyshop.id,
|
||||
conversationId: conversation.id
|
||||
@@ -116,6 +116,8 @@ exports.receive = async (req, res) => {
|
||||
msid: message.sid
|
||||
};
|
||||
|
||||
const broadcastRoom = getBodyshopRoom(conversation.bodyshop.id);
|
||||
|
||||
ioRedis.to(broadcastRoom).emit("new-message-summary", {
|
||||
...commonPayload,
|
||||
existingConversation: !!existingConversation,
|
||||
@@ -131,13 +133,13 @@ exports.receive = async (req, res) => {
|
||||
summary: false
|
||||
});
|
||||
|
||||
// Step 5: Send FCM notification
|
||||
// Step 7: Send FCM notification
|
||||
const fcmresp = await admin.messaging().send({
|
||||
topic: `${message.conversation.bodyshop.imexshopid}-messaging`,
|
||||
notification: {
|
||||
title: InstanceManager({
|
||||
imex: `ImEX Online Message - ${message.conversation.phone_num}`,
|
||||
rome: `Rome Online Message - ${message.conversation.phone_num}`,
|
||||
rome: `Rome Online Message - ${message.conversation.phone_num}`
|
||||
}),
|
||||
body: message.image_path ? `Image ${message.text}` : message.text
|
||||
},
|
||||
@@ -157,11 +159,17 @@ exports.receive = async (req, res) => {
|
||||
|
||||
res.status(200).send("");
|
||||
} catch (e) {
|
||||
handleError(req, e, res, "RECEIVE_MESSAGE");
|
||||
handleError(req, e, res, "RECEIVE_MESSAGE", logger);
|
||||
}
|
||||
};
|
||||
|
||||
const generateMediaArray = (body) => {
|
||||
/**
|
||||
* Generate media array from the request body
|
||||
* @param body
|
||||
* @param logger
|
||||
* @returns {null|*[]}
|
||||
*/
|
||||
const generateMediaArray = (body, logger) => {
|
||||
const { NumMedia } = body;
|
||||
if (parseInt(NumMedia) > 0) {
|
||||
const ret = [];
|
||||
@@ -174,12 +182,20 @@ const generateMediaArray = (body) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleError = (req, error, res, context) => {
|
||||
/**
|
||||
* Handle error logging and response
|
||||
* @param req
|
||||
* @param error
|
||||
* @param res
|
||||
* @param context
|
||||
* @param logger
|
||||
*/
|
||||
const handleError = (req, error, res, context, logger) => {
|
||||
logger.log("sms-inbound-error", "ERROR", "api", null, {
|
||||
msid: req.body.SmsMessageSid,
|
||||
text: req.body.Body,
|
||||
image: !!req.body.MediaUrl0,
|
||||
image_path: generateMediaArray(req.body),
|
||||
image_path: generateMediaArray(req.body, logger),
|
||||
messagingServiceSid: req.body.MessagingServiceSid,
|
||||
context,
|
||||
error
|
||||
@@ -187,3 +203,7 @@ const handleError = (req, error, res, context) => {
|
||||
|
||||
res.status(500).json({ error: error.message || "Internal Server Error" });
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
receive
|
||||
};
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
const twilio = require("twilio");
|
||||
const { phone } = require("phone");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const logger = require("../utils/logger");
|
||||
const { INSERT_MESSAGE } = require("../graphql-client/queries");
|
||||
const client = twilio(process.env.TWILIO_AUTH_TOKEN, process.env.TWILIO_AUTH_KEY);
|
||||
const gqlClient = require("../graphql-client/graphql-client").client;
|
||||
|
||||
exports.send = async (req, res) => {
|
||||
/**
|
||||
* Send an outbound SMS message
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const send = async (req, res) => {
|
||||
const { to, messagingServiceSid, body, conversationid, selectedMedia, imexshopid } = req.body;
|
||||
const {
|
||||
ioRedis,
|
||||
logger,
|
||||
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }
|
||||
} = req;
|
||||
|
||||
@@ -25,8 +26,8 @@ exports.send = async (req, res) => {
|
||||
conversationid,
|
||||
isoutbound: true,
|
||||
userid: req.user.email,
|
||||
image: req.body.selectedMedia.length > 0,
|
||||
image_path: req.body.selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
image: selectedMedia.length > 0,
|
||||
image_path: selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
});
|
||||
|
||||
if (!to || !messagingServiceSid || (!body && selectedMedia.length === 0) || !conversationid) {
|
||||
@@ -38,8 +39,8 @@ exports.send = async (req, res) => {
|
||||
conversationid,
|
||||
isoutbound: true,
|
||||
userid: req.user.email,
|
||||
image: req.body.selectedMedia.length > 0,
|
||||
image_path: req.body.selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
image: selectedMedia.length > 0,
|
||||
image_path: selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
});
|
||||
res.status(400).json({ success: false, message: "Missing required parameter(s)." });
|
||||
return;
|
||||
@@ -59,12 +60,15 @@ exports.send = async (req, res) => {
|
||||
conversationid,
|
||||
isoutbound: true,
|
||||
userid: req.user.email,
|
||||
image: req.body.selectedMedia.length > 0,
|
||||
image_path: req.body.selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
image: selectedMedia.length > 0,
|
||||
image_path: selectedMedia.length > 0 ? selectedMedia.map((i) => i.src) : []
|
||||
};
|
||||
|
||||
try {
|
||||
const gqlResponse = await gqlClient.request(queries.INSERT_MESSAGE, { msg: newMessage, conversationid });
|
||||
const gqlResponse = await gqlClient.request(INSERT_MESSAGE, {
|
||||
msg: newMessage,
|
||||
conversationid
|
||||
});
|
||||
|
||||
logger.log("sms-outbound-success", "DEBUG", req.user.email, null, {
|
||||
msid: message.sid,
|
||||
@@ -111,3 +115,7 @@ exports.send = async (req, res) => {
|
||||
res.status(500).json({ success: false, message: "Failed to send message through Twilio." });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
send
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||
});
|
||||
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const queries = require("../graphql-client/queries");
|
||||
const { UPDATE_MESSAGE_STATUS, MARK_MESSAGES_AS_READ } = require("../graphql-client/queries");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
exports.status = async (req, res) => {
|
||||
/**
|
||||
* Handle the status of an SMS message
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
const status = async (req, res) => {
|
||||
const { SmsSid, SmsStatus } = req.body;
|
||||
const {
|
||||
ioRedis,
|
||||
@@ -21,7 +22,7 @@ exports.status = async (req, res) => {
|
||||
}
|
||||
|
||||
// Update message status in the database
|
||||
const response = await client.request(queries.UPDATE_MESSAGE_STATUS, {
|
||||
const response = await client.request(UPDATE_MESSAGE_STATUS, {
|
||||
msid: SmsSid,
|
||||
fields: { status: SmsStatus }
|
||||
});
|
||||
@@ -65,7 +66,13 @@ exports.status = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
exports.markConversationRead = async (req, res) => {
|
||||
/**
|
||||
* Mark a conversation as read
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
const markConversationRead = async (req, res) => {
|
||||
const {
|
||||
ioRedis,
|
||||
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom }
|
||||
@@ -80,7 +87,7 @@ exports.markConversationRead = async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await client.request(queries.MARK_MESSAGES_AS_READ, {
|
||||
const response = await client.request(MARK_MESSAGES_AS_READ, {
|
||||
conversationId
|
||||
});
|
||||
|
||||
@@ -104,3 +111,8 @@ exports.markConversationRead = async (req, res) => {
|
||||
res.status(500).json({ error: "Failed to mark conversation as read." });
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
status,
|
||||
markConversationRead
|
||||
};
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* @module ioHelpers
|
||||
* @param app
|
||||
* @param api
|
||||
* @param io
|
||||
* @param logger
|
||||
* @returns {{getBodyshopRoom: (function(*): string), getBodyshopConversationRoom: (function({bodyshopId: *, conversationId: *}): string)}}
|
||||
*/
|
||||
const applyIOHelpers = ({ app, api, io, logger }) => {
|
||||
// Global Bodyshop Room
|
||||
const getBodyshopRoom = (bodyshopId) => `bodyshop-broadcast-room:${bodyshopId}`;
|
||||
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
const { defaultProvider } = require("@aws-sdk/credential-provider-node");
|
||||
const { InstanceRegion } = require("./instanceMgr");
|
||||
const { isString, isEmpty } = require("lodash");
|
||||
const { getSignedUrl } = require("@aws-sdk/s3-request-presigner");
|
||||
|
||||
const createS3Client = () => {
|
||||
const S3Options = {
|
||||
@@ -95,6 +96,17 @@ const createS3Client = () => {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getPresignedUrl = async ({ bucketName, key }) => {
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: bucketName,
|
||||
Key: key,
|
||||
StorageClass: "INTELLIGENT_TIERING"
|
||||
});
|
||||
const presignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 360 });
|
||||
return presignedUrl;
|
||||
}
|
||||
|
||||
return {
|
||||
uploadFileToS3,
|
||||
downloadFileFromS3,
|
||||
@@ -102,8 +114,12 @@ const createS3Client = () => {
|
||||
deleteFileFromS3,
|
||||
copyFileInS3,
|
||||
fileExistsInS3,
|
||||
getPresignedUrl,
|
||||
...s3Client
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = createS3Client();
|
||||
|
||||
Reference in New Issue
Block a user