Compare commits
56 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28abd9707e | ||
|
|
5f621e1ae0 | ||
|
|
fbd6766dcd | ||
|
|
9ace531edb | ||
|
|
9c59fd4c00 | ||
|
|
a9f959cced | ||
|
|
414897bba0 | ||
|
|
7467a31d76 | ||
|
|
894f6bf6d2 | ||
|
|
744dfa8163 | ||
|
|
2293119518 | ||
|
|
bd529a0dfa | ||
|
|
57ad89747f | ||
|
|
3ae8f38adb | ||
|
|
dc5ed1a39c | ||
|
|
aa6e6b8980 | ||
|
|
1dc80c068b | ||
|
|
bd0c4ceae2 | ||
|
|
30b58c6ea5 | ||
|
|
a55e9224f8 | ||
|
|
0c80abb3ca | ||
|
|
7137e611cd | ||
|
|
6f9d291d36 | ||
|
|
f2a2653eae | ||
|
|
73c25ab91f | ||
|
|
780449bac6 | ||
|
|
2509a1ecf3 | ||
|
|
16075f7ddd | ||
|
|
27d28e7ffc | ||
|
|
66b87e5c45 | ||
|
|
c1e1dff7d2 | ||
|
|
f76eb7abf5 | ||
|
|
25ea2a80a3 | ||
|
|
633d5668f0 | ||
|
|
00cc47553b | ||
|
|
3c360130a3 | ||
|
|
13e4143eeb | ||
|
|
68c7b184d2 | ||
|
|
9b85d15ff1 | ||
|
|
e7cf49a2ec | ||
|
|
04b29b6970 | ||
|
|
f5bc79cba7 | ||
|
|
2ae18681cb | ||
|
|
fda763476a | ||
|
|
999cbd80f4 | ||
|
|
ad2a5fe95b | ||
|
|
d835021069 | ||
|
|
c4b303aee1 | ||
|
|
e2c5a4cba4 | ||
|
|
fd04125ed1 | ||
|
|
a0566e76ab | ||
|
|
87e8b2ce27 | ||
|
|
d52426f5f5 | ||
|
|
5e24404e82 | ||
|
|
858a11f8b4 | ||
|
|
3fe0e3a33c |
385
client/package-lock.json
generated
385
client/package-lock.json
generated
@@ -14,18 +14,18 @@
|
|||||||
"@emotion/is-prop-valid": "^1.3.1",
|
"@emotion/is-prop-valid": "^1.3.1",
|
||||||
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
||||||
"@firebase/analytics": "^0.10.16",
|
"@firebase/analytics": "^0.10.16",
|
||||||
"@firebase/app": "^0.13.0",
|
"@firebase/app": "^0.13.1",
|
||||||
"@firebase/auth": "^1.10.6",
|
"@firebase/auth": "^1.10.6",
|
||||||
"@firebase/firestore": "^4.7.16",
|
"@firebase/firestore": "^4.7.17",
|
||||||
"@firebase/messaging": "^0.12.21",
|
"@firebase/messaging": "^0.12.21",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@sentry/cli": "^2.46.0",
|
"@sentry/cli": "^2.46.0",
|
||||||
"@sentry/react": "^9.23.0",
|
"@sentry/react": "^9.27.0",
|
||||||
"@sentry/vite-plugin": "^3.5.0",
|
"@sentry/vite-plugin": "^3.5.0",
|
||||||
"@splitsoftware/splitio-react": "^2.1.1",
|
"@splitsoftware/splitio-react": "^2.3.1",
|
||||||
"@tanem/react-nprogress": "^5.0.53",
|
"@tanem/react-nprogress": "^5.0.53",
|
||||||
"antd": "^5.25.3",
|
"antd": "^5.25.4",
|
||||||
"apollo-link-logger": "^2.0.1",
|
"apollo-link-logger": "^2.0.1",
|
||||||
"apollo-link-sentry": "^4.3.0",
|
"apollo-link-sentry": "^4.3.0",
|
||||||
"autosize": "^6.0.1",
|
"autosize": "^6.0.1",
|
||||||
@@ -42,18 +42,18 @@
|
|||||||
"i18next": "^24.2.3",
|
"i18next": "^24.2.3",
|
||||||
"i18next-browser-languagedetector": "^8.1.0",
|
"i18next-browser-languagedetector": "^8.1.0",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
"libphonenumber-js": "^1.12.8",
|
"libphonenumber-js": "^1.12.9",
|
||||||
"logrocket": "^9.0.2",
|
"logrocket": "^9.0.2",
|
||||||
"markerjs2": "^2.32.4",
|
"markerjs2": "^2.32.4",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"normalize-url": "^8.0.1",
|
"normalize-url": "^8.0.2",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"phone": "^3.1.59",
|
"phone": "^3.1.59",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"query-string": "^9.2.0",
|
"query-string": "^9.2.0",
|
||||||
"raf-schd": "^4.0.3",
|
"raf-schd": "^4.0.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-big-calendar": "^1.18.0",
|
"react-big-calendar": "^1.19.2",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-cookie": "^8.0.1",
|
"react-cookie": "^8.0.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
"react-resizable": "^3.0.5",
|
"react-resizable": "^3.0.5",
|
||||||
"react-router-dom": "^6.30.0",
|
"react-router-dom": "^6.30.0",
|
||||||
"react-sticky": "^6.0.3",
|
"react-sticky": "^6.0.3",
|
||||||
"react-virtuoso": "^4.12.7",
|
"react-virtuoso": "^4.12.8",
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.2",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-actions": "^3.0.3",
|
"redux-actions": "^3.0.3",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"redux-saga": "^1.3.0",
|
"redux-saga": "^1.3.0",
|
||||||
"redux-state-sync": "^3.1.4",
|
"redux-state-sync": "^3.1.4",
|
||||||
"reselect": "^5.1.1",
|
"reselect": "^5.1.1",
|
||||||
"sass": "^1.89.0",
|
"sass": "^1.89.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"styled-components": "^6.1.18",
|
"styled-components": "^6.1.18",
|
||||||
"subscriptions-transport-ws": "^0.11.0",
|
"subscriptions-transport-ws": "^0.11.0",
|
||||||
@@ -94,14 +94,14 @@
|
|||||||
"@dotenvx/dotenvx": "^1.44.1",
|
"@dotenvx/dotenvx": "^1.44.1",
|
||||||
"@emotion/babel-plugin": "^11.13.5",
|
"@emotion/babel-plugin": "^11.13.5",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@eslint/js": "^9.27.0",
|
"@eslint/js": "^9.28.0",
|
||||||
"@playwright/test": "^1.51.1",
|
"@playwright/test": "^1.51.1",
|
||||||
"@sentry/webpack-plugin": "^3.5.0",
|
"@sentry/webpack-plugin": "^3.5.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@vitejs/plugin-react": "^4.5.0",
|
"@vitejs/plugin-react": "^4.5.1",
|
||||||
"browserslist": "^4.24.5",
|
"browserslist": "^4.25.0",
|
||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
"vite-plugin-node-polyfills": "^0.23.0",
|
"vite-plugin-node-polyfills": "^0.23.0",
|
||||||
"vite-plugin-pwa": "^1.0.0",
|
"vite-plugin-pwa": "^1.0.0",
|
||||||
"vite-plugin-style-import": "^2.0.0",
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
"vitest": "^3.1.4",
|
"vitest": "^3.2.3",
|
||||||
"workbox-window": "^7.3.0"
|
"workbox-window": "^7.3.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2913,9 +2913,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/js": {
|
"node_modules/@eslint/js": {
|
||||||
"version": "9.27.0",
|
"version": "9.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz",
|
||||||
"integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==",
|
"integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -2951,9 +2951,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@firebase/app": {
|
"node_modules/@firebase/app": {
|
||||||
"version": "0.13.0",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.1.tgz",
|
||||||
"integrity": "sha512-Vj3MST245nq+V5UmmfEkB3isIgPouyUr8yGJlFeL9Trg/umG5ogAvrjAYvQ8gV7daKDoQSRnJKWI2JFpQqRsuQ==",
|
"integrity": "sha512-0O33PKrXLoIWkoOO5ByFaLjZehBctSYWnb+xJkIdx2SKP/K9l1UPFXPwASyrOIqyY3ws+7orF/1j7wI5EKzPYQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@firebase/component": "0.6.17",
|
"@firebase/component": "0.6.17",
|
||||||
@@ -3004,9 +3004,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@firebase/firestore": {
|
"node_modules/@firebase/firestore": {
|
||||||
"version": "4.7.16",
|
"version": "4.7.17",
|
||||||
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.16.tgz",
|
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.17.tgz",
|
||||||
"integrity": "sha512-5OpvlwYVUTLEnqewOlXmtIpH8t2ISlZHDW0NDbKROM2D0ATMqFkMHdvl+/wz9zOAcb8GMQYlhCihOnVAliUbpQ==",
|
"integrity": "sha512-YhXWA7HlSnekExhZ5u4i0e+kpPxsh/qMrzeNDgsAva71JXK8OOuOx+yLyYBFhmu3Hr5JJDO2fsZA/wrWoQYHDg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@firebase/component": "0.6.17",
|
"@firebase/component": "0.6.17",
|
||||||
@@ -4469,50 +4469,50 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/browser-utils": {
|
"node_modules/@sentry-internal/browser-utils": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-9.27.0.tgz",
|
||||||
"integrity": "sha512-hyN2Q6mh7ggw8sDVHeRyWz5LR6gjvf8zHSzQnMaF7QkeSyaeGM/SVSL4ODwqR9TRH7U2ku6nZFMbKhaBPV+Hfg==",
|
"integrity": "sha512-SJa7f6Ct1BzP8rWEomnshSGN1CmT+axNKvT+StrbFPD6AyHnYfFLJpKgc2iToIJHB/pmeuOI9dUwqtzVx+5nSw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "9.23.0"
|
"@sentry/core": "9.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/feedback": {
|
"node_modules/@sentry-internal/feedback": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-9.27.0.tgz",
|
||||||
"integrity": "sha512-Xf+KqV69TBiPo1gk2EsU6O/dumuTMxWOF52uVWJddQYI3pQTU5DqSeoZ5AY76bIIhV9n6AEFDGqNPXmuj4Acrw==",
|
"integrity": "sha512-e7L8eG0y63RulN352lmafoCCfQGg4jLVT8YLx6096eWu/YKLkgmVpgi8livsT5WREnH+HB+iFSrejOwK7cRkhw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/core": "9.23.0"
|
"@sentry/core": "9.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay": {
|
"node_modules/@sentry-internal/replay": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-9.27.0.tgz",
|
||||||
"integrity": "sha512-0/q15tvSboaK7/05BFQhs71bqgHKejJoDJgXmH0lySqgsRh/S18867ZxQNiuYhuVt337h07u1QaCyjnNJKHfuA==",
|
"integrity": "sha512-n2kO1wOfCG7GxkMAqbYYkpgTqJM5tuVLdp0JuNCqTOLTXWvw6svWGaYKlYpKUgsK9X/GDzJYSXZmfe+Dbg+FJQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "9.23.0",
|
"@sentry-internal/browser-utils": "9.27.0",
|
||||||
"@sentry/core": "9.23.0"
|
"@sentry/core": "9.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry-internal/replay-canvas": {
|
"node_modules/@sentry-internal/replay-canvas": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-9.27.0.tgz",
|
||||||
"integrity": "sha512-cYlw5svJjyPequm0PJjFGLpee86L1NieONEHlujOXkIG6IEriiorMm+8bNpGsHRuyvg41B+4P/YmcQAGtEGxXg==",
|
"integrity": "sha512-44rVSt3LCH6qePYRQrl4WUBwnkOk9dzinmnKmuwRksEdDOkVq5KBRhi/IDr7omwSpX8C+KrX5alfKhOx1cP0gQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/replay": "9.23.0",
|
"@sentry-internal/replay": "9.27.0",
|
||||||
"@sentry/core": "9.23.0"
|
"@sentry/core": "9.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -4528,16 +4528,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/browser": {
|
"node_modules/@sentry/browser": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-9.27.0.tgz",
|
||||||
"integrity": "sha512-QRkNxWys8e088260vByztoTsEVZ0W6v/XnZfKT6wkPPGn0aFeOrg/xjgxfI8D5huqZCxT28Cf23akOOly4FXjg==",
|
"integrity": "sha512-geR3lhRJOmUQqi1WgovLSYcD/f66zYnctdnDEa7j1BW2XIB1nlTJn0mpYyAHghXKkUN/pBpp1Z+Jk0XlVwFYVg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry-internal/browser-utils": "9.23.0",
|
"@sentry-internal/browser-utils": "9.27.0",
|
||||||
"@sentry-internal/feedback": "9.23.0",
|
"@sentry-internal/feedback": "9.27.0",
|
||||||
"@sentry-internal/replay": "9.23.0",
|
"@sentry-internal/replay": "9.27.0",
|
||||||
"@sentry-internal/replay-canvas": "9.23.0",
|
"@sentry-internal/replay-canvas": "9.27.0",
|
||||||
"@sentry/core": "9.23.0"
|
"@sentry/core": "9.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -4914,22 +4914,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/core": {
|
"node_modules/@sentry/core": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-9.27.0.tgz",
|
||||||
"integrity": "sha512-9846pn/BvASGgl7WsnKY4xry98WreP9ToeLfCQTQOf+pNfD/qNPn1/0xPInGni3LVMAXRtfHHMPm2Ghz255N7A==",
|
"integrity": "sha512-Zb2SSAdWXQjTem+sVWrrAq9L6YYfxyoTwtapaE6C6qZBR5C8Uak0wcYww8StaCFH7dDA/PSW+VxOwjNXocrQHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sentry/react": {
|
"node_modules/@sentry/react": {
|
||||||
"version": "9.23.0",
|
"version": "9.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-9.27.0.tgz",
|
||||||
"integrity": "sha512-2J/oOx8jd7Jr2koYIe5IcJyStHBXpjkQnxawo54Zyyvzc96MftyM2Dv5TeYdz7fChU1NIXw7BVbEpkQ9XEQlqg==",
|
"integrity": "sha512-UT7iaGEwTqe06O4mgHfKGTRBHg+U0JSI/id+QxrOji6ksosOsSnSC3Vdq+gPs9pzCCFE+6+DkH6foYNNLIN0lw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/browser": "9.23.0",
|
"@sentry/browser": "9.27.0",
|
||||||
"@sentry/core": "9.23.0",
|
"@sentry/core": "9.27.0",
|
||||||
"hoist-non-react-statics": "^3.3.2"
|
"hoist-non-react-statics": "^3.3.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -4977,12 +4977,12 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@splitsoftware/splitio": {
|
"node_modules/@splitsoftware/splitio": {
|
||||||
"version": "11.2.0",
|
"version": "11.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio/-/splitio-11.4.1.tgz",
|
||||||
"integrity": "sha512-M0TK8jlhLBv4+PchzBvn5R33MZzvRTInauGKGeaTpbxI+zq/g58meaNEiLRJxNw1lAWOjPhPRLIVg+V3Mf+uaA==",
|
"integrity": "sha512-wipPwsWwXPRzvEs28VYahILsF8+Lor4tby2GB3CD9kn0C3sQ2Zf3/NaDH4i7acobMlRy2sQ5mu4eeRt15gLJyw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@splitsoftware/splitio-commons": "2.2.0",
|
"@splitsoftware/splitio-commons": "2.4.1",
|
||||||
"bloom-filters": "^3.0.4",
|
"bloom-filters": "^3.0.4",
|
||||||
"ioredis": "^4.28.0",
|
"ioredis": "^4.28.0",
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
@@ -4995,9 +4995,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@splitsoftware/splitio-commons": {
|
"node_modules/@splitsoftware/splitio-commons": {
|
||||||
"version": "2.2.0",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.1.tgz",
|
||||||
"integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==",
|
"integrity": "sha512-VcbWpPykfx19LTJ0yeZbV0u3PUIt8MuiZ2a8zqkNf9KnDnhau/XxS/ctoO5jYrg4Nk2rCi0fpt1TkTstqzbaYA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/ioredis": "^4.28.0",
|
"@types/ioredis": "^4.28.0",
|
||||||
@@ -5013,12 +5013,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@splitsoftware/splitio-react": {
|
"node_modules/@splitsoftware/splitio-react": {
|
||||||
"version": "2.2.0",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio-react/-/splitio-react-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@splitsoftware/splitio-react/-/splitio-react-2.3.1.tgz",
|
||||||
"integrity": "sha512-OvGHtnrcJaBaY1Ec5skXWTiVCof8PsP6I4/g37BAgmvJEpLj6tgC5BDC4oyUWRnQOf8P7pqGtW0Qzk9wjJrpVQ==",
|
"integrity": "sha512-s1+IbKvK2HbQykjJKgezBBg076ZsT6f470FJn68N5DtgsVn0QXIwpTZ+j3k78x4ISVZcXTpY0GgWohZ+lujPlg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@splitsoftware/splitio": "11.2.0",
|
"@splitsoftware/splitio": "11.4.1",
|
||||||
"memoize-one": "^5.1.1",
|
"memoize-one": "^5.1.1",
|
||||||
"shallowequal": "^1.1.0",
|
"shallowequal": "^1.1.0",
|
||||||
"tslib": "^2.3.1"
|
"tslib": "^2.3.1"
|
||||||
@@ -5273,6 +5273,16 @@
|
|||||||
"@babel/types": "^7.20.7"
|
"@babel/types": "^7.20.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/chai": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/deep-eql": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/d3-array": {
|
"node_modules/@types/d3-array": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||||
@@ -5345,6 +5355,13 @@
|
|||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/deep-eql": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "8.56.12",
|
"version": "8.56.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
|
||||||
@@ -5805,9 +5822,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-react": {
|
"node_modules/@vitejs/plugin-react": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.1.tgz",
|
||||||
"integrity": "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==",
|
"integrity": "sha512-uPZBqSI0YD4lpkIru6M35sIfylLGTyhGHvDZbNLuMA73lMlwJKz5xweH7FajfcCAc2HnINciejA9qTz0dr0M7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5826,14 +5843,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/expect": {
|
"node_modules/@vitest/expect": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.3.tgz",
|
||||||
"integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==",
|
"integrity": "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.1.4",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/utils": "3.1.4",
|
"@vitest/spy": "3.2.3",
|
||||||
|
"@vitest/utils": "3.2.3",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -5842,13 +5860,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/mocker": {
|
"node_modules/@vitest/mocker": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.3.tgz",
|
||||||
"integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==",
|
"integrity": "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.1.4",
|
"@vitest/spy": "3.2.3",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"magic-string": "^0.30.17"
|
"magic-string": "^0.30.17"
|
||||||
},
|
},
|
||||||
@@ -5857,7 +5875,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"msw": "^2.4.9",
|
"msw": "^2.4.9",
|
||||||
"vite": "^5.0.0 || ^6.0.0"
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"msw": {
|
"msw": {
|
||||||
@@ -5889,9 +5907,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/pretty-format": {
|
"node_modules/@vitest/pretty-format": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.3.tgz",
|
||||||
"integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==",
|
"integrity": "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5902,14 +5920,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/runner": {
|
"node_modules/@vitest/runner": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.3.tgz",
|
||||||
"integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==",
|
"integrity": "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/utils": "3.1.4",
|
"@vitest/utils": "3.2.3",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3",
|
||||||
|
"strip-literal": "^3.0.0"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
@@ -5923,13 +5942,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/snapshot": {
|
"node_modules/@vitest/snapshot": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.3.tgz",
|
||||||
"integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==",
|
"integrity": "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.1.4",
|
"@vitest/pretty-format": "3.2.3",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
},
|
},
|
||||||
@@ -5955,26 +5974,26 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/spy": {
|
"node_modules/@vitest/spy": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.3.tgz",
|
||||||
"integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==",
|
"integrity": "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tinyspy": "^3.0.2"
|
"tinyspy": "^4.0.3"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vitest/utils": {
|
"node_modules/@vitest/utils": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.3.tgz",
|
||||||
"integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==",
|
"integrity": "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.1.4",
|
"@vitest/pretty-format": "3.2.3",
|
||||||
"loupe": "^3.1.3",
|
"loupe": "^3.1.3",
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -6104,9 +6123,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/antd": {
|
"node_modules/antd": {
|
||||||
"version": "5.25.3",
|
"version": "5.25.4",
|
||||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.25.3.tgz",
|
"resolved": "https://registry.npmjs.org/antd/-/antd-5.25.4.tgz",
|
||||||
"integrity": "sha512-tBBcAFRjmWM3sitxrL/FEbQL+MTQntYY5bGa5c1ZZZHXWCynkhS3Ch/gy25mGMUY1M/9Uw3pH029v/RGht1x3w==",
|
"integrity": "sha512-yXdWqq1NJSZnD1HoPZWnWuQJGVYYnB3h0Ufsz4sbt3T0N9SdJ4G9GPpLMk8Gn9zWtwBekfR4THPVZ9uzAyhBHQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^7.2.1",
|
"@ant-design/colors": "^7.2.1",
|
||||||
@@ -6128,7 +6147,7 @@
|
|||||||
"rc-checkbox": "~3.5.0",
|
"rc-checkbox": "~3.5.0",
|
||||||
"rc-collapse": "~3.9.0",
|
"rc-collapse": "~3.9.0",
|
||||||
"rc-dialog": "~9.6.0",
|
"rc-dialog": "~9.6.0",
|
||||||
"rc-drawer": "~7.2.0",
|
"rc-drawer": "~7.3.0",
|
||||||
"rc-dropdown": "~4.2.1",
|
"rc-dropdown": "~4.2.1",
|
||||||
"rc-field-form": "~2.7.0",
|
"rc-field-form": "~2.7.0",
|
||||||
"rc-image": "~7.12.0",
|
"rc-image": "~7.12.0",
|
||||||
@@ -6154,7 +6173,7 @@
|
|||||||
"rc-tooltip": "~6.4.0",
|
"rc-tooltip": "~6.4.0",
|
||||||
"rc-tree": "~5.13.1",
|
"rc-tree": "~5.13.1",
|
||||||
"rc-tree-select": "~5.27.0",
|
"rc-tree-select": "~5.27.0",
|
||||||
"rc-upload": "~4.9.0",
|
"rc-upload": "~4.9.2",
|
||||||
"rc-util": "^5.44.4",
|
"rc-util": "^5.44.4",
|
||||||
"scroll-into-view-if-needed": "^3.1.0",
|
"scroll-into-view-if-needed": "^3.1.0",
|
||||||
"throttle-debounce": "^5.0.2"
|
"throttle-debounce": "^5.0.2"
|
||||||
@@ -6982,9 +7001,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.24.5",
|
"version": "4.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz",
|
||||||
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
"integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -7001,8 +7020,8 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001716",
|
"caniuse-lite": "^1.0.30001718",
|
||||||
"electron-to-chromium": "^1.5.149",
|
"electron-to-chromium": "^1.5.160",
|
||||||
"node-releases": "^2.0.19",
|
"node-releases": "^2.0.19",
|
||||||
"update-browserslist-db": "^1.1.3"
|
"update-browserslist-db": "^1.1.3"
|
||||||
},
|
},
|
||||||
@@ -7181,9 +7200,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001717",
|
"version": "1.0.30001721",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz",
|
||||||
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==",
|
"integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -7978,9 +7997,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
@@ -8345,9 +8364,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.149",
|
"version": "1.5.165",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.149.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz",
|
||||||
"integrity": "sha512-UyiO82eb9dVOx8YO3ajDf9jz2kKyt98DEITRdeLPstOEuTlLzDA4Gyq5K9he71TQziU5jUVu2OAu5N48HmQiyQ==",
|
"integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/elliptic": {
|
"node_modules/elliptic": {
|
||||||
@@ -11480,9 +11499,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/libphonenumber-js": {
|
"node_modules/libphonenumber-js": {
|
||||||
"version": "1.12.8",
|
"version": "1.12.9",
|
||||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.8.tgz",
|
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.9.tgz",
|
||||||
"integrity": "sha512-f1KakiQJa9tdc7w1phC2ST+DyxWimy9c3g3yeF+84QtEanJr2K77wAmBPP22riU05xldniHsvXuflnLZ4oysqA==",
|
"integrity": "sha512-VWwAdNeJgN7jFOD+wN4qx83DTPMVPPAUyx9/TUkBXKLiNkuWWk6anV0439tgdtwaJDrEdqkvdN22iA6J4bUCZg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
@@ -12689,9 +12708,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/normalize-url": {
|
"node_modules/normalize-url": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz",
|
||||||
"integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
|
"integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.16"
|
"node": ">=14.16"
|
||||||
@@ -13742,9 +13761,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rc-drawer": {
|
"node_modules/rc-drawer": {
|
||||||
"version": "7.2.0",
|
"version": "7.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz",
|
||||||
"integrity": "sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg==",
|
"integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.23.9",
|
"@babel/runtime": "^7.23.9",
|
||||||
@@ -14235,9 +14254,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rc-upload": {
|
"node_modules/rc-upload": {
|
||||||
"version": "4.9.0",
|
"version": "4.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.9.2.tgz",
|
||||||
"integrity": "sha512-pAzlPnyiFn1GCtEybEG2m9nXNzQyWXqWV2xFYCmDxjN9HzyjS5Pz2F+pbNdYw8mMJsixLEKLG0wVy9vOGxJMJA==",
|
"integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.18.3",
|
"@babel/runtime": "^7.18.3",
|
||||||
@@ -14295,9 +14314,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-big-calendar": {
|
"node_modules/react-big-calendar": {
|
||||||
"version": "1.18.0",
|
"version": "1.19.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.19.2.tgz",
|
||||||
"integrity": "sha512-bGrCdyfnCGe2qnIdEoGkGgQdEFOiGO1Tq7RLkI1a2t8ZudyEAKekFtneO2/ltKQEQK6zH76YdJ7vR9UMyD+ULw==",
|
"integrity": "sha512-2orH+TOXPJBlQGwSl9ZnTK2WZR9OfVf0r1s8mnbpjvtENZfmWHP6nXqxmten1vkvzOMqefVGjh5GurM27HHOZw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.7",
|
"@babel/runtime": "^7.20.7",
|
||||||
@@ -14723,9 +14742,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-virtuoso": {
|
"node_modules/react-virtuoso": {
|
||||||
"version": "4.12.7",
|
"version": "4.12.8",
|
||||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.8.tgz",
|
||||||
"integrity": "sha512-njJp764he6Fi1p89PUW0k2kbyWu9w/y+MwdxmwK2kvdwwzVDbz2c2wMj5xdSruBFVgFTsI7Z85hxZR7aSHBrbQ==",
|
"integrity": "sha512-NMMKfDBr/+xZZqCQF3tN1SZsh6FwOJkYgThlfnsPLkaEhdyQo0EuWUzu3ix6qjnI7rYwJhMwRGoJBi+aiDfGsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16 || >=17 || >= 18 || >= 19",
|
"react": ">=16 || >=17 || >= 18 || >= 19",
|
||||||
@@ -15400,9 +15419,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.89.0",
|
"version": "1.89.1",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.89.1.tgz",
|
||||||
"integrity": "sha512-ld+kQU8YTdGNjOLfRWBzewJpU5cwEv/h5yyqlSeJcj6Yh8U4TDA9UA5FPicqDz/xgRPWRSYIQNiFks21TbA9KQ==",
|
"integrity": "sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
@@ -16281,6 +16300,26 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strip-literal": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"js-tokens": "^9.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-literal/node_modules/js-tokens": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/style-to-js": {
|
"node_modules/style-to-js": {
|
||||||
"version": "1.1.16",
|
"version": "1.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz",
|
||||||
@@ -16647,9 +16686,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.13",
|
"version": "0.2.14",
|
||||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -16664,9 +16703,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinypool": {
|
"node_modules/tinypool": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz",
|
||||||
"integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
|
"integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -16684,9 +16723,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tinyspy": {
|
"node_modules/tinyspy": {
|
||||||
"version": "3.0.2",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
|
||||||
"integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
|
"integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -17558,17 +17597,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite-node": {
|
"node_modules/vite-node": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.3.tgz",
|
||||||
"integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==",
|
"integrity": "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cac": "^6.7.14",
|
"cac": "^6.7.14",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.1",
|
||||||
"es-module-lexer": "^1.7.0",
|
"es-module-lexer": "^1.7.0",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
"vite": "^5.0.0 || ^6.0.0"
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite-node": "vite-node.mjs"
|
"vite-node": "vite-node.mjs"
|
||||||
@@ -17756,32 +17795,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vitest": {
|
"node_modules/vitest": {
|
||||||
"version": "3.1.4",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.3.tgz",
|
||||||
"integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==",
|
"integrity": "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/expect": "3.1.4",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/mocker": "3.1.4",
|
"@vitest/expect": "3.2.3",
|
||||||
"@vitest/pretty-format": "^3.1.4",
|
"@vitest/mocker": "3.2.3",
|
||||||
"@vitest/runner": "3.1.4",
|
"@vitest/pretty-format": "^3.2.3",
|
||||||
"@vitest/snapshot": "3.1.4",
|
"@vitest/runner": "3.2.3",
|
||||||
"@vitest/spy": "3.1.4",
|
"@vitest/snapshot": "3.2.3",
|
||||||
"@vitest/utils": "3.1.4",
|
"@vitest/spy": "3.2.3",
|
||||||
|
"@vitest/utils": "3.2.3",
|
||||||
"chai": "^5.2.0",
|
"chai": "^5.2.0",
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.1",
|
||||||
"expect-type": "^1.2.1",
|
"expect-type": "^1.2.1",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"pathe": "^2.0.3",
|
"pathe": "^2.0.3",
|
||||||
|
"picomatch": "^4.0.2",
|
||||||
"std-env": "^3.9.0",
|
"std-env": "^3.9.0",
|
||||||
"tinybench": "^2.9.0",
|
"tinybench": "^2.9.0",
|
||||||
"tinyexec": "^0.3.2",
|
"tinyexec": "^0.3.2",
|
||||||
"tinyglobby": "^0.2.13",
|
"tinyglobby": "^0.2.14",
|
||||||
"tinypool": "^1.0.2",
|
"tinypool": "^1.1.0",
|
||||||
"tinyrainbow": "^2.0.0",
|
"tinyrainbow": "^2.0.0",
|
||||||
"vite": "^5.0.0 || ^6.0.0",
|
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
|
||||||
"vite-node": "3.1.4",
|
"vite-node": "3.2.3",
|
||||||
"why-is-node-running": "^2.3.0"
|
"why-is-node-running": "^2.3.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -17797,8 +17838,8 @@
|
|||||||
"@edge-runtime/vm": "*",
|
"@edge-runtime/vm": "*",
|
||||||
"@types/debug": "^4.1.12",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
||||||
"@vitest/browser": "3.1.4",
|
"@vitest/browser": "3.2.3",
|
||||||
"@vitest/ui": "3.1.4",
|
"@vitest/ui": "3.2.3",
|
||||||
"happy-dom": "*",
|
"happy-dom": "*",
|
||||||
"jsdom": "*"
|
"jsdom": "*"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,18 +13,18 @@
|
|||||||
"@emotion/is-prop-valid": "^1.3.1",
|
"@emotion/is-prop-valid": "^1.3.1",
|
||||||
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
"@fingerprintjs/fingerprintjs": "^4.6.1",
|
||||||
"@firebase/analytics": "^0.10.16",
|
"@firebase/analytics": "^0.10.16",
|
||||||
"@firebase/app": "^0.13.0",
|
"@firebase/app": "^0.13.1",
|
||||||
"@firebase/auth": "^1.10.6",
|
"@firebase/auth": "^1.10.6",
|
||||||
"@firebase/firestore": "^4.7.16",
|
"@firebase/firestore": "^4.7.17",
|
||||||
"@firebase/messaging": "^0.12.21",
|
"@firebase/messaging": "^0.12.21",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@sentry/cli": "^2.46.0",
|
"@sentry/cli": "^2.46.0",
|
||||||
"@sentry/react": "^9.23.0",
|
"@sentry/react": "^9.27.0",
|
||||||
"@sentry/vite-plugin": "^3.5.0",
|
"@sentry/vite-plugin": "^3.5.0",
|
||||||
"@splitsoftware/splitio-react": "^2.1.1",
|
"@splitsoftware/splitio-react": "^2.3.1",
|
||||||
"@tanem/react-nprogress": "^5.0.53",
|
"@tanem/react-nprogress": "^5.0.53",
|
||||||
"antd": "^5.25.3",
|
"antd": "^5.25.4",
|
||||||
"apollo-link-logger": "^2.0.1",
|
"apollo-link-logger": "^2.0.1",
|
||||||
"apollo-link-sentry": "^4.3.0",
|
"apollo-link-sentry": "^4.3.0",
|
||||||
"autosize": "^6.0.1",
|
"autosize": "^6.0.1",
|
||||||
@@ -41,18 +41,18 @@
|
|||||||
"i18next": "^24.2.3",
|
"i18next": "^24.2.3",
|
||||||
"i18next-browser-languagedetector": "^8.1.0",
|
"i18next-browser-languagedetector": "^8.1.0",
|
||||||
"immutability-helper": "^3.1.1",
|
"immutability-helper": "^3.1.1",
|
||||||
"libphonenumber-js": "^1.12.8",
|
"libphonenumber-js": "^1.12.9",
|
||||||
"logrocket": "^9.0.2",
|
"logrocket": "^9.0.2",
|
||||||
"markerjs2": "^2.32.4",
|
"markerjs2": "^2.32.4",
|
||||||
"memoize-one": "^6.0.0",
|
"memoize-one": "^6.0.0",
|
||||||
"normalize-url": "^8.0.1",
|
"normalize-url": "^8.0.2",
|
||||||
"object-hash": "^3.0.0",
|
"object-hash": "^3.0.0",
|
||||||
"phone": "^3.1.59",
|
"phone": "^3.1.59",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"query-string": "^9.2.0",
|
"query-string": "^9.2.0",
|
||||||
"raf-schd": "^4.0.3",
|
"raf-schd": "^4.0.3",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-big-calendar": "^1.18.0",
|
"react-big-calendar": "^1.19.2",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-cookie": "^8.0.1",
|
"react-cookie": "^8.0.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
"react-resizable": "^3.0.5",
|
"react-resizable": "^3.0.5",
|
||||||
"react-router-dom": "^6.30.0",
|
"react-router-dom": "^6.30.0",
|
||||||
"react-sticky": "^6.0.3",
|
"react-sticky": "^6.0.3",
|
||||||
"react-virtuoso": "^4.12.7",
|
"react-virtuoso": "^4.12.8",
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.2",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-actions": "^3.0.3",
|
"redux-actions": "^3.0.3",
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
"redux-saga": "^1.3.0",
|
"redux-saga": "^1.3.0",
|
||||||
"redux-state-sync": "^3.1.4",
|
"redux-state-sync": "^3.1.4",
|
||||||
"reselect": "^5.1.1",
|
"reselect": "^5.1.1",
|
||||||
"sass": "^1.89.0",
|
"sass": "^1.89.1",
|
||||||
"socket.io-client": "^4.8.1",
|
"socket.io-client": "^4.8.1",
|
||||||
"styled-components": "^6.1.18",
|
"styled-components": "^6.1.18",
|
||||||
"subscriptions-transport-ws": "^0.11.0",
|
"subscriptions-transport-ws": "^0.11.0",
|
||||||
@@ -134,14 +134,14 @@
|
|||||||
"@dotenvx/dotenvx": "^1.44.1",
|
"@dotenvx/dotenvx": "^1.44.1",
|
||||||
"@emotion/babel-plugin": "^11.13.5",
|
"@emotion/babel-plugin": "^11.13.5",
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@eslint/js": "^9.27.0",
|
"@eslint/js": "^9.28.0",
|
||||||
"@playwright/test": "^1.51.1",
|
"@playwright/test": "^1.51.1",
|
||||||
"@sentry/webpack-plugin": "^3.5.0",
|
"@sentry/webpack-plugin": "^3.5.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@vitejs/plugin-react": "^4.5.0",
|
"@vitejs/plugin-react": "^4.5.1",
|
||||||
"browserslist": "^4.24.5",
|
"browserslist": "^4.25.0",
|
||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"chalk": "^5.4.1",
|
"chalk": "^5.4.1",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
"vite-plugin-node-polyfills": "^0.23.0",
|
"vite-plugin-node-polyfills": "^0.23.0",
|
||||||
"vite-plugin-pwa": "^1.0.0",
|
"vite-plugin-pwa": "^1.0.0",
|
||||||
"vite-plugin-style-import": "^2.0.0",
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
"vitest": "^3.1.4",
|
"vitest": "^3.2.3",
|
||||||
"workbox-window": "^7.3.0"
|
"workbox-window": "^7.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { useApolloClient, useMutation } from "@apollo/client";
|
|||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Checkbox, Form, Modal, Space } from "antd";
|
import { Button, Checkbox, Form, Modal, Space } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
|
||||||
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||||
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
|
||||||
@@ -24,7 +25,7 @@ import BillFormContainer from "../bill-form/bill-form.container";
|
|||||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { handleUpload as handleUploadToImageProxy } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
billEnterModal: selectBillEnterModal,
|
billEnterModal: selectBillEnterModal,
|
||||||
@@ -53,10 +54,10 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
treatments: { Enhanced_Payroll }
|
treatments: { Enhanced_Payroll, Imgproxy }
|
||||||
} = useSplitTreatments({
|
} = useSplitTreatments({
|
||||||
attributes: {},
|
attributes: {},
|
||||||
names: ["Enhanced_Payroll"],
|
names: ["Enhanced_Payroll", "Imgproxy"],
|
||||||
splitKey: bodyshop.imexshopid
|
splitKey: bodyshop.imexshopid
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -196,7 +197,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
job: { lbr_adjustments: newAdjustments }
|
job: { lbr_adjustments: newAdjustments }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!!jobUpdate.errors) {
|
if (jobUpdate.errors) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("jobs.errors.saving", {
|
message: t("jobs.errors.saving", {
|
||||||
message: JSON.stringify(jobUpdate.errors)
|
message: JSON.stringify(jobUpdate.errors)
|
||||||
@@ -213,7 +214,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
variables: { partsLineIds: markPolReceived.map((p) => p.id) },
|
variables: { partsLineIds: markPolReceived.map((p) => p.id) },
|
||||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"]
|
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"]
|
||||||
});
|
});
|
||||||
if (!!r2.errors) {
|
if (r2.errors) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setEnterAgain(false);
|
setEnterAgain(false);
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -224,7 +225,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!r1.errors) {
|
if (r1.errors) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setEnterAgain(false);
|
setEnterAgain(false);
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -244,7 +245,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
consumedbybillid: billId
|
consumedbybillid: billId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!!r2.errors) {
|
if (r2.errors) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setEnterAgain(false);
|
setEnterAgain(false);
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -298,20 +299,39 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
upload.forEach((u) => {
|
//Check if using Imgproxy or cloudinary
|
||||||
handleUpload(
|
|
||||||
{ file: u.originFileObj },
|
if (Imgproxy.treatment === "on") {
|
||||||
{
|
upload.forEach((u) => {
|
||||||
bodyshop: bodyshop,
|
handleUploadToImageProxy(
|
||||||
uploaded_by: currentUser.email,
|
{ file: u.originFileObj },
|
||||||
jobId: values.jobid,
|
{
|
||||||
billId: billId,
|
bodyshop: bodyshop,
|
||||||
tagsArray: null,
|
uploaded_by: currentUser.email,
|
||||||
callback: null
|
jobId: values.jobid,
|
||||||
},
|
billId: billId,
|
||||||
notification
|
tagsArray: null,
|
||||||
);
|
callback: null
|
||||||
});
|
},
|
||||||
|
notification
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
upload.forEach((u) => {
|
||||||
|
handleUpload(
|
||||||
|
{ file: u.originFileObj },
|
||||||
|
{
|
||||||
|
bodyshop: bodyshop,
|
||||||
|
uploaded_by: currentUser.email,
|
||||||
|
jobId: values.jobid,
|
||||||
|
billId: billId,
|
||||||
|
tagsArray: null,
|
||||||
|
callback: null
|
||||||
|
},
|
||||||
|
notification
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
@@ -396,7 +416,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
{t("bills.labels.generatepartslabel")}
|
{t("bills.labels.generatepartslabel")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
||||||
<Button loading={loading} onClick={() => form.submit()}>
|
<Button loading={loading} onClick={() => form.submit()} id="save-bill-enter-modal">
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
{billEnterModal.context && billEnterModal.context.id ? null : (
|
{billEnterModal.context && billEnterModal.context.id ? null : (
|
||||||
@@ -406,6 +426,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEnterAgain(true);
|
setEnterAgain(true);
|
||||||
}}
|
}}
|
||||||
|
id="save-and-new-bill-enter-modal"
|
||||||
>
|
>
|
||||||
{t("general.actions.saveandnew")}
|
{t("general.actions.saveandnew")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaTasks } from "react-icons/fa";
|
import { FaTasks } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -209,6 +209,7 @@ export function BillsListTableComponent({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
id="reconcile-bills-button"
|
||||||
>
|
>
|
||||||
<LockerWrapperComponent featureName="bills"> {t("jobs.actions.reconcile")}</LockerWrapperComponent>
|
<LockerWrapperComponent featureName="bills"> {t("jobs.actions.reconcile")}</LockerWrapperComponent>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
open={open}
|
open={open}
|
||||||
onOpenChange={handleVisibleChange}
|
onOpenChange={handleVisibleChange}
|
||||||
overlayClassName="media-selector-popover"
|
classNames={{ root: "media-selector-popover" }}
|
||||||
>
|
>
|
||||||
<Badge count={selectedMedia.filter((s) => s.isSelected).length}>
|
<Badge count={selectedMedia.filter((s) => s.isSelected).length}>
|
||||||
<PictureFilled style={{ margin: "0 .5rem" }} />
|
<PictureFilled style={{ margin: "0 .5rem" }} />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
.media-selector-popover {
|
.media-selector-popover {
|
||||||
.ant-popover-inner-content {
|
.ant-popover-inner-content {
|
||||||
|
position: relative;
|
||||||
max-width: 640px;
|
max-width: 640px;
|
||||||
max-height: 480px;
|
max-height: 480px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -36,11 +37,6 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: transform 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grid layout for gallery components */
|
/* Grid layout for gallery components */
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { DownloadOutlined, SyncOutlined } from "@ant-design/icons";
|
import { DownloadOutlined, SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
partnerVersion: selectPartnerVersion
|
partnerVersion: selectPartnerVersion
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAvailableScan);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsAvailableScan);
|
||||||
@@ -126,6 +126,7 @@ export function JobsAvailableScan({ partnerVersion, refetch }) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
scanEstimates();
|
scanEstimates();
|
||||||
}}
|
}}
|
||||||
|
id="scan-estimates-button"
|
||||||
>
|
>
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Card, Input, Table } from "antd";
|
import { Card, Input, Table } from "antd";
|
||||||
import React, { useContext, useState } from "react";
|
import { useContext, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
@@ -91,6 +91,7 @@ export default function JobsCreateOwnerInfoSearchComponent({ loading, owners })
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
enterButton
|
enterButton
|
||||||
|
id="search-owner"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -112,9 +113,9 @@ export default function JobsCreateOwnerInfoSearchComponent({ loading, owners })
|
|||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [state.owner.selectedid]
|
selectedRowKeys: [state.owner.selectedid]
|
||||||
}}
|
}}
|
||||||
onRow={(record, rowIndex) => {
|
onRow={(record) => {
|
||||||
return {
|
return {
|
||||||
onClick: (event) => {
|
onClick: () => {
|
||||||
if (record) {
|
if (record) {
|
||||||
if (record.id) {
|
if (record.id) {
|
||||||
setState({
|
setState({
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React, { useContext, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Card, Input, Space, Table } from "antd";
|
import { Card, Input, Space, Table } from "antd";
|
||||||
|
import { useContext, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
|
||||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||||
|
|
||||||
export default function JobsCreateVehicleInfoSearchComponent({ loading, vehicles }) {
|
export default function JobsCreateVehicleInfoSearchComponent({ loading, vehicles }) {
|
||||||
@@ -63,6 +63,7 @@ export default function JobsCreateVehicleInfoSearchComponent({ loading, vehicles
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
enterButton
|
enterButton
|
||||||
|
id="search-vehicle"
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
@@ -91,9 +92,9 @@ export default function JobsCreateVehicleInfoSearchComponent({ loading, vehicles
|
|||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [state.vehicle.selectedid]
|
selectedRowKeys: [state.vehicle.selectedid]
|
||||||
}}
|
}}
|
||||||
onRow={(record, rowIndex) => {
|
onRow={(record) => {
|
||||||
return {
|
return {
|
||||||
onClick: (event) => {
|
onClick: () => {
|
||||||
if (record) {
|
if (record) {
|
||||||
if (record.id) {
|
if (record.id) {
|
||||||
setState({
|
setState({
|
||||||
|
|||||||
@@ -335,7 +335,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail })
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...colSpan}>
|
<Col {...colSpan}>
|
||||||
<Card style={{ height: "100%" }} title={t("jobs.labels.employeeassignments")}>
|
<Card
|
||||||
|
style={{ height: "100%" }}
|
||||||
|
title=<span id="job-employee-assignments-title">{t("jobs.labels.employeeassignments")}</span>
|
||||||
|
id={"job-employee-assignments"}
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<JobEmployeeAssignments job={job} />
|
<JobEmployeeAssignments job={job} />
|
||||||
<Divider style={{ margin: ".5rem" }} />
|
<Divider style={{ margin: ".5rem" }} />
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function JobsDocumentsContainer({
|
|||||||
variables: { jobId: jobId },
|
variables: { jobId: jobId },
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
skip: Imgproxy.treatment === "on" || !!billId
|
skip: !!billId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
if (loading) return <LoadingSpinner />;
|
||||||
|
|||||||
@@ -46,32 +46,40 @@ export function JobsDocumentsImgproxyDownloadButton({ bodyshop, galleryImages, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
function standardMediaDownload(bufferData) {
|
function standardMediaDownload(bufferData) {
|
||||||
const a = document.createElement("a");
|
try {
|
||||||
const url = window.URL.createObjectURL(new Blob([bufferData]));
|
const a = document.createElement("a");
|
||||||
a.href = url;
|
const url = window.URL.createObjectURL(new Blob([bufferData]));
|
||||||
a.download = `${identifier || "documents"}.zip`;
|
a.href = url;
|
||||||
a.click();
|
a.download = `${identifier || "documents"}.zip`;
|
||||||
|
a.click();
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false);
|
||||||
|
setDownload(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownload = async () => {
|
const handleDownload = async () => {
|
||||||
logImEXEvent("jobs_documents_download");
|
logImEXEvent("jobs_documents_download");
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const zipUrl = await axios({
|
try {
|
||||||
url: "/media/imgproxy/download",
|
const response = await axios({
|
||||||
method: "POST",
|
url: "/media/imgproxy/download",
|
||||||
data: { jobId, documentids: imagesToDownload.map((_) => _.id) }
|
method: "POST",
|
||||||
});
|
responseType: "blob",
|
||||||
|
data: { jobId, documentids: imagesToDownload.map((_) => _.id) },
|
||||||
|
onDownloadProgress: downloadProgress
|
||||||
|
});
|
||||||
|
|
||||||
const theDownloadedZip = await cleanAxios({
|
setLoading(false);
|
||||||
url: zipUrl.data.url,
|
setDownload(null);
|
||||||
method: "GET",
|
|
||||||
responseType: "arraybuffer",
|
|
||||||
onDownloadProgress: downloadProgress
|
|
||||||
});
|
|
||||||
setLoading(false);
|
|
||||||
setDownload(null);
|
|
||||||
|
|
||||||
standardMediaDownload(theDownloadedZip.data);
|
// Use the response data (Blob) to trigger download
|
||||||
|
standardMediaDownload(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false);
|
||||||
|
setDownload(null);
|
||||||
|
// handle error (optional)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -98,7 +98,13 @@ function JobsDocumentsImgproxyComponent({
|
|||||||
jobId={jobId}
|
jobId={jobId}
|
||||||
totalSize={totalSize}
|
totalSize={totalSize}
|
||||||
billId={billId}
|
billId={billId}
|
||||||
callbackAfterUpload={billsCallback || fetchThumbnails || refetch}
|
callbackAfterUpload={
|
||||||
|
billsCallback ||
|
||||||
|
function () {
|
||||||
|
isFunction(refetch) && refetch();
|
||||||
|
isFunction(fetchThumbnails) && fetchThumbnails();
|
||||||
|
}
|
||||||
|
}
|
||||||
ignoreSizeLimit={ignoreSizeLimit}
|
ignoreSizeLimit={ignoreSizeLimit}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Checkbox, Divider, Input, Space, Table } from "antd";
|
import { Button, Checkbox, Divider, Input, Space, Table } from "antd";
|
||||||
import dayjs from "../../utils/day";
|
import { useState } from "react";
|
||||||
import React, { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
@@ -223,9 +223,9 @@ export default function JobsFindModalComponent({
|
|||||||
type: "radio",
|
type: "radio",
|
||||||
selectedRowKeys: [selectedJob]
|
selectedRowKeys: [selectedJob]
|
||||||
}}
|
}}
|
||||||
onRow={(record, rowIndex) => {
|
onRow={(record) => {
|
||||||
return {
|
return {
|
||||||
onClick: (event) => {
|
onClick: () => {
|
||||||
handleOnRowClick(record);
|
handleOnRowClick(record);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -241,15 +241,17 @@ export default function JobsFindModalComponent({
|
|||||||
overrideHeaders: e.target.checked
|
overrideHeaders: e.target.checked
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
id="override_header"
|
||||||
>
|
>
|
||||||
{t("jobs.labels.override_header")}
|
{t("jobs.labels.override_header")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Checkbox checked={partsQueueToggle} onChange={(e) => setPartsQueueToggle(e.target.checked)}>
|
<Checkbox checked={partsQueueToggle} onChange={(e) => setPartsQueueToggle(e.target.checked)} id="parts_queue_toggle">
|
||||||
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={updateSchComp.checked}
|
checked={updateSchComp.checked}
|
||||||
onChange={(e) => setSchComp({ ...updateSchComp, checked: e.target.checked })}
|
onChange={(e) => setSchComp({ ...updateSchComp, checked: e.target.checked })}
|
||||||
|
id="update_scheduled_completion"
|
||||||
>
|
>
|
||||||
{t("jobs.labels.update_scheduled_completion")}
|
{t("jobs.labels.update_scheduled_completion")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
@@ -261,6 +263,7 @@ export default function JobsFindModalComponent({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSchComp({ ...updateSchComp, scheduled_completion: e });
|
setSchComp({ ...updateSchComp, scheduled_completion: e });
|
||||||
}}
|
}}
|
||||||
|
id="scheduled_completion_date_time_picker"
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -273,6 +276,7 @@ export default function JobsFindModalComponent({
|
|||||||
automatic: true
|
automatic: true
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
id="calculate_scheduled_completion"
|
||||||
>
|
>
|
||||||
{t("jobs.labels.calc_scheuled_completion")}
|
{t("jobs.labels.calc_scheuled_completion")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Modal } from "antd";
|
import { Modal } from "antd";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -66,7 +65,7 @@ export default connect(
|
|||||||
title={t("jobs.labels.existing_jobs")}
|
title={t("jobs.labels.existing_jobs")}
|
||||||
width={"80%"}
|
width={"80%"}
|
||||||
destroyOnHidden
|
destroyOnHidden
|
||||||
okButtonProps={{ disabled: selectedJob ? false : true }}
|
okButtonProps={{ disabled: selectedJob ? false : true, id: "jobs-find-modal-container-ok" }}
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
>
|
>
|
||||||
{loading ? <LoadingSpinner /> : null}
|
{loading ? <LoadingSpinner /> : null}
|
||||||
|
|||||||
@@ -32,15 +32,17 @@ function OwnerDetailFormContainer({ owner, refetch, bodyshop }) {
|
|||||||
// Fetch opt-out status on mount
|
// Fetch opt-out status on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchOptOutStatus = async () => {
|
const fetchOptOutStatus = async () => {
|
||||||
if (bodyshop?.id && (owner?.ownr_ph1 || owner?.ownr_ph2)) {
|
if (bodyshop?.id && bodyshop?.messagingservicesid && (owner?.ownr_ph1 || owner?.ownr_ph2)) {
|
||||||
const phoneNumbers = [owner.ownr_ph1, owner.ownr_ph2].filter(Boolean);
|
const phoneNumbers = [owner.ownr_ph1, owner.ownr_ph2].filter(Boolean);
|
||||||
const optOutSet = await phoneNumberOptOutService(apolloClient, bodyshop.id, phoneNumbers);
|
const optOutSet = await phoneNumberOptOutService(apolloClient, bodyshop.id, phoneNumbers);
|
||||||
setOptedOutPhones(optOutSet);
|
setOptedOutPhones(optOutSet);
|
||||||
|
} else {
|
||||||
|
setOptedOutPhones(new Set());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchOptOutStatus();
|
fetchOptOutStatus();
|
||||||
}, [apolloClient, bodyshop?.id, owner?.ownr_ph1, owner?.ownr_ph2]);
|
}, [apolloClient, bodyshop?.id, bodyshop?.messagingservicesid, owner?.ownr_ph1, owner?.ownr_ph2]);
|
||||||
|
|
||||||
// Reset form fields when owner changes
|
// Reset form fields when owner changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -136,10 +138,14 @@ function OwnerDetailFormContainer({ owner, refetch, bodyshop }) {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
form={form}
|
form={form}
|
||||||
isPhone1OptedOut={
|
isPhone1OptedOut={
|
||||||
owner?.ownr_ph1 && optedOutPhones.has(phone(owner.ownr_ph1, "CA").phoneNumber?.replace(/^\+1/, ""))
|
bodyshop?.messagingservicesid &&
|
||||||
|
owner?.ownr_ph1 &&
|
||||||
|
optedOutPhones.has(phone(owner.ownr_ph1, "CA").phoneNumber?.replace(/^\+1/, ""))
|
||||||
}
|
}
|
||||||
isPhone2OptedOut={
|
isPhone2OptedOut={
|
||||||
owner?.ownr_ph2 && optedOutPhones.has(phone(owner.ownr_ph2, "CA").phoneNumber?.replace(/^\+1/, ""))
|
bodyshop?.messagingservicesid &&
|
||||||
|
owner?.ownr_ph2 &&
|
||||||
|
optedOutPhones.has(phone(owner.ownr_ph2, "CA").phoneNumber?.replace(/^\+1/, ""))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useLazyQuery } from "@apollo/client";
|
import { useLazyQuery } from "@apollo/client";
|
||||||
import { Input, Modal } from "antd";
|
import { Input, Modal } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
import OwnerFindModalComponent from "./owner-find-modal.component";
|
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
|
import OwnerFindModalComponent from "./owner-find-modal.component";
|
||||||
|
|
||||||
export default function OwnerFindModalContainer({
|
export default function OwnerFindModalContainer({
|
||||||
loading,
|
loading,
|
||||||
@@ -41,6 +41,7 @@ export default function OwnerFindModalContainer({
|
|||||||
<Modal
|
<Modal
|
||||||
title={<span id="owner-find-modal-title">{t("owners.labels.existing_owners")}</span>}
|
title={<span id="owner-find-modal-title">{t("owners.labels.existing_owners")}</span>}
|
||||||
width={"80%"}
|
width={"80%"}
|
||||||
|
okButtonProps={{ id: "owner-find-modal-ok-button" }}
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
>
|
>
|
||||||
{loading ? <LoadingSpinner /> : null}
|
{loading ? <LoadingSpinner /> : null}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Col, Row } from "antd";
|
import { Col, Row } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component";
|
import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component";
|
||||||
import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component";
|
import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component";
|
||||||
import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component";
|
import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component";
|
||||||
@@ -26,18 +26,18 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
|
|||||||
start: dayjs().startOf("month"),
|
start: dayjs().startOf("month"),
|
||||||
end: dayjs().endOf("month")
|
end: dayjs().endOf("month")
|
||||||
},
|
},
|
||||||
pollInterval: 60000*5
|
pollInterval: 60000 * 5
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data } = scoreboardSubscription;
|
const { data } = scoreboardSubscription;
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const scoreBoardlist = (data && data.scoreboard) || [];
|
const scoreBoardlist = data?.scoreboard || [];
|
||||||
|
|
||||||
const sbEntriesByDate = {};
|
const sbEntriesByDate = {};
|
||||||
|
|
||||||
scoreBoardlist.forEach((i) => {
|
scoreBoardlist.forEach((i) => {
|
||||||
const entryDate = i.date;
|
const entryDate = i.date;
|
||||||
if (!!!sbEntriesByDate[entryDate]) {
|
if (!sbEntriesByDate[entryDate]) {
|
||||||
sbEntriesByDate[entryDate] = [];
|
sbEntriesByDate[entryDate] = [];
|
||||||
}
|
}
|
||||||
sbEntriesByDate[entryDate].push(i);
|
sbEntriesByDate[entryDate].push(i);
|
||||||
@@ -58,10 +58,9 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
|
|||||||
});
|
});
|
||||||
appointments = data.appointments;
|
appointments = data.appointments;
|
||||||
}
|
}
|
||||||
|
dayjs.updateLocale(dayjs.locale(), {
|
||||||
dayjs.updateLocale("ca", {
|
|
||||||
workingWeekdays: translateSettingsToWorkingDays(bodyshop.workingdays),
|
workingWeekdays: translateSettingsToWorkingDays(bodyshop.workingdays),
|
||||||
...(appointments
|
...(appointments?.length
|
||||||
? {
|
? {
|
||||||
holidays: appointments.map((h) => dayjs(h.start).format("MM-DD-YYYY"))
|
holidays: appointments.map((h) => dayjs(h.start).format("MM-DD-YYYY"))
|
||||||
}
|
}
|
||||||
@@ -78,11 +77,9 @@ export function ScoreboardDisplayComponent({ bodyshop }) {
|
|||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<ScoreboardTargetsTable scoreBoardlist={scoreBoardlist} />
|
<ScoreboardTargetsTable scoreBoardlist={scoreBoardlist} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<ScoreboardLastDays sbEntriesByDate={sbEntriesByDate} />
|
<ScoreboardLastDays sbEntriesByDate={sbEntriesByDate} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<ScoreboardChart sbEntriesByDate={sbEntriesByDate} />
|
<ScoreboardChart sbEntriesByDate={sbEntriesByDate} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation, useQuery } from "@apollo/client";
|
import { useApolloClient, useMutation, useQuery } from "@apollo/client";
|
||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Form, Input, InputNumber, Select, Switch, Table } from "antd";
|
import { Button, Card, Form, Input, InputNumber, Select, Switch, Table } from "antd";
|
||||||
import { useForm } from "antd/es/form/Form";
|
import { useForm } from "antd/es/form/Form";
|
||||||
import dayjs from "../../utils/day";
|
import queryString from "query-string";
|
||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
CHECK_EMPLOYEE_NUMBER,
|
CHECK_EMPLOYEE_NUMBER,
|
||||||
@@ -20,19 +22,17 @@ import {
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CiecaSelect from "../../utils/Ciecaselect";
|
import CiecaSelect from "../../utils/Ciecaselect";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
import ShopEmployeeAddVacation from "./shop-employees-add-vacation.component";
|
||||||
import queryString from "query-string";
|
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then(() => {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("employees.successes.save")
|
message: t("employees.successes.save")
|
||||||
});
|
});
|
||||||
@@ -120,13 +120,13 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
title: t("employees.fields.vacation.start"),
|
title: t("employees.fields.vacation.start"),
|
||||||
dataIndex: "start",
|
dataIndex: "start",
|
||||||
key: "start",
|
key: "start",
|
||||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>
|
render: (text) => <DateFormatter>{text}</DateFormatter>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("employees.fields.vacation.end"),
|
title: t("employees.fields.vacation.end"),
|
||||||
dataIndex: "end",
|
dataIndex: "end",
|
||||||
key: "end",
|
key: "end",
|
||||||
render: (text, record) => <DateFormatter>{text}</DateFormatter>
|
render: (text) => <DateFormatter>{text}</DateFormatter>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("employees.fields.vacation.length"),
|
title: t("employees.fields.vacation.length"),
|
||||||
@@ -210,7 +210,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
({ getFieldValue }) => ({
|
() => ({
|
||||||
async validator(rule, value) {
|
async validator(rule, value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const response = await client.query({
|
const response = await client.query({
|
||||||
@@ -369,8 +369,9 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
add();
|
add();
|
||||||
}}
|
}}
|
||||||
style={{ width: "100%" }}
|
style={{ width: "100%" }}
|
||||||
|
id="add-employee-rate-button"
|
||||||
>
|
>
|
||||||
{t("employees.actions.newrate")}
|
<span id="new-employee-rate">{t("employees.actions.newrate")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
@@ -383,7 +384,7 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
|||||||
title={() => <ShopEmployeeAddVacation employee={data && data.employees_by_pk} />}
|
title={() => <ShopEmployeeAddVacation employee={data && data.employees_by_pk} />}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey={"id"}
|
rowKey={"id"}
|
||||||
dataSource={data ? data.employees_by_pk.employee_vacations : []}
|
dataSource={data?.employees_by_pk?.employee_vacations ?? []}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Tabs } from "antd";
|
import { Button, Card, Tabs } from "antd";
|
||||||
import React from "react";
|
import queryString from "query-string";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
|
import LockWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
||||||
import ShopInfoGeneral from "./shop-info.general.component";
|
import ShopInfoGeneral from "./shop-info.general.component";
|
||||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
|
import ShopInfoNotificationsAutoadd from "./shop-info.notifications-autoadd.component.jsx";
|
||||||
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||||
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||||
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||||
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||||
|
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
||||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
|
||||||
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
||||||
import queryString from "query-string";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
|
||||||
import ShopInfoIntellipay from "./shop-intellipay-config.component";
|
import ShopInfoIntellipay from "./shop-intellipay-config.component";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
|
||||||
import LockWrapperComponent from "../lock-wrapper/lock-wrapper.component";
|
|
||||||
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
|
||||||
import ShopInfoNotificationsAutoadd from "./shop-info.notifications-autoadd.component.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||||
@@ -158,7 +157,7 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
extra={
|
extra={
|
||||||
<Button type="primary" loading={saveLoading} onClick={() => form.submit()}>
|
<Button type="primary" loading={saveLoading} onClick={() => form.submit()} id="shop-info-save-button">
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, DatePicker, Form, Input, InputNumber, Radio, Select, Space, Switch } from "antd";
|
import { Button, DatePicker, Form, Input, InputNumber, Radio, Select, Space, Switch } from "antd";
|
||||||
import React from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -15,11 +14,12 @@ import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-forma
|
|||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoGeneral);
|
||||||
@@ -144,236 +144,246 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<FeatureWrapper featureName="export" noauth={() => null}>
|
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
||||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
{HasFeatureAccess({ featureName: "export", bodyshop }) && (
|
||||||
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
<>
|
||||||
<Switch />
|
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
||||||
</Form.Item>
|
<Switch />
|
||||||
{InstanceRenderManager({
|
</Form.Item>
|
||||||
imex: (
|
{InstanceRenderManager({
|
||||||
<Form.Item shouldUpdate noStyle>
|
imex: (
|
||||||
{() => (
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.qbo_usa")}
|
||||||
|
shouldUpdate
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["accountingconfig", "qbo_usa"]}
|
||||||
|
>
|
||||||
|
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.accountingtiers")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "tiers"]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value={2}>2</Radio>
|
||||||
|
<Radio value={3}>3</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item shouldUpdate>
|
||||||
|
{() => {
|
||||||
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.qbo_usa")}
|
label={t("bodyshop.labels.2tiersetup")}
|
||||||
shouldUpdate
|
shouldUpdate
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "qbo_usa"]}
|
{
|
||||||
|
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "twotierpref"]}
|
||||||
>
|
>
|
||||||
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
||||||
|
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||||
|
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
);
|
||||||
</Form.Item>
|
}}
|
||||||
)
|
</Form.Item>
|
||||||
})}
|
<Form.Item
|
||||||
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
label={t("bodyshop.labels.printlater")}
|
||||||
<Input />
|
valuePropName="checked"
|
||||||
</Form.Item>
|
name={["accountingconfig", "printlater"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.labels.accountingtiers")}
|
<Switch />
|
||||||
rules={[
|
</Form.Item>
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.labels.emaillater")}
|
||||||
//message: t("general.validation.required"),
|
valuePropName="checked"
|
||||||
}
|
name={["accountingconfig", "emaillater"]}
|
||||||
]}
|
>
|
||||||
name={["accountingconfig", "tiers"]}
|
<Switch />
|
||||||
>
|
</Form.Item>
|
||||||
<Radio.Group>
|
</>
|
||||||
<Radio value={2}>2</Radio>
|
)}
|
||||||
<Radio value={3}>3</Radio>
|
<Form.Item
|
||||||
</Radio.Group>
|
label={t("bodyshop.fields.inhousevendorid")}
|
||||||
</Form.Item>
|
name={"inhousevendorid"}
|
||||||
<Form.Item shouldUpdate>
|
rules={[
|
||||||
{() => {
|
{
|
||||||
return (
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.default_adjustment_rate")}
|
||||||
|
name={"default_adjustment_rate"}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
|
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
{HasFeatureAccess({ featureName: "bills", bodyshop }) && (
|
||||||
|
<>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.2tiersetup")}
|
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||||
shouldUpdate
|
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
name={["accountingconfig", "twotierpref"]}
|
|
||||||
>
|
>
|
||||||
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
<InputNumber />
|
||||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
|
||||||
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
)
|
||||||
}}
|
})}
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
<Form.Item
|
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||||
label={t("bodyshop.labels.printlater")}
|
name={["bill_tax_rates", "state_tax_rate"]}
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "printlater"]}
|
{
|
||||||
>
|
required: true
|
||||||
<Switch />
|
//message: t("general.validation.required"),
|
||||||
</Form.Item>
|
}
|
||||||
<Form.Item
|
]}
|
||||||
label={t("bodyshop.labels.emaillater")}
|
>
|
||||||
valuePropName="checked"
|
<InputNumber />
|
||||||
name={["accountingconfig", "emaillater"]}
|
</Form.Item>
|
||||||
>
|
<Form.Item
|
||||||
<Switch />
|
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||||
</Form.Item>
|
name={["bill_tax_rates", "local_tax_rate"]}
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("bodyshop.fields.inhousevendorid")}
|
{
|
||||||
name={"inhousevendorid"}
|
required: true
|
||||||
rules={[
|
//message: t("general.validation.required"),
|
||||||
{
|
}
|
||||||
required: true
|
]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
}
|
<InputNumber />
|
||||||
]}
|
</Form.Item>
|
||||||
>
|
</>
|
||||||
<Input />
|
)}
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
<Form.Item
|
name={["md_payment_types"]}
|
||||||
label={t("bodyshop.fields.default_adjustment_rate")}
|
label={t("bodyshop.fields.md_payment_types")}
|
||||||
name={"default_adjustment_rate"}
|
rules={[
|
||||||
rules={[
|
{
|
||||||
{
|
required: true,
|
||||||
required: true
|
//message: t("general.validation.required"),
|
||||||
//message: t("general.validation.required"),
|
type: "array"
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} precision={2} />
|
<Select mode="tags" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{InstanceRenderManager({
|
<Form.Item
|
||||||
imex: (
|
name={["md_categories"]}
|
||||||
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
label={t("bodyshop.fields.md_categories")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
|
{HasFeatureAccess({ featureName: "export", bodyshop }) && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ReceivableCustomField1"]}
|
||||||
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
||||||
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ReceivableCustomField2"]}
|
||||||
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
||||||
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ReceivableCustomField3"]}
|
||||||
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
||||||
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_classes"]}
|
||||||
|
label={t("bodyshop.fields.md_classes")}
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => {
|
||||||
|
return {
|
||||||
|
required: getFieldValue("enforce_class"),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
{ClosingPeriod.treatment === "on" && (
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ClosingPeriod"]}
|
||||||
|
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||||
|
>
|
||||||
|
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{ADPPayroll.treatment === "on" && (
|
||||||
|
<Form.Item name={["accountingconfig", "companyCode"]} label={t("bodyshop.fields.companycode")}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)}
|
||||||
})}
|
{ADPPayroll.treatment === "on" && (
|
||||||
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
<Form.Item name={["accountingconfig", "batchID"]} label={t("bodyshop.fields.batchid")}>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
|
||||||
{InstanceRenderManager({
|
|
||||||
imex: (
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)
|
)}
|
||||||
})}
|
</>
|
||||||
<Form.Item
|
)}
|
||||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
</LayoutFormRow>
|
||||||
name={["bill_tax_rates", "state_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "local_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_payment_types"]}
|
|
||||||
label={t("bodyshop.fields.md_payment_types")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_categories"]}
|
|
||||||
label={t("bodyshop.fields.md_categories")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField1"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField2"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField3"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_classes"]}
|
|
||||||
label={t("bodyshop.fields.md_classes")}
|
|
||||||
rules={[
|
|
||||||
({ getFieldValue }) => {
|
|
||||||
return {
|
|
||||||
required: getFieldValue("enforce_class"),
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
{ClosingPeriod.treatment === "on" && (
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ClosingPeriod"]}
|
|
||||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
|
||||||
>
|
|
||||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
{ADPPayroll.treatment === "on" && (
|
|
||||||
<Form.Item name={["accountingconfig", "companyCode"]} label={t("bodyshop.fields.companycode")}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
{ADPPayroll.treatment === "on" && (
|
|
||||||
<Form.Item name={["accountingconfig", "batchID"]} label={t("bodyshop.fields.batchid")}>
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
</LayoutFormRow>
|
|
||||||
</FeatureWrapper>
|
|
||||||
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -823,7 +833,11 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow header={t("bodyshop.labels.insurancecos")} id="insurancecos">
|
<LayoutFormRow
|
||||||
|
grow
|
||||||
|
header=<span id="insurancecos-header">{t("bodyshop.labels.insurancecos")}</span>
|
||||||
|
id="insurancecos"
|
||||||
|
>
|
||||||
<Form.List name={["md_ins_cos"]}>
|
<Form.List name={["md_ins_cos"]}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ import { ColorPicker } from "./shop-info.rostatus.component";
|
|||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = () => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LayoutFormRow>
|
<LayoutFormRow id="shopinfo-scheduling">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.appt_length")}
|
label={t("bodyshop.fields.appt_length")}
|
||||||
name={"appt_length"}
|
name={"appt_length"}
|
||||||
@@ -44,6 +44,7 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
|
|||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
id="schedule_start_time"
|
||||||
>
|
>
|
||||||
<TimePicker disableSeconds={true} format="HH:mm" />
|
<TimePicker disableSeconds={true} format="HH:mm" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -56,6 +57,7 @@ export function ShopInfoSchedulingComponent({ form, bodyshop }) {
|
|||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
|
id="schedule_end_time"
|
||||||
>
|
>
|
||||||
<TimePicker disableSeconds={true} format="HH:mm" />
|
<TimePicker disableSeconds={true} format="HH:mm" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -16,5 +16,6 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopSubStatus);
|
|||||||
export function ShopSubStatus({ bodyshop }) {
|
export function ShopSubStatus({ bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sub_status } = bodyshop;
|
const { sub_status } = bodyshop;
|
||||||
return <Result status="403" title={t(`general.labels.sub_status.${sub_status}`)} />;
|
// ‘expired’ ‘trail-expired' are the valid sub_status values
|
||||||
|
return <Result status="403" title={t(`general.errors.sub_status.${sub_status}`)} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { SocketContext, INITIAL_NOTIFICATIONS } from "./useSocket.js";
|
import { INITIAL_NOTIFICATIONS, SocketContext } from "./useSocket.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Socket Provider - Scenario Notifications / Web Socket related items
|
* Socket Provider - Scenario Notifications / Web Socket related items
|
||||||
@@ -157,7 +157,10 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => {
|
|||||||
auth: { token, bodyshopId: bodyshop.id },
|
auth: { token, bodyshopId: bodyshop.id },
|
||||||
reconnectionAttempts: Infinity,
|
reconnectionAttempts: Infinity,
|
||||||
reconnectionDelay: 2000,
|
reconnectionDelay: 2000,
|
||||||
reconnectionDelayMax: 10000
|
reconnectionDelayMax: 60000,
|
||||||
|
randomizationFactor: 0.5,
|
||||||
|
transports: ["websocket", "polling"], // Add this to prefer WebSocket with polling fallback
|
||||||
|
rememberUpgrade: true
|
||||||
});
|
});
|
||||||
|
|
||||||
socketRef.current = socketInstance;
|
socketRef.current = socketInstance;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Badge, Button, Divider, Form, Space, Tabs } from "antd";
|
|||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks } from "react-icons/fa";
|
import { FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -28,6 +28,7 @@ import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal
|
|||||||
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
|
||||||
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
|
||||||
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
|
||||||
|
import JobWatcherToggleContainer from "../../components/job-watcher-toggle/job-watcher-toggle.container.jsx";
|
||||||
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
|
||||||
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
import JobsConvertButton from "../../components/jobs-convert-button/jobs-convert-button.component";
|
||||||
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
|
||||||
@@ -45,6 +46,8 @@ import LockWrapperComponent from "../../components/lock-wrapper/lock-wrapper.com
|
|||||||
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
|
||||||
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
|
||||||
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
|
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
|
||||||
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
|
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
||||||
import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries.js";
|
import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries.js";
|
||||||
import { QUERY_JOB_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
import { QUERY_JOB_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
@@ -55,9 +58,6 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|||||||
import { DateTimeFormat } from "../../utils/DateFormatter";
|
import { DateTimeFormat } from "../../utils/DateFormatter";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import UndefinedToNull from "../../utils/undefinedtonull";
|
import UndefinedToNull from "../../utils/undefinedtonull";
|
||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
|
||||||
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
|
||||||
import JobWatcherToggleContainer from "../../components/job-watcher-toggle/job-watcher-toggle.container.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -322,11 +322,11 @@ export function JobsDetailPage({
|
|||||||
>
|
>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
// onBack={() => window.history.back()}
|
// onBack={() => window.history.back()}
|
||||||
|
id="job-detail-header"
|
||||||
title={
|
title={
|
||||||
<Space>
|
<Space>
|
||||||
{scenarioNotificationsOn && <JobWatcherToggleContainer job={job} />}
|
{scenarioNotificationsOn && <JobWatcherToggleContainer job={job} />}
|
||||||
{job.ro_number || t("general.labels.na")}
|
<span id="job-ro_number">{job.ro_number || t("general.labels.na")}</span>
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
extra={menuExtra}
|
extra={menuExtra}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Icon, { FieldTimeOutlined } from "@ant-design/icons";
|
import Icon, { FieldTimeOutlined } from "@ant-design/icons";
|
||||||
import { Card, Tabs } from "antd";
|
import { Card, Tabs } from "antd";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaShieldAlt } from "react-icons/fa";
|
import { FaShieldAlt } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -78,7 +78,7 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
|
|||||||
<RbacWrapper action="scoreboard:view">
|
<RbacWrapper action="scoreboard:view">
|
||||||
<Tabs
|
<Tabs
|
||||||
activeKey={tab || "sb"}
|
activeKey={tab || "sb"}
|
||||||
destroyInactiveTabPane
|
destroyOnHidden
|
||||||
onChange={(key) => {
|
onChange={(key) => {
|
||||||
searchParams.tab = key;
|
searchParams.tab = key;
|
||||||
history({
|
history({
|
||||||
|
|||||||
@@ -1230,7 +1230,11 @@
|
|||||||
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
||||||
"notfound": "No record was found.",
|
"notfound": "No record was found.",
|
||||||
"sizelimit": "The selected items exceed the size limit.",
|
"sizelimit": "The selected items exceed the size limit.",
|
||||||
"submit-for-testing": "Error submitting Job for testing."
|
"submit-for-testing": "Error submitting Job for testing.",
|
||||||
|
"sub_status": {
|
||||||
|
"expired": "The subscription for this shop has expired. Please contact Sales to reactivate.",
|
||||||
|
"trial-expired": "The trial for this shop has expired. Please contact Sales to reactivate."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
"contract": "CC Contract",
|
"contract": "CC Contract",
|
||||||
|
|||||||
@@ -1230,7 +1230,11 @@
|
|||||||
"fcm": "",
|
"fcm": "",
|
||||||
"notfound": "",
|
"notfound": "",
|
||||||
"sizelimit": "",
|
"sizelimit": "",
|
||||||
"submit-for-testing": ""
|
"submit-for-testing": "",
|
||||||
|
"sub_status": {
|
||||||
|
"expired": "",
|
||||||
|
"trial-expired": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
"contract": "",
|
"contract": "",
|
||||||
|
|||||||
@@ -1230,7 +1230,11 @@
|
|||||||
"fcm": "",
|
"fcm": "",
|
||||||
"notfound": "",
|
"notfound": "",
|
||||||
"sizelimit": "",
|
"sizelimit": "",
|
||||||
"submit-for-testing": ""
|
"submit-for-testing": "",
|
||||||
|
"sub_status": {
|
||||||
|
"expired": "",
|
||||||
|
"trial-expired": ""
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"itemtypes": {
|
"itemtypes": {
|
||||||
"contract": "",
|
"contract": "",
|
||||||
|
|||||||
@@ -1035,6 +1035,7 @@
|
|||||||
- use_fippa
|
- use_fippa
|
||||||
- use_paint_scale_data
|
- use_paint_scale_data
|
||||||
- uselocalmediaserver
|
- uselocalmediaserver
|
||||||
|
- external_shop_id
|
||||||
- website
|
- website
|
||||||
- workingdays
|
- workingdays
|
||||||
- zip_post
|
- zip_post
|
||||||
@@ -1130,6 +1131,7 @@
|
|||||||
- use_fippa
|
- use_fippa
|
||||||
- use_paint_scale_data
|
- use_paint_scale_data
|
||||||
- uselocalmediaserver
|
- uselocalmediaserver
|
||||||
|
- external_shop_id
|
||||||
- website
|
- website
|
||||||
- workingdays
|
- workingdays
|
||||||
- zip_post
|
- zip_post
|
||||||
|
|||||||
@@ -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 "we_profile_id" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "we_profile_id" text
|
||||||
|
null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" rename column "parts_management_key" to "we_profile_id";
|
||||||
|
alter table "public"."bodyshops" drop constraint "bodyshops_we_profile_id_key";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add constraint "bodyshops_we_profile_id_key" unique ("we_profile_id");
|
||||||
|
alter table "public"."bodyshops" rename column "we_profile_id" to "parts_management_key";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."bodyshops" rename column "external_shop_id" to "parts_management_key";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."bodyshops" rename column "parts_management_key" to "external_shop_id";
|
||||||
2110
package-lock.json
generated
2110
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@@ -16,14 +16,14 @@
|
|||||||
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
"job-totals-fixtures:local": "docker exec node-app /usr/bin/node /app/download-job-totals-fixtures.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-cloudwatch-logs": "^3.817.0",
|
"@aws-sdk/client-cloudwatch-logs": "^3.826.0",
|
||||||
"@aws-sdk/client-elasticache": "^3.817.0",
|
"@aws-sdk/client-elasticache": "^3.826.0",
|
||||||
"@aws-sdk/client-s3": "^3.817.0",
|
"@aws-sdk/client-s3": "^3.826.0",
|
||||||
"@aws-sdk/client-secrets-manager": "^3.817.0",
|
"@aws-sdk/client-secrets-manager": "^3.826.0",
|
||||||
"@aws-sdk/client-ses": "^3.817.0",
|
"@aws-sdk/client-ses": "^3.826.0",
|
||||||
"@aws-sdk/credential-provider-node": "^3.817.0",
|
"@aws-sdk/credential-provider-node": "^3.826.0",
|
||||||
"@aws-sdk/lib-storage": "^3.817.0",
|
"@aws-sdk/lib-storage": "^3.826.0",
|
||||||
"@aws-sdk/s3-request-presigner": "^3.817.0",
|
"@aws-sdk/s3-request-presigner": "^3.826.0",
|
||||||
"@opensearch-project/opensearch": "^2.13.0",
|
"@opensearch-project/opensearch": "^2.13.0",
|
||||||
"@socket.io/admin-ui": "^0.5.1",
|
"@socket.io/admin-ui": "^0.5.1",
|
||||||
"@socket.io/redis-adapter": "^8.3.0",
|
"@socket.io/redis-adapter": "^8.3.0",
|
||||||
@@ -31,14 +31,14 @@
|
|||||||
"aws4": "^1.13.2",
|
"aws4": "^1.13.2",
|
||||||
"axios": "^1.8.4",
|
"axios": "^1.8.4",
|
||||||
"better-queue": "^3.8.12",
|
"better-queue": "^3.8.12",
|
||||||
"bullmq": "^5.53.0",
|
"bullmq": "^5.53.2",
|
||||||
"chart.js": "^4.4.8",
|
"chart.js": "^4.4.8",
|
||||||
"cloudinary": "^2.6.1",
|
"cloudinary": "^2.6.1",
|
||||||
"compression": "^1.8.0",
|
"compression": "^1.8.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crisp-status-reporter": "^1.2.2",
|
"crisp-status-reporter": "^1.2.2",
|
||||||
"dd-trace": "^5.53.0",
|
"dd-trace": "^5.55.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
@@ -61,26 +61,27 @@
|
|||||||
"recursive-diff": "^1.0.9",
|
"recursive-diff": "^1.0.9",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"skia-canvas": "^2.0.2",
|
"skia-canvas": "^2.0.2",
|
||||||
"soap": "^1.1.10",
|
"soap": "^1.1.12",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-adapter": "^2.5.5",
|
"socket.io-adapter": "^2.5.5",
|
||||||
"ssh2-sftp-client": "^11.0.0",
|
"ssh2-sftp-client": "^11.0.0",
|
||||||
"twilio": "^5.6.1",
|
"twilio": "^5.7.0",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"winston": "^3.17.0",
|
"winston": "^3.17.0",
|
||||||
"winston-cloudwatch": "^6.3.0",
|
"winston-cloudwatch": "^6.3.0",
|
||||||
"xml2js": "^0.6.2",
|
"xml2js": "^0.6.2",
|
||||||
"xmlbuilder2": "^3.1.1"
|
"xmlbuilder2": "^3.1.1",
|
||||||
|
"yazl": "^3.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.27.0",
|
"@eslint/js": "^9.28.0",
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"globals": "^15.15.0",
|
"globals": "^15.15.0",
|
||||||
"mock-require": "^3.0.3",
|
"mock-require": "^3.0.3",
|
||||||
"p-limit": "^3.1.0",
|
"p-limit": "^3.1.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"supertest": "^7.1.1",
|
"supertest": "^7.1.1",
|
||||||
"vitest": "^3.1.4"
|
"vitest": "^3.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ async function InsertJob(oauthClient, qbo_realmId, req, job, parentTierRef) {
|
|||||||
|
|
||||||
exports.InsertJob = InsertJob;
|
exports.InsertJob = InsertJob;
|
||||||
|
|
||||||
async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid) {
|
async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid, jobid) {
|
||||||
const items = await oauthClient.makeApiCall({
|
const items = await oauthClient.makeApiCall({
|
||||||
url: urlBuilder(qbo_realmId, "query", `select * From Item where active=true maxresults 1000`),
|
url: urlBuilder(qbo_realmId, "query", `select * From Item where active=true maxresults 1000`),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -492,6 +492,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid) {
|
|||||||
name: "QueryItems",
|
name: "QueryItems",
|
||||||
status: items.response?.status,
|
status: items.response?.status,
|
||||||
bodyshopid,
|
bodyshopid,
|
||||||
|
jobid: jobid,
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
})
|
})
|
||||||
setNewRefreshToken(req.user.email, items);
|
setNewRefreshToken(req.user.email, items);
|
||||||
@@ -508,6 +509,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid) {
|
|||||||
name: "QueryTaxCodes",
|
name: "QueryTaxCodes",
|
||||||
status: taxCodes.response?.status,
|
status: taxCodes.response?.status,
|
||||||
bodyshopid,
|
bodyshopid,
|
||||||
|
jobid: jobid,
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
})
|
})
|
||||||
const classes = await oauthClient.makeApiCall({
|
const classes = await oauthClient.makeApiCall({
|
||||||
@@ -523,6 +525,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid) {
|
|||||||
name: "QueryClasses",
|
name: "QueryClasses",
|
||||||
status: classes.response?.status,
|
status: classes.response?.status,
|
||||||
bodyshopid,
|
bodyshopid,
|
||||||
|
jobid: jobid,
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
})
|
})
|
||||||
const taxCodeMapping = {};
|
const taxCodeMapping = {};
|
||||||
@@ -559,7 +562,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, bodyshopid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function InsertInvoice(oauthClient, qbo_realmId, req, job, bodyshop, parentTierRef) {
|
async function InsertInvoice(oauthClient, qbo_realmId, req, job, bodyshop, parentTierRef) {
|
||||||
const { items, taxCodes, classes } = await QueryMetaData(oauthClient, qbo_realmId, req, job.shopid);
|
const { items, taxCodes, classes } = await QueryMetaData(oauthClient, qbo_realmId, req, job.shopid, job.id);
|
||||||
const InvoiceLineAdd = CreateInvoiceLines({
|
const InvoiceLineAdd = CreateInvoiceLines({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
jobs_by_pk: job,
|
jobs_by_pk: job,
|
||||||
@@ -653,7 +656,7 @@ async function InsertInvoice(oauthClient, qbo_realmId, req, job, bodyshop, paren
|
|||||||
platform: "QBO",
|
platform: "QBO",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
name: "InsertInvoice",
|
name: "InsertInvoice",
|
||||||
status: result.status,
|
status: result.response?.status,
|
||||||
bodyshopid: job.shopid,
|
bodyshopid: job.shopid,
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
@@ -778,7 +781,7 @@ async function InsertInvoiceMultiPayerInvoice(
|
|||||||
platform: "QBO",
|
platform: "QBO",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
name: "InsertInvoice",
|
name: "InsertInvoice",
|
||||||
status: result.response.status,
|
status: result.response?.status,
|
||||||
bodyshopid: job.shopid,
|
bodyshopid: job.shopid,
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
email: req.user.email
|
email: req.user.email
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ exports.default = async (req, res) => {
|
|||||||
//Query the usage data.
|
//Query the usage data.
|
||||||
const queryResults = await client.request(queries.STATUS_UPDATE, {
|
const queryResults = await client.request(queries.STATUS_UPDATE, {
|
||||||
today: moment().startOf("day").subtract(7, "days"),
|
today: moment().startOf("day").subtract(7, "days"),
|
||||||
period: moment().subtract(90, "days").startOf("day")
|
period: moment().subtract(365, "days").startOf("day")
|
||||||
});
|
});
|
||||||
|
|
||||||
//Massage the data.
|
//Massage the data.
|
||||||
@@ -66,7 +66,7 @@ exports.default = async (req, res) => {
|
|||||||
Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers.
|
Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included.
|
- Days Since Creation: The number of days since the shop was created. Only shops created in the last 365 days are included.
|
||||||
- Updated values should be higher than created values.
|
- Updated values should be higher than created values.
|
||||||
- Counts are inclusive of the last 7 days of data.
|
- Counts are inclusive of the last 7 days of data.
|
||||||
`,
|
`,
|
||||||
|
|||||||
1236
server/integrations/partsManagement/defaultNewShop.json
Normal file
1236
server/integrations/partsManagement/defaultNewShop.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,257 @@
|
|||||||
|
const crypto = require("crypto");
|
||||||
|
const admin = require("firebase-admin");
|
||||||
|
const client = require("../../graphql-client/graphql-client").client;
|
||||||
|
const DefaultNewShop = require("./defaultNewShop.json");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the required fields are present in the payload.
|
||||||
|
* @param payload
|
||||||
|
* @param fields
|
||||||
|
*/
|
||||||
|
const requireFields = (payload, fields) => {
|
||||||
|
for (const field of fields) {
|
||||||
|
if (!payload[field]) {
|
||||||
|
throw { status: 400, message: `${field} is required.` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the email is not already registered in Firebase.
|
||||||
|
* @param email
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const ensureEmailNotRegistered = async (email) => {
|
||||||
|
try {
|
||||||
|
await admin.auth().getUserByEmail(email);
|
||||||
|
throw { status: 400, message: "userEmail is already registered in Firebase." };
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code !== "auth/user-not-found") {
|
||||||
|
throw { status: 500, message: "Error validating userEmail uniqueness", detail: err };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Firebase user with the provided email.
|
||||||
|
* @param email
|
||||||
|
* @returns {Promise<UserRecord>}
|
||||||
|
*/
|
||||||
|
const createFirebaseUser = async (email) => {
|
||||||
|
return admin.auth().createUser({ email });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a Firebase user by their UID.
|
||||||
|
* @param uid
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const deleteFirebaseUser = async (uid) => {
|
||||||
|
return admin.auth().deleteUser(uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a password reset link for the given email.
|
||||||
|
* @param email
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
const generateResetLink = async (email) => {
|
||||||
|
return admin.auth().generatePasswordResetLink(email);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the external shop ID is unique in the database.
|
||||||
|
* @param externalId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const ensureExternalIdUnique = async (externalId) => {
|
||||||
|
const query = `
|
||||||
|
query CHECK_KEY($key: String!) {
|
||||||
|
bodyshops(where: { external_shop_id: { _eq: $key } }) {
|
||||||
|
external_shop_id
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const resp = await client.request(query, { key: externalId });
|
||||||
|
if (resp.bodyshops.length) {
|
||||||
|
throw { status: 400, message: `external_shop_id '${externalId}' is already in use.` };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new bodyshop into the database.
|
||||||
|
* @param input
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
const insertBodyshop = async (input) => {
|
||||||
|
const mutation = `
|
||||||
|
mutation CREATE_SHOP($bs: bodyshops_insert_input!) {
|
||||||
|
insert_bodyshops_one(object: $bs) { id }
|
||||||
|
}`;
|
||||||
|
const resp = await client.request(mutation, { bs: input });
|
||||||
|
return resp.insert_bodyshops_one.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all vendors associated with a specific shop ID.
|
||||||
|
* @param shopId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const deleteVendorsByShop = async (shopId) => {
|
||||||
|
const mutation = `
|
||||||
|
mutation DELETE_VENDORS($shopId: uuid!) {
|
||||||
|
delete_vendors(where: { shopid: { _eq: $shopId } }) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
await client.request(mutation, { shopId });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a bodyshop by its ID.
|
||||||
|
* @param shopId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const deleteBodyshop = async (shopId) => {
|
||||||
|
const mutation = `
|
||||||
|
mutation DELETE_SHOP($id: uuid!) {
|
||||||
|
delete_bodyshops_by_pk(id: $id) { id }
|
||||||
|
}`;
|
||||||
|
await client.request(mutation, { id: shopId });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new user association into the database.
|
||||||
|
* @param uid
|
||||||
|
* @param email
|
||||||
|
* @param shopId
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
const insertUserAssociation = async (uid, email, shopId) => {
|
||||||
|
const mutation = `
|
||||||
|
mutation CREATE_USER($u: users_insert_input!) {
|
||||||
|
insert_users_one(object: $u) {
|
||||||
|
id: authid
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const vars = {
|
||||||
|
u: {
|
||||||
|
email,
|
||||||
|
authid: uid,
|
||||||
|
validemail: true,
|
||||||
|
associations: {
|
||||||
|
data: [{ shopid: shopId, authlevel: 80, active: true }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const resp = await client.request(mutation, vars);
|
||||||
|
return resp.insert_users_one;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the provisioning of a new parts management shop and user.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
const partsManagementProvisioning = async (req, res) => {
|
||||||
|
const { logger } = req;
|
||||||
|
const p = { ...req.body, userEmail: req.body.userEmail?.toLowerCase() };
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Validate inputs
|
||||||
|
await ensureEmailNotRegistered(p.userEmail);
|
||||||
|
requireFields(p, [
|
||||||
|
"external_shop_id",
|
||||||
|
"shopname",
|
||||||
|
"address1",
|
||||||
|
"city",
|
||||||
|
"state",
|
||||||
|
"zip_post",
|
||||||
|
"country",
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
"userEmail"
|
||||||
|
]);
|
||||||
|
await ensureExternalIdUnique(p.external_shop_id);
|
||||||
|
|
||||||
|
logger.log("admin-create-shop-user", "debug", p.userEmail, null, {
|
||||||
|
request: req.body,
|
||||||
|
ioadmin: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create shop
|
||||||
|
const shopInput = {
|
||||||
|
shopname: p.shopname,
|
||||||
|
address1: p.address1,
|
||||||
|
address2: p.address2 || null,
|
||||||
|
city: p.city,
|
||||||
|
state: p.state,
|
||||||
|
zip_post: p.zip_post,
|
||||||
|
country: p.country,
|
||||||
|
email: p.email,
|
||||||
|
external_shop_id: p.external_shop_id,
|
||||||
|
timezone: p.timezone,
|
||||||
|
phone: p.phone,
|
||||||
|
logo_img_path: {
|
||||||
|
src: p.logoUrl,
|
||||||
|
width: "",
|
||||||
|
height: "",
|
||||||
|
headerMargin: DefaultNewShop.logo_img_path.headerMargin
|
||||||
|
},
|
||||||
|
md_ro_statuses: DefaultNewShop.md_ro_statuses,
|
||||||
|
vendors: {
|
||||||
|
data: p.vendors.map((v) => ({
|
||||||
|
name: v.name,
|
||||||
|
street1: v.street1 || null,
|
||||||
|
street2: v.street2 || null,
|
||||||
|
city: v.city || null,
|
||||||
|
state: v.state || null,
|
||||||
|
zip: v.zip || null,
|
||||||
|
country: v.country || null,
|
||||||
|
email: v.email || null,
|
||||||
|
discount: v.discount ?? 0,
|
||||||
|
due_date: v.due_date ?? null,
|
||||||
|
cost_center: v.cost_center || null,
|
||||||
|
favorite: v.favorite ?? [],
|
||||||
|
phone: v.phone || null,
|
||||||
|
active: v.active ?? true,
|
||||||
|
dmsid: v.dmsid || null
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const newShopId = await insertBodyshop(shopInput);
|
||||||
|
|
||||||
|
// Create user + association
|
||||||
|
const userRecord = await createFirebaseUser(p.userEmail);
|
||||||
|
const resetLink = await generateResetLink(p.userEmail);
|
||||||
|
const createdUser = await insertUserAssociation(userRecord.uid, p.userEmail, newShopId);
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
shop: { id: newShopId, shopname: p.shopname },
|
||||||
|
user: {
|
||||||
|
id: createdUser.id,
|
||||||
|
email: createdUser.email,
|
||||||
|
resetLink
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.log("admin-create-shop-user-error", "error", p.userEmail, null, {
|
||||||
|
message: err.message,
|
||||||
|
detail: err.detail || err
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup on failure
|
||||||
|
if (err.userRecord) {
|
||||||
|
await deleteFirebaseUser(err.userRecord.uid).catch(() => {});
|
||||||
|
}
|
||||||
|
if (err.newShopId) {
|
||||||
|
await deleteVendorsByShop(err.newShopId).catch(() => {});
|
||||||
|
await deleteBodyshop(err.newShopId).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(err.status || 500).json({ error: err.message || "Internal server error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = partsManagementProvisioning;
|
||||||
160
server/integrations/partsManagement/swagger.yaml
Normal file
160
server/integrations/partsManagement/swagger.yaml
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Parts Management Provisioning API
|
||||||
|
description: API endpoint to provision a new shop and user in the Parts Management system.
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/parts-management/provision:
|
||||||
|
post:
|
||||||
|
summary: Provision a new parts management shop and user
|
||||||
|
operationId: partsManagementProvisioning
|
||||||
|
tags:
|
||||||
|
- Parts Management
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- external_shop_id
|
||||||
|
- shopname
|
||||||
|
- address1
|
||||||
|
- city
|
||||||
|
- state
|
||||||
|
- zip_post
|
||||||
|
- country
|
||||||
|
- email
|
||||||
|
- phone
|
||||||
|
- userEmail
|
||||||
|
properties:
|
||||||
|
external_shop_id:
|
||||||
|
type: string
|
||||||
|
description: External shop ID (must be unique)
|
||||||
|
shopname:
|
||||||
|
type: string
|
||||||
|
address1:
|
||||||
|
type: string
|
||||||
|
address2:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
city:
|
||||||
|
type: string
|
||||||
|
state:
|
||||||
|
type: string
|
||||||
|
zip_post:
|
||||||
|
type: string
|
||||||
|
country:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
phone:
|
||||||
|
type: string
|
||||||
|
userEmail:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
logoUrl:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
nullable: true
|
||||||
|
timezone:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
vendors:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
street1:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
street2:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
city:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
state:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
zip:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
country:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
format: email
|
||||||
|
nullable: true
|
||||||
|
discount:
|
||||||
|
type: number
|
||||||
|
nullable: true
|
||||||
|
due_date:
|
||||||
|
type: string
|
||||||
|
format: date
|
||||||
|
nullable: true
|
||||||
|
cost_center:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
favorite:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
phone:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
active:
|
||||||
|
type: boolean
|
||||||
|
nullable: true
|
||||||
|
dmsid:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Shop and user successfully created
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
shop:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
shopname:
|
||||||
|
type: string
|
||||||
|
user:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
resetLink:
|
||||||
|
type: string
|
||||||
|
format: uri
|
||||||
|
'400':
|
||||||
|
description: Bad request (missing or invalid fields)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
'500':
|
||||||
|
description: Internal server error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
@@ -19,7 +19,7 @@ async function JobCosting(req, res) {
|
|||||||
const client = req.userGraphQLClient;
|
const client = req.userGraphQLClient;
|
||||||
|
|
||||||
//Uncomment for further testing
|
//Uncomment for further testing
|
||||||
// logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null);
|
logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_JOB_COSTING_DETAILS, {
|
const resp = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_JOB_COSTING_DETAILS, {
|
||||||
@@ -47,9 +47,9 @@ async function JobCostingMulti(req, res) {
|
|||||||
const client = req.userGraphQLClient;
|
const client = req.userGraphQLClient;
|
||||||
|
|
||||||
//Uncomment for further testing
|
//Uncomment for further testing
|
||||||
// logger.log("job-costing-multi-start", "DEBUG", req?.user?.email, null, {
|
logger.log("job-costing-multi-start", "DEBUG", req?.user?.email, null, {
|
||||||
// jobids
|
jobids
|
||||||
// });
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await client
|
const resp = await client
|
||||||
@@ -589,7 +589,7 @@ function GenerateCostingData(job) {
|
|||||||
amount: Math.round((job.storage_payable || 0) * 100)
|
amount: Math.round((job.storage_payable || 0) * 100)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Is it a DMS Setup?
|
//Is it a DMS Setup?
|
||||||
const selectedDmsAllocationConfig =
|
const selectedDmsAllocationConfig =
|
||||||
(job.bodyshop.md_responsibility_centers.dms_defaults &&
|
(job.bodyshop.md_responsibility_centers.dms_defaults &&
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const getLifecycleStatusColor = require("../utils/getLifecycleStatusColor");
|
|||||||
const jobLifecycle = async (req, res) => {
|
const jobLifecycle = async (req, res) => {
|
||||||
// Grab the jobids and statuses from the request body
|
// Grab the jobids and statuses from the request body
|
||||||
const { jobids, statuses } = req.body;
|
const { jobids, statuses } = req.body;
|
||||||
|
const { logger } = req;
|
||||||
|
|
||||||
if (!jobids) {
|
if (!jobids) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -16,102 +17,118 @@ const jobLifecycle = async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const jobIDs = _.isArray(jobids) ? jobids : [jobids];
|
const jobIDs = _.isArray(jobids) ? jobids : [jobids];
|
||||||
const client = req.userGraphQLClient;
|
|
||||||
const resp = await client.request(queries.QUERY_TRANSITIONS_BY_JOBID, { jobids: jobIDs });
|
|
||||||
|
|
||||||
const transitions = resp.transitions;
|
logger.log("job-lifecycle-start", "DEBUG", req?.user?.email, null, {
|
||||||
|
jobids: jobIDs
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = req.userGraphQLClient;
|
||||||
|
const resp = await client.request(queries.QUERY_TRANSITIONS_BY_JOBID, { jobids: jobIDs });
|
||||||
|
|
||||||
|
const transitions = resp.transitions;
|
||||||
|
|
||||||
|
if (!transitions) {
|
||||||
|
return res.status(200).json({
|
||||||
|
jobIDs,
|
||||||
|
transitions: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const transitionsByJobId = _.groupBy(resp.transitions, "jobid");
|
||||||
|
|
||||||
|
const groupedTransitions = {};
|
||||||
|
const allDurations = [];
|
||||||
|
|
||||||
|
for (let jobId in transitionsByJobId) {
|
||||||
|
let lifecycle = transitionsByJobId[jobId].map((transition) => {
|
||||||
|
transition.start_readable = transition.start ? moment(transition.start).fromNow() : "N/A";
|
||||||
|
transition.end_readable = transition.end ? moment(transition.end).fromNow() : "N/A";
|
||||||
|
|
||||||
|
if (transition.duration) {
|
||||||
|
transition.duration_seconds = Math.round(transition.duration / 1000);
|
||||||
|
transition.duration_minutes = Math.round(transition.duration_seconds / 60);
|
||||||
|
let duration = moment.duration(transition.duration);
|
||||||
|
transition.duration_readable = durationToHumanReadable(duration);
|
||||||
|
} else {
|
||||||
|
transition.duration_seconds = 0;
|
||||||
|
transition.duration_minutes = 0;
|
||||||
|
transition.duration_readable = "N/A";
|
||||||
|
}
|
||||||
|
return transition;
|
||||||
|
});
|
||||||
|
|
||||||
|
const durations = calculateStatusDuration(lifecycle, statuses);
|
||||||
|
|
||||||
|
groupedTransitions[jobId] = {
|
||||||
|
lifecycle,
|
||||||
|
durations
|
||||||
|
};
|
||||||
|
|
||||||
|
if (durations?.summations) {
|
||||||
|
allDurations.push(durations.summations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalSummations = [];
|
||||||
|
const flatGroupedAllDurations = _.groupBy(allDurations.flat(), "status");
|
||||||
|
|
||||||
|
const finalStatusCounts = Object.keys(flatGroupedAllDurations).reduce((acc, status) => {
|
||||||
|
acc[status] = flatGroupedAllDurations[status].length;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
// Calculate total value of all statuses
|
||||||
|
const finalTotal = Object.values(flatGroupedAllDurations).reduce((total, statusArr) => {
|
||||||
|
return total + statusArr.reduce((acc, curr) => acc + curr.value, 0);
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
Object.keys(flatGroupedAllDurations).forEach((status) => {
|
||||||
|
const value = flatGroupedAllDurations[status].reduce((acc, curr) => acc + curr.value, 0);
|
||||||
|
const humanReadable = durationToHumanReadable(moment.duration(value));
|
||||||
|
const percentage = finalTotal > 0 ? (value / finalTotal) * 100 : 0;
|
||||||
|
const color = getLifecycleStatusColor(status);
|
||||||
|
const roundedPercentage = `${Math.round(percentage)}%`;
|
||||||
|
const averageValue = _.size(jobIDs) > 0 ? value / jobIDs.length : 0;
|
||||||
|
const averageHumanReadable = durationToHumanReadable(moment.duration(averageValue));
|
||||||
|
finalSummations.push({
|
||||||
|
status,
|
||||||
|
value,
|
||||||
|
humanReadable,
|
||||||
|
percentage,
|
||||||
|
color,
|
||||||
|
roundedPercentage,
|
||||||
|
averageValue,
|
||||||
|
averageHumanReadable
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (!transitions) {
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
jobIDs,
|
jobIDs,
|
||||||
transitions: []
|
transition: groupedTransitions,
|
||||||
});
|
durations: {
|
||||||
}
|
jobs: jobIDs.length,
|
||||||
|
summations: finalSummations,
|
||||||
const transitionsByJobId = _.groupBy(resp.transitions, "jobid");
|
totalStatuses: finalSummations.length,
|
||||||
|
total: finalTotal,
|
||||||
const groupedTransitions = {};
|
statusCounts: finalStatusCounts,
|
||||||
const allDurations = [];
|
humanReadable: durationToHumanReadable(moment.duration(finalTotal)),
|
||||||
|
averageValue: _.size(jobIDs) > 0 ? finalTotal / jobIDs.length : 0,
|
||||||
for (let jobId in transitionsByJobId) {
|
averageHumanReadable:
|
||||||
let lifecycle = transitionsByJobId[jobId].map((transition) => {
|
_.size(jobIDs) > 0
|
||||||
transition.start_readable = transition.start ? moment(transition.start).fromNow() : "N/A";
|
? durationToHumanReadable(moment.duration(finalTotal / jobIDs.length))
|
||||||
transition.end_readable = transition.end ? moment(transition.end).fromNow() : "N/A";
|
: durationToHumanReadable(moment.duration(0))
|
||||||
|
|
||||||
if (transition.duration) {
|
|
||||||
transition.duration_seconds = Math.round(transition.duration / 1000);
|
|
||||||
transition.duration_minutes = Math.round(transition.duration_seconds / 60);
|
|
||||||
let duration = moment.duration(transition.duration);
|
|
||||||
transition.duration_readable = durationToHumanReadable(duration);
|
|
||||||
} else {
|
|
||||||
transition.duration_seconds = 0;
|
|
||||||
transition.duration_minutes = 0;
|
|
||||||
transition.duration_readable = "N/A";
|
|
||||||
}
|
}
|
||||||
return transition;
|
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
const durations = calculateStatusDuration(lifecycle, statuses);
|
logger.log("job-lifecycle-error", "ERROR", req?.user?.email, null, {
|
||||||
|
jobids: jobIDs,
|
||||||
groupedTransitions[jobId] = {
|
statuses: statuses ? JSON.stringify(statuses) : "N/A",
|
||||||
lifecycle,
|
error: error.message
|
||||||
durations
|
});
|
||||||
};
|
return res.status(500).json({
|
||||||
|
error: "Internal server error"
|
||||||
if (durations?.summations) {
|
});
|
||||||
allDurations.push(durations.summations);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalSummations = [];
|
|
||||||
const flatGroupedAllDurations = _.groupBy(allDurations.flat(), "status");
|
|
||||||
|
|
||||||
const finalStatusCounts = Object.keys(flatGroupedAllDurations).reduce((acc, status) => {
|
|
||||||
acc[status] = flatGroupedAllDurations[status].length;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
// Calculate total value of all statuses
|
|
||||||
const finalTotal = Object.values(flatGroupedAllDurations).reduce((total, statusArr) => {
|
|
||||||
return total + statusArr.reduce((acc, curr) => acc + curr.value, 0);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
Object.keys(flatGroupedAllDurations).forEach((status) => {
|
|
||||||
const value = flatGroupedAllDurations[status].reduce((acc, curr) => acc + curr.value, 0);
|
|
||||||
const humanReadable = durationToHumanReadable(moment.duration(value));
|
|
||||||
const percentage = finalTotal > 0 ? (value / finalTotal) * 100 : 0;
|
|
||||||
const color = getLifecycleStatusColor(status);
|
|
||||||
const roundedPercentage = `${Math.round(percentage)}%`;
|
|
||||||
const averageValue = _.size(jobIDs) > 0 ? value / jobIDs.length : 0;
|
|
||||||
const averageHumanReadable = durationToHumanReadable(moment.duration(averageValue));
|
|
||||||
finalSummations.push({
|
|
||||||
status,
|
|
||||||
value,
|
|
||||||
humanReadable,
|
|
||||||
percentage,
|
|
||||||
color,
|
|
||||||
roundedPercentage,
|
|
||||||
averageValue,
|
|
||||||
averageHumanReadable
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).json({
|
|
||||||
jobIDs,
|
|
||||||
transition: groupedTransitions,
|
|
||||||
durations: {
|
|
||||||
jobs: jobIDs.length,
|
|
||||||
summations: finalSummations,
|
|
||||||
totalStatuses: finalSummations.length,
|
|
||||||
total: finalTotal,
|
|
||||||
statusCounts: finalStatusCounts,
|
|
||||||
humanReadable: durationToHumanReadable(moment.duration(finalTotal)),
|
|
||||||
averageValue: _.size(jobIDs) > 0 ? finalTotal / jobIDs.length : 0,
|
|
||||||
averageHumanReadable:
|
|
||||||
_.size(jobIDs) > 0
|
|
||||||
? durationToHumanReadable(moment.duration(finalTotal / jobIDs.length))
|
|
||||||
: durationToHumanReadable(moment.duration(0))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = jobLifecycle;
|
module.exports = jobLifecycle;
|
||||||
|
|||||||
@@ -381,7 +381,7 @@ async function CalculateRatesTotals({ job, client }) {
|
|||||||
|
|
||||||
if (item.mod_lbr_ty) {
|
if (item.mod_lbr_ty) {
|
||||||
//Check to see if it has 0 hours and a price instead.
|
//Check to see if it has 0 hours and a price instead.
|
||||||
if (item.mod_lb_hrs === 0 && item.act_price > 0 && item.lbr_op === "OP14") {
|
if (item.lbr_op === "OP14" && item.act_price > 0 && (!item.part_type || item.mod_lb_hrs === 0)) {
|
||||||
//Scenario where SGI may pay out hours using a part price.
|
//Scenario where SGI may pay out hours using a part price.
|
||||||
if (!ret[item.mod_lbr_ty.toLowerCase()].total) {
|
if (!ret[item.mod_lbr_ty.toLowerCase()].total) {
|
||||||
ret[item.mod_lbr_ty.toLowerCase()].total = Dinero();
|
ret[item.mod_lbr_ty.toLowerCase()].total = Dinero();
|
||||||
|
|||||||
@@ -314,7 +314,8 @@ function CalculateRatesTotals(ratesList) {
|
|||||||
|
|
||||||
if (item.mod_lbr_ty) {
|
if (item.mod_lbr_ty) {
|
||||||
//Check to see if it has 0 hours and a price instead.
|
//Check to see if it has 0 hours and a price instead.
|
||||||
if (item.mod_lb_hrs === 0 && item.act_price > 0 && item.lbr_op === "OP14") {
|
//Extend for when there are hours and a price.
|
||||||
|
if (item.lbr_op === "OP14" && item.act_price > 0 && (!item.part_type || item.mod_lb_hrs === 0)) {
|
||||||
//Scenario where SGI may pay out hours using a part price.
|
//Scenario where SGI may pay out hours using a part price.
|
||||||
if (!ret[item.mod_lbr_ty.toLowerCase()].total) {
|
if (!ret[item.mod_lbr_ty.toLowerCase()].total) {
|
||||||
ret[item.mod_lbr_ty.toLowerCase()].total = Dinero();
|
ret[item.mod_lbr_ty.toLowerCase()].total = Dinero();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const {
|
|||||||
GET_DOCUMENTS_BY_IDS,
|
GET_DOCUMENTS_BY_IDS,
|
||||||
DELETE_MEDIA_DOCUMENTS
|
DELETE_MEDIA_DOCUMENTS
|
||||||
} = require("../graphql-client/queries");
|
} = require("../graphql-client/queries");
|
||||||
|
const yazl = require("yazl");
|
||||||
|
|
||||||
const imgproxyBaseUrl = process.env.IMGPROXY_BASE_URL; // `https://u4gzpp5wm437dnm75qa42tvza40fguqr.lambda-url.ca-central-1.on.aws` //Direct Lambda function access to bypass CDN.
|
const imgproxyBaseUrl = process.env.IMGPROXY_BASE_URL; // `https://u4gzpp5wm437dnm75qa42tvza40fguqr.lambda-url.ca-central-1.on.aws` //Direct Lambda function access to bypass CDN.
|
||||||
const imgproxySalt = process.env.IMGPROXY_SALT;
|
const imgproxySalt = process.env.IMGPROXY_SALT;
|
||||||
@@ -102,13 +103,7 @@ const getThumbnailUrls = async (req, res) => {
|
|||||||
//<Cloudfront_to_lambda>/<hmac with SHA of entire request URI path (with base64 encoded URL if needed), beginning with un-encoded/un-hashed Salt>/<remainder of url - resize params >/< base 64 URL encoded to image path>
|
//<Cloudfront_to_lambda>/<hmac with SHA of entire request URI path (with base64 encoded URL if needed), beginning with un-encoded/un-hashed Salt>/<remainder of url - resize params >/< base 64 URL encoded to image path>
|
||||||
//When working with documents from Cloudinary, the URL does not include the extension.
|
//When working with documents from Cloudinary, the URL does not include the extension.
|
||||||
|
|
||||||
let key;
|
let key = keyStandardize(document)
|
||||||
|
|
||||||
if (/\.[^/.]+$/.test(document.key)) {
|
|
||||||
key = document.key;
|
|
||||||
} else {
|
|
||||||
key = `${document.key}.${document.extension.toLowerCase()}`;
|
|
||||||
}
|
|
||||||
// Build the S3 path to the object.
|
// Build the S3 path to the object.
|
||||||
const fullS3Path = `s3://${imgproxyDestinationBucket}/${key}`;
|
const fullS3Path = `s3://${imgproxyDestinationBucket}/${key}`;
|
||||||
const base64UrlEncodedKeyString = base64UrlEncode(fullS3Path);
|
const base64UrlEncodedKeyString = base64UrlEncode(fullS3Path);
|
||||||
@@ -168,78 +163,73 @@ const getThumbnailUrls = async (req, res) => {
|
|||||||
* @returns {Promise<*>}
|
* @returns {Promise<*>}
|
||||||
*/
|
*/
|
||||||
const downloadFiles = async (req, res) => {
|
const downloadFiles = async (req, res) => {
|
||||||
//Given a series of document IDs or keys, generate a file (or a link) to download all images in bulk
|
|
||||||
const { jobId, billid, documentids } = req.body;
|
const { jobId, billid, documentids } = req.body;
|
||||||
|
|
||||||
|
logger.log("imgproxy-download", "DEBUG", req.user?.email, jobId, { billid, jobId, documentids });
|
||||||
|
|
||||||
|
const client = req.userGraphQLClient;
|
||||||
|
let data;
|
||||||
try {
|
try {
|
||||||
logger.log("imgproxy-download", "DEBUG", req.user?.email, jobId, { billid, jobId, documentids });
|
data = await client.request(GET_DOCUMENTS_BY_IDS, { documentIds: documentids });
|
||||||
|
|
||||||
//Delayed as the key structure may change slightly from what it is currently and will require evaluating mobile components.
|
|
||||||
const client = req.userGraphQLClient;
|
|
||||||
|
|
||||||
//Query for the keys of the document IDs
|
|
||||||
const data = await client.request(GET_DOCUMENTS_BY_IDS, { documentIds: documentids });
|
|
||||||
|
|
||||||
//Using the Keys, get all the S3 links, zip them, and send back to the client.
|
|
||||||
const s3client = new S3Client({ region: InstanceRegion() });
|
|
||||||
const archiveStream = archiver("zip");
|
|
||||||
|
|
||||||
archiveStream.on("error", (error) => {
|
|
||||||
console.error("Archival encountered an error:", error);
|
|
||||||
throw new Error(error);
|
|
||||||
});
|
|
||||||
|
|
||||||
const passThrough = new stream.PassThrough();
|
|
||||||
|
|
||||||
archiveStream.pipe(passThrough);
|
|
||||||
|
|
||||||
for (const key of data.documents.map((d) => d.key)) {
|
|
||||||
const response = await s3client.send(
|
|
||||||
new GetObjectCommand({
|
|
||||||
Bucket: imgproxyDestinationBucket,
|
|
||||||
Key: key
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
archiveStream.append(response.Body, { name: path.basename(key) });
|
|
||||||
}
|
|
||||||
|
|
||||||
await archiveStream.finalize();
|
|
||||||
|
|
||||||
const archiveKey = `archives/${jobId || "na"}/archive-${new Date().toISOString()}.zip`;
|
|
||||||
|
|
||||||
const parallelUploads3 = new Upload({
|
|
||||||
client: s3client,
|
|
||||||
queueSize: 4, // optional concurrency configuration
|
|
||||||
leavePartsOnError: false, // optional manually handle dropped parts
|
|
||||||
params: { Bucket: imgproxyDestinationBucket, Key: archiveKey, Body: passThrough }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Disabled progress logging for upload, uncomment if needed
|
|
||||||
// parallelUploads3.on("httpUploadProgress", (progress) => {
|
|
||||||
// console.log(progress);
|
|
||||||
// });
|
|
||||||
|
|
||||||
await parallelUploads3.done();
|
|
||||||
|
|
||||||
//Generate the presigned URL to download it.
|
|
||||||
const presignedUrl = await getSignedUrl(
|
|
||||||
s3client,
|
|
||||||
new GetObjectCommand({ Bucket: imgproxyDestinationBucket, Key: archiveKey }),
|
|
||||||
{ expiresIn: 360 }
|
|
||||||
);
|
|
||||||
|
|
||||||
return res.json({ success: true, url: presignedUrl });
|
|
||||||
//Iterate over them, build the link based on the media type, and return the array.
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("imgproxy-thumbnails-error", "ERROR", req.user?.email, jobId, {
|
logger.log("imgproxy-download-error", "ERROR", req.user?.email, jobId, {
|
||||||
jobId,
|
jobId,
|
||||||
billid,
|
billid,
|
||||||
message: error.message,
|
message: error.message,
|
||||||
stack: error.stack
|
stack: error.stack
|
||||||
});
|
});
|
||||||
|
return res.status(400).json({ message: error.message });
|
||||||
|
}
|
||||||
|
|
||||||
return res.status(400).json({ message: error.message, stack: error.stack });
|
const s3client = new S3Client({ region: InstanceRegion() });
|
||||||
|
const zipfile = new yazl.ZipFile();
|
||||||
|
|
||||||
|
const filename = `archive-${jobId || "na"}-${new Date().toISOString().replace(/[:.]/g, "-")}.zip`;
|
||||||
|
res.setHeader("Content-Type", "application/zip");
|
||||||
|
res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
|
||||||
|
|
||||||
|
// Handle zipfile stream errors
|
||||||
|
zipfile.outputStream.on("error", (err) => {
|
||||||
|
logger.log("imgproxy-download-zipstream-error", "ERROR", req.user?.email, jobId, { message: err.message, stack: err.stack });
|
||||||
|
// Cannot send another response here, just destroy the connection
|
||||||
|
res.destroy(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
zipfile.outputStream.pipe(res);
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const doc of data.documents) {
|
||||||
|
let key = keyStandardize(doc)
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await s3client.send(
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: imgproxyDestinationBucket,
|
||||||
|
Key: key
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
logger.log("imgproxy-download-s3-error", "ERROR", req.user?.email, jobId, { key, message: err.message, stack: err.stack });
|
||||||
|
// Optionally, skip this file or add a placeholder file in the zip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Attach error handler to S3 stream
|
||||||
|
response.Body.on("error", (err) => {
|
||||||
|
logger.log("imgproxy-download-s3stream-error", "ERROR", req.user?.email, jobId, { key, message: err.message, stack: err.stack });
|
||||||
|
res.destroy(err);
|
||||||
|
});
|
||||||
|
zipfile.addReadStream(response.Body, path.basename(key));
|
||||||
|
}
|
||||||
|
zipfile.end();
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("imgproxy-download-error", "ERROR", req.user?.email, jobId, {
|
||||||
|
jobId,
|
||||||
|
billid,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
// Cannot send another response here, just destroy the connection
|
||||||
|
res.destroy(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -392,6 +382,15 @@ const moveFiles = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const keyStandardize = (doc) => {
|
||||||
|
if (/\.[^/.]+$/.test(doc.key)) {
|
||||||
|
return doc.key;
|
||||||
|
} else {
|
||||||
|
return `${doc.key}.${doc.extension.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
generateSignedUploadUrls,
|
generateSignedUploadUrls,
|
||||||
getThumbnailUrls,
|
getThumbnailUrls,
|
||||||
|
|||||||
23
server/middleware/partsManagementIntegrationMiddleware.js
Normal file
23
server/middleware/partsManagementIntegrationMiddleware.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Middleware to check if the request is authorized for Parts Management Integration.
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @param next
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
const partsManagementIntegrationMiddleware = (req, res, next) => {
|
||||||
|
const secret = process.env.PARTS_MANAGEMENT_INTEGRATION_SECRET;
|
||||||
|
if (typeof secret !== "string" || secret.length === 0) {
|
||||||
|
return res.status(500).send("Server misconfiguration");
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerValue = req.headers["parts-management-integration-secret"];
|
||||||
|
if (typeof headerValue !== "string" || headerValue.trim() !== secret) {
|
||||||
|
return res.status(401).send("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
req.isPartsManagementIntegrationAuthorized = true;
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = partsManagementIntegrationMiddleware;
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
/**
|
/**
|
||||||
* VSSTA Integration Middleware
|
* VSSTA Integration Middleware
|
||||||
* @param req
|
* Fails closed if the env var is missing or empty, and strictly compares header.
|
||||||
* @param res
|
|
||||||
* @param next
|
|
||||||
* @returns {*}
|
|
||||||
*/
|
*/
|
||||||
const vsstaIntegrationMiddleware = (req, res, next) => {
|
const vsstaIntegrationMiddleware = (req, res, next) => {
|
||||||
if (req?.headers?.["vssta-integration-secret"] !== process.env?.VSSTA_INTEGRATION_SECRET) {
|
const secret = process.env.VSSTA_INTEGRATION_SECRET;
|
||||||
|
if (typeof secret !== "string" || secret.length === 0) {
|
||||||
|
return res.status(500).send("Server misconfiguration");
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerValue = req.headers["vssta-integration-secret"];
|
||||||
|
if (typeof headerValue !== "string" || headerValue.trim() !== secret) {
|
||||||
return res.status(401).send("Unauthorized");
|
return res.status(401).send("Unauthorized");
|
||||||
}
|
}
|
||||||
|
|
||||||
req.isIntegrationAuthorized = true;
|
req.isVsstaIntegrationAuthorized = true;
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ async function OpenSearchUpdateHandler(req, res) {
|
|||||||
document = pick(req.body.event.data.new, ["id", "ownr_fn", "ownr_ln", "ownr_co_nm", "ownr_ph1", "ownr_ph2"]);
|
document = pick(req.body.event.data.new, ["id", "ownr_fn", "ownr_ln", "ownr_co_nm", "ownr_ph1", "ownr_ph2"]);
|
||||||
document.bodyshopid = req.body.event.data.new.shopid;
|
document.bodyshopid = req.body.event.data.new.shopid;
|
||||||
break;
|
break;
|
||||||
case "bills":
|
case "bills": {
|
||||||
const bill = await client.request(
|
const bill = await client.request(
|
||||||
`query ADMIN_GET_BILL_BY_ID($billId: uuid!) {
|
`query ADMIN_GET_BILL_BY_ID($billId: uuid!) {
|
||||||
bills_by_pk(id: $billId) {
|
bills_by_pk(id: $billId) {
|
||||||
@@ -97,7 +97,8 @@ async function OpenSearchUpdateHandler(req, res) {
|
|||||||
bodyshopid: bill.bills_by_pk.job.shopid
|
bodyshopid: bill.bills_by_pk.job.shopid
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case "payments":
|
}
|
||||||
|
case "payments": {
|
||||||
//Query to get the job and RO number
|
//Query to get the job and RO number
|
||||||
|
|
||||||
const payment = await client.request(
|
const payment = await client.request(
|
||||||
@@ -141,6 +142,7 @@ async function OpenSearchUpdateHandler(req, res) {
|
|||||||
bodyshopid: payment.payments_by_pk.job.shopid
|
bodyshopid: payment.payments_by_pk.job.shopid
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const payload = {
|
const payload = {
|
||||||
id: req.body.event.data.new.id,
|
id: req.body.event.data.new.id,
|
||||||
@@ -255,6 +257,7 @@ async function OpenSearchSearchHandler(req, res) {
|
|||||||
"*ownr_co_nm^8",
|
"*ownr_co_nm^8",
|
||||||
"*ownr_ph1^8",
|
"*ownr_ph1^8",
|
||||||
"*ownr_ph2^8",
|
"*ownr_ph2^8",
|
||||||
|
"*vendor.name^8",
|
||||||
"*comment^6"
|
"*comment^6"
|
||||||
// "*"
|
// "*"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,27 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const vsstaIntegration = require("../integrations/VSSTA/vsstaIntegrationRoute");
|
|
||||||
const vsstaMiddleware = require("../middleware/vsstaIntegrationMiddleware");
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.post("/vssta", vsstaMiddleware, vsstaIntegration);
|
// Pull secrets from env
|
||||||
|
const { VSSTA_INTEGRATION_SECRET, PARTS_MANAGEMENT_INTEGRATION_SECRET } = process.env;
|
||||||
|
|
||||||
|
// Only load VSSTA routes if the secret is set
|
||||||
|
if (typeof VSSTA_INTEGRATION_SECRET === "string" && VSSTA_INTEGRATION_SECRET.length > 0) {
|
||||||
|
const vsstaIntegration = require("../integrations/VSSTA/vsstaIntegrationRoute");
|
||||||
|
const vsstaMiddleware = require("../middleware/vsstaIntegrationMiddleware");
|
||||||
|
|
||||||
|
router.post("/vssta", vsstaMiddleware, vsstaIntegration);
|
||||||
|
} else {
|
||||||
|
console.warn("VSSTA_INTEGRATION_SECRET is not set — skipping /vssta integration route");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only load Parts Management routes if that secret is set
|
||||||
|
if (typeof PARTS_MANAGEMENT_INTEGRATION_SECRET === "string" && PARTS_MANAGEMENT_INTEGRATION_SECRET.length > 0) {
|
||||||
|
const partsManagementProvisioning = require("../integrations/partsManagement/partsManagementProvisioning");
|
||||||
|
const partsManagementIntegrationMiddleware = require("../middleware/partsManagementIntegrationMiddleware");
|
||||||
|
|
||||||
|
router.post("/parts-management/provision", partsManagementIntegrationMiddleware, partsManagementProvisioning);
|
||||||
|
} else {
|
||||||
|
console.warn("PARTS_MANAGEMENT_INTEGRATION_SECRET is not set — skipping /parts-management/provision route");
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ const optOutKeywords = ["STOP", "STOPALL", "UNSUBSCRIBE", "CANCEL", "END", "QUIT
|
|||||||
|
|
||||||
// System Message text, will also need to be localized if we support multiple languages
|
// System Message text, will also need to be localized if we support multiple languages
|
||||||
const systemMessageOptions = {
|
const systemMessageOptions = {
|
||||||
optIn: "Customer has Opted-in",
|
optIn: "Customer has opted-in",
|
||||||
optOut: "Customer has Opted-out"
|
optOut: "Customer has opted-out"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ const createLogger = () => {
|
|||||||
jobid,
|
jobid,
|
||||||
paymentid,
|
paymentid,
|
||||||
billid,
|
billid,
|
||||||
status: status.toString() ?? "0",
|
status: status?.toString() ?? "0",
|
||||||
bodyshopid,
|
bodyshopid,
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user