Compare commits
2 Commits
feature/IO
...
IO-2825-No
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18da11f593 | ||
|
|
136d52ec0b |
179
client/package-lock.json
generated
179
client/package-lock.json
generated
@@ -19,7 +19,7 @@
|
||||
"@splitsoftware/splitio-react": "^1.12.1",
|
||||
"@tanem/react-nprogress": "^5.0.51",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"antd": "^5.20.1",
|
||||
"antd": "^5.20.2",
|
||||
"apollo-link-logger": "^2.0.1",
|
||||
"apollo-link-sentry": "^3.3.0",
|
||||
"autosize": "^6.0.1",
|
||||
@@ -32,12 +32,12 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"env-cmd": "^10.1.0",
|
||||
"exifr": "^7.1.3",
|
||||
"firebase": "^10.12.5",
|
||||
"firebase": "^10.13.0",
|
||||
"graphql": "^16.9.0",
|
||||
"i18next": "^23.12.3",
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"libphonenumber-js": "^1.11.5",
|
||||
"libphonenumber-js": "^1.11.7",
|
||||
"logrocket": "^8.1.2",
|
||||
"markerjs2": "^2.32.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
@@ -47,7 +47,7 @@
|
||||
"query-string": "^9.1.0",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.13.2",
|
||||
"react-big-calendar": "^1.13.3",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -60,10 +60,10 @@
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-number-format": "^5.4.0",
|
||||
"react-popopo": "^2.1.9",
|
||||
"react-product-fruits": "^2.2.6",
|
||||
"react-product-fruits": "^2.2.61",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-resizable": "^3.0.5",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-sticky": "^6.0.3",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"react-virtuoso": "^4.10.1",
|
||||
@@ -103,7 +103,7 @@
|
||||
"react-error-overlay": "6.0.11",
|
||||
"redux-logger": "^3.0.6",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"vite": "^5.4.0",
|
||||
"vite": "^5.4.1",
|
||||
"vite-plugin-babel": "^1.2.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-legacy": "^2.1.0",
|
||||
@@ -3498,9 +3498,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@firebase/app": {
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.8.tgz",
|
||||
"integrity": "sha512-xSLmW0/RShcnUEXH7l+wC0AFWaUtty4tUFF2loIgbtXTRmra0UH/SqYDf/IcfreUninRrCsusNmvoTidGkXJPw==",
|
||||
"version": "0.10.9",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.9.tgz",
|
||||
"integrity": "sha512-AmGlPg/4SoDhwCdvVDeZsN5Yn+czYD/m/NAEOOCOhwn3Cz1xmEFKAKcyZKKahLrh5QPmge5Adyw+sk3cBTubBg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
@@ -3554,12 +3554,12 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@firebase/app-compat": {
|
||||
"version": "0.2.38",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.38.tgz",
|
||||
"integrity": "sha512-36ZrSvkYLW7QR01Sii2X+IY18ErMpRg6e2B2f/DVTtJBolthwXOnNBps+wvaVBvegdvdVPspgDXZUV0ppqh45w==",
|
||||
"version": "0.2.39",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.39.tgz",
|
||||
"integrity": "sha512-NnTFywe+M/jxZn751NIEhidgDePiDvlcfabvGxBy4YbU1E+b0TpEuJUnm3L6YDZtaZLVEz8ieoq9wbJkgGZ2rg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/app": "0.10.8",
|
||||
"@firebase/app": "0.10.9",
|
||||
"@firebase/component": "0.6.8",
|
||||
"@firebase/logger": "0.4.2",
|
||||
"@firebase/util": "1.9.7",
|
||||
@@ -3573,9 +3573,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@firebase/auth": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.6.tgz",
|
||||
"integrity": "sha512-T+lA5xoug9CByGYkD5WkfTh2ujEYq/frGZPbk0H+fNU6fNl7nqg88KcsmzsC6Fsqbjm3LLEb/i6wJvF6NSNEig==",
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.7.tgz",
|
||||
"integrity": "sha512-gMB0uRRNiIvYorEDLtIq1mc7x5D080EsoghTIph9xnbLqcQS3qRBREEC2o21nMEhviAeiGJMelRkKhAkkggjmA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
@@ -3595,12 +3595,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/auth-compat": {
|
||||
"version": "0.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.11.tgz",
|
||||
"integrity": "sha512-7rE3MkQDoWwI2qd8qsra4/QZCO2GzQSbCL6AVQpult9+Nbimg+5A+YeHxpLTcYAxUV6HDg2CqTDQreFLhcm1CQ==",
|
||||
"version": "0.5.12",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.12.tgz",
|
||||
"integrity": "sha512-K47inLqjTREez85D7pP0TmRv5aQcap22cJW67poLwJoJ6BVVH0I2NOfIoMqENetCrgGS+7vXSIZaLjvHFHwS+g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/auth": "1.7.6",
|
||||
"@firebase/auth": "1.7.7",
|
||||
"@firebase/auth-types": "0.12.2",
|
||||
"@firebase/component": "0.6.8",
|
||||
"@firebase/util": "1.9.7",
|
||||
@@ -3675,9 +3675,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/firestore": {
|
||||
"version": "4.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.6.5.tgz",
|
||||
"integrity": "sha512-0+Ascaht4qUzj4pCopMPWmoAujk8HKjwCpaNYOOjbYMZ65RVfZPsfZwwbWi/zWMXj6xvPsai5oBiErUUkrLwNw==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.0.tgz",
|
||||
"integrity": "sha512-wGOp84P1qa1pfpdct6lckfyowTuvIlUDHoiRcN8dFDT4WnZDh0tZW1X77SMiBUVejK8xIRLBCK3yDTejlRVrUA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
@@ -3697,13 +3697,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/firestore-compat": {
|
||||
"version": "0.3.34",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.34.tgz",
|
||||
"integrity": "sha512-OBP2F/Ccydl2U2j8XIfpKBxf0EnQHEhbZ4LTwbSS2QlG9+8TwhvKFkKk/ZljWYqaype+qFKPuXZ5flCqYEETeA==",
|
||||
"version": "0.3.35",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.35.tgz",
|
||||
"integrity": "sha512-VdYQtMIrPjsgZpuBwvry6LgcS0vbUhHzpebaKm5oc9oTTvP4K7oxvR/ZJdDjIE5rBugn1SdY++uGMatcIvBkZg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
"@firebase/firestore": "4.6.5",
|
||||
"@firebase/firestore": "4.7.0",
|
||||
"@firebase/firestore-types": "3.0.2",
|
||||
"@firebase/util": "1.9.7",
|
||||
"tslib": "^2.1.0"
|
||||
@@ -3912,9 +3912,10 @@
|
||||
"integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA=="
|
||||
},
|
||||
"node_modules/@firebase/storage": {
|
||||
"version": "0.12.6",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.12.6.tgz",
|
||||
"integrity": "sha512-Zgb9WuehJxzhj7pGXUvkAEaH+3HvLjD9xSZ9nepuXf5f8378xME7oGJtREr/RnepdDA5YW0XIxe0QQBNHpe1nw==",
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.0.tgz",
|
||||
"integrity": "sha512-3RQaYpkR4TwPnPR1XlmDUAXiYt5QVQRGRGY1+/yNyS9ohHOCNNgbcs6a+QYvaDInbYTywrdddKYMFFXKKb1pRg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
"@firebase/util": "1.9.7",
|
||||
@@ -3926,12 +3927,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@firebase/storage-compat": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.9.tgz",
|
||||
"integrity": "sha512-WWgAp5bTW961oIsCc9+98m4MIVKpEqztAlIngfHfwO/x3DYoBPRl/awMRG3CAXyVxG+7B7oHC5IsnqM+vTwx2A==",
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.10.tgz",
|
||||
"integrity": "sha512-KcikeV5dK1H1cXi0zEb7gJ3IZ4dKKCjpyucVK8r/Qv5eNAqeQAzPgKKhsSv67wT1N6DTxmqsNEXwMo0dcrKOEg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.6.8",
|
||||
"@firebase/storage": "0.12.6",
|
||||
"@firebase/storage": "0.13.0",
|
||||
"@firebase/storage-types": "0.8.2",
|
||||
"@firebase/util": "1.9.7",
|
||||
"tslib": "^2.1.0"
|
||||
@@ -3944,6 +3946,7 @@
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz",
|
||||
"integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"@firebase/app-types": "0.x",
|
||||
"@firebase/util": "1.x"
|
||||
@@ -4614,9 +4617,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.0.tgz",
|
||||
"integrity": "sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA==",
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
|
||||
"integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -6333,9 +6336,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/antd": {
|
||||
"version": "5.20.1",
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.20.1.tgz",
|
||||
"integrity": "sha512-YjVCYAfBjrTyNKsg+heAOR0Gm4qJNJoBZQcV1h1BX/ufwoLx0PC5RGs75g6gQFy/1nv8OrJH7DXUGdtwPMB3Vg==",
|
||||
"version": "5.20.2",
|
||||
"resolved": "https://registry.npmjs.org/antd/-/antd-5.20.2.tgz",
|
||||
"integrity": "sha512-9d6Bs5ZKIV+JhB0eD7KxYnIfnhUh86kNtTGIuNiIxHFUhbuyT1DXN2SuMksDmtSfuRYZ82/C4hq+OJjWNNbmHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.1.0",
|
||||
@@ -6359,7 +6362,7 @@
|
||||
"rc-dialog": "~9.5.2",
|
||||
"rc-drawer": "~7.2.0",
|
||||
"rc-dropdown": "~4.2.0",
|
||||
"rc-field-form": "~2.3.0",
|
||||
"rc-field-form": "~2.4.0",
|
||||
"rc-image": "~7.9.0",
|
||||
"rc-input": "~1.6.3",
|
||||
"rc-input-number": "~9.2.0",
|
||||
@@ -6368,7 +6371,7 @@
|
||||
"rc-motion": "^2.9.2",
|
||||
"rc-notification": "~5.6.0",
|
||||
"rc-pagination": "~4.2.0",
|
||||
"rc-picker": "~4.6.12",
|
||||
"rc-picker": "~4.6.13",
|
||||
"rc-progress": "~4.0.0",
|
||||
"rc-rate": "~2.13.0",
|
||||
"rc-resize-observer": "^1.4.0",
|
||||
@@ -6383,7 +6386,7 @@
|
||||
"rc-tooltip": "~6.2.0",
|
||||
"rc-tree": "~5.8.8",
|
||||
"rc-tree-select": "~5.22.1",
|
||||
"rc-upload": "~4.6.0",
|
||||
"rc-upload": "~4.7.0",
|
||||
"rc-util": "^5.43.0",
|
||||
"scroll-into-view-if-needed": "^3.1.0",
|
||||
"throttle-debounce": "^5.0.2"
|
||||
@@ -10149,24 +10152,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/firebase": {
|
||||
"version": "10.12.5",
|
||||
"resolved": "https://registry.npmjs.org/firebase/-/firebase-10.12.5.tgz",
|
||||
"integrity": "sha512-J0yL3yh12CfFprTkSOQ9HqBugERyqvWwOuOoo1j1QHmYe9cYLKnBmtNCvGIYInDcsVUnJoRXCM+hxbGf48oVhg==",
|
||||
"version": "10.13.0",
|
||||
"resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.0.tgz",
|
||||
"integrity": "sha512-a8gm8c9CYO98QuXJn7m5W5Gj7kHV8fme81/mQ9dBs+VMz9uI5HdavnMVPXCILputpZFMFpiKK+u7VVsn5lQg+w==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@firebase/analytics": "0.10.7",
|
||||
"@firebase/analytics-compat": "0.2.13",
|
||||
"@firebase/app": "0.10.8",
|
||||
"@firebase/app": "0.10.9",
|
||||
"@firebase/app-check": "0.8.7",
|
||||
"@firebase/app-check-compat": "0.3.14",
|
||||
"@firebase/app-compat": "0.2.38",
|
||||
"@firebase/app-compat": "0.2.39",
|
||||
"@firebase/app-types": "0.9.2",
|
||||
"@firebase/auth": "1.7.6",
|
||||
"@firebase/auth-compat": "0.5.11",
|
||||
"@firebase/auth": "1.7.7",
|
||||
"@firebase/auth-compat": "0.5.12",
|
||||
"@firebase/database": "1.0.7",
|
||||
"@firebase/database-compat": "1.0.7",
|
||||
"@firebase/firestore": "4.6.5",
|
||||
"@firebase/firestore-compat": "0.3.34",
|
||||
"@firebase/firestore": "4.7.0",
|
||||
"@firebase/firestore-compat": "0.3.35",
|
||||
"@firebase/functions": "0.11.6",
|
||||
"@firebase/functions-compat": "0.3.12",
|
||||
"@firebase/installations": "0.6.8",
|
||||
@@ -10177,8 +10180,8 @@
|
||||
"@firebase/performance-compat": "0.2.8",
|
||||
"@firebase/remote-config": "0.4.8",
|
||||
"@firebase/remote-config-compat": "0.2.8",
|
||||
"@firebase/storage": "0.12.6",
|
||||
"@firebase/storage-compat": "0.3.9",
|
||||
"@firebase/storage": "0.13.0",
|
||||
"@firebase/storage-compat": "0.3.10",
|
||||
"@firebase/util": "1.9.7",
|
||||
"@firebase/vertexai-preview": "0.0.3"
|
||||
}
|
||||
@@ -10862,9 +10865,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "23.12.3",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.12.3.tgz",
|
||||
"integrity": "sha512-DyigQmrR10V9U2N6pjhbfahW13GY7n8BQD9swN09JuRRropgsksWVi4vRLeex0Qf7zCPnBfIqQfhcBzdZBQBYw==",
|
||||
"version": "23.14.0",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.14.0.tgz",
|
||||
"integrity": "sha512-Y5GL4OdA8IU2geRrt2+Uc1iIhsjICdHZzT9tNwQ3TVqdNzgxHToGCKf/TPRP80vTCAP6svg2WbbJL+Gx5MFQVA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -11948,9 +11951,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/libphonenumber-js": {
|
||||
"version": "1.11.5",
|
||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.5.tgz",
|
||||
"integrity": "sha512-TwHR5BZxGRODtAfz03szucAkjT5OArXr+94SMtAM2pYXIlQNVMrxvb6uSCbnaJJV6QXEyICk7+l6QPgn72WHhg==",
|
||||
"version": "1.11.7",
|
||||
"resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.7.tgz",
|
||||
"integrity": "sha512-x2xON4/Qg2bRIS11KIN9yCNYUjhtiEjNyptjX0mX+pyKHecxuJVLIpfX1lq9ZD6CrC/rB+y4GBi18c6CEcUR+A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lie": {
|
||||
@@ -14168,9 +14171,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rc-field-form": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.3.0.tgz",
|
||||
"integrity": "sha512-QyiYrE3uweGGi21MJpxHFmDW+Tb1yt5hitM1k0EbWc5hKDiSf5imOBc6NLLHrYk+sdelrw2Ju/fD4uRQdhSqNg==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.4.0.tgz",
|
||||
"integrity": "sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.0",
|
||||
@@ -14609,9 +14612,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rc-upload": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.6.0.tgz",
|
||||
"integrity": "sha512-Zr0DT1NHw/ApxrP7UAoxOtGaVYuzarrrCVr0ld7RiEFsKX07uFhE1EpCBxwL11ruFn89GMcshOKWp+s6FLyAlA==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-4.7.0.tgz",
|
||||
"integrity": "sha512-eUwxYNHlsYe5vYhKFAUGrQG95JrnPzY+BmPi1Daq39fWNl/eOc7v4UODuWrVp2LFkQBuV3cMCG/I68iub6oBrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"classnames": "^2.2.5",
|
||||
@@ -14670,9 +14674,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-big-calendar": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.13.2.tgz",
|
||||
"integrity": "sha512-yzeVRM1I+JloeJXytrZx2lJWKUfLAi5bsgGuBjh3aFSHZrdFcGnfA7LE6pBacdyOG+NGP+332m2MziszkmQWcw==",
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.13.3.tgz",
|
||||
"integrity": "sha512-PaZEQMhZCPiInxkKeP9S1TgI1e0Gd4aPpueYXS97rMa89AfX2ZGEmoMjDyeOkIz+mA1utuYF/yQYaxGPNXS0Sg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.7",
|
||||
@@ -14939,9 +14943,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-product-fruits": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react-product-fruits/-/react-product-fruits-2.2.6.tgz",
|
||||
"integrity": "sha512-f57m1rCD+Cu8QfFQSpkHJjLaWauPbD01GNrbh4icQaOHA/6bHJGpcoSSEQfAtk3g2PyQBpsDdRvL9jC+hyxhhQ==",
|
||||
"version": "2.2.61",
|
||||
"resolved": "https://registry.npmjs.org/react-product-fruits/-/react-product-fruits-2.2.61.tgz",
|
||||
"integrity": "sha512-zqmrtgwI6TLIR1g6pk9SzAutoKgC/7L1Y0ffsleymcAjSUxhHAla5A0bS2vMLq+WkgYKKH7Dl+Z9VxUywLEdeg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"product-fruits": "^1.0.25"
|
||||
},
|
||||
@@ -14992,12 +14997,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.0.tgz",
|
||||
"integrity": "sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg==",
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
|
||||
"integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.0"
|
||||
"@remix-run/router": "1.19.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -15007,13 +15012,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.0.tgz",
|
||||
"integrity": "sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ==",
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz",
|
||||
"integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.19.0",
|
||||
"react-router": "6.26.0"
|
||||
"@remix-run/router": "1.19.1",
|
||||
"react-router": "6.26.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -17551,14 +17556,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz",
|
||||
"integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==",
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.1.tgz",
|
||||
"integrity": "sha512-1oE6yuNXssjrZdblI9AfBbHCC41nnyoVoEZxQnID6yvQZAFBzxxkqoFLtHUMkYunL8hwOLEjgTuxpkRxvba3kA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.40",
|
||||
"postcss": "^8.4.41",
|
||||
"rollup": "^4.13.0"
|
||||
},
|
||||
"bin": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@splitsoftware/splitio-react": "^1.12.1",
|
||||
"@tanem/react-nprogress": "^5.0.51",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"antd": "^5.20.1",
|
||||
"antd": "^5.20.2",
|
||||
"apollo-link-logger": "^2.0.1",
|
||||
"apollo-link-sentry": "^3.3.0",
|
||||
"autosize": "^6.0.1",
|
||||
@@ -32,12 +32,12 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"env-cmd": "^10.1.0",
|
||||
"exifr": "^7.1.3",
|
||||
"firebase": "^10.12.5",
|
||||
"firebase": "^10.13.0",
|
||||
"graphql": "^16.9.0",
|
||||
"i18next": "^23.12.3",
|
||||
"i18next": "^23.14.0",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"libphonenumber-js": "^1.11.5",
|
||||
"libphonenumber-js": "^1.11.7",
|
||||
"logrocket": "^8.1.2",
|
||||
"markerjs2": "^2.32.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
@@ -47,7 +47,7 @@
|
||||
"query-string": "^9.1.0",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.13.2",
|
||||
"react-big-calendar": "^1.13.3",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
@@ -60,10 +60,10 @@
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-number-format": "^5.4.0",
|
||||
"react-popopo": "^2.1.9",
|
||||
"react-product-fruits": "^2.2.6",
|
||||
"react-product-fruits": "^2.2.61",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-resizable": "^3.0.5",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-sticky": "^6.0.3",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"react-virtuoso": "^4.10.1",
|
||||
@@ -147,7 +147,7 @@
|
||||
"react-error-overlay": "6.0.11",
|
||||
"redux-logger": "^3.0.6",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"vite": "^5.4.0",
|
||||
"vite": "^5.4.1",
|
||||
"vite-plugin-babel": "^1.2.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-legacy": "^2.1.0",
|
||||
|
||||
@@ -14,6 +14,7 @@ import dayjs from "../../utils/day";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
@@ -21,7 +22,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import { CalculateBillTotal } from "./bill-form.totals.utility";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -276,7 +276,7 @@ export function BillFormComponent({
|
||||
})
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly disabled={disabled} />
|
||||
<FormDatePicker disabled={disabled} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bills.fields.is_credit_memo")}
|
||||
|
||||
@@ -8,8 +8,8 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
||||
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import InputPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
@@ -196,7 +196,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
{dlExpiresBeforeReturn && (
|
||||
<Space style={{ color: "tomato" }}>
|
||||
@@ -274,7 +274,7 @@ export default function ContractFormComponent({ form, create = false, selectedJo
|
||||
<InputPhone />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("contracts.fields.driver_dob")} name="driver_dob">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<ContractsRatesChangeButton form={form} />
|
||||
|
||||
@@ -10,12 +10,16 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
export default function CourtesyCarCreateFormComponent({ form, saveLoading, newCC }) {
|
||||
export default function CourtesyCarCreateFormComponent({
|
||||
form,
|
||||
saveLoading,
|
||||
newCC,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
|
||||
@@ -157,16 +161,16 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("courtesycars.fields.purchasedate")} name="purchasedate">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("courtesycars.fields.servicestartdate")} name="servicestartdate">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("courtesycars.fields.serviceenddate")} name="serviceenddate">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("courtesycars.fields.leaseenddate")} name="leaseenddate">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
@@ -224,7 +228,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
|
||||
</div>
|
||||
<div>
|
||||
<Form.Item label={t("courtesycars.fields.nextservicedate")} name="nextservicedate">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate={(p, c) => p.nextservicedate !== c.nextservicedate}>
|
||||
{() => {
|
||||
@@ -256,7 +260,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
|
||||
</Form.Item>
|
||||
<div>
|
||||
<Form.Item label={t("courtesycars.fields.registrationexpires")} name="registrationexpires">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate={(p, c) => p.registrationexpires !== c.registrationexpires}>
|
||||
{() => {
|
||||
@@ -289,7 +293,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading, newC
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate={(p, c) => p.insuranceexpires !== c.insuranceexpires}>
|
||||
{() => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Form, InputNumber } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
export default function CourtesyCarReturnModalComponent() {
|
||||
const { t } = useTranslation();
|
||||
@@ -19,7 +19,7 @@ export default function CourtesyCarReturnModalComponent() {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("contracts.fields.kmend")}
|
||||
|
||||
@@ -24,9 +24,9 @@ import i18n from "../../translations/i18n";
|
||||
import dayjs from "../../utils/day";
|
||||
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
||||
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -164,7 +164,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item name="inservicedate" label={t("jobs.fields.dms.inservicedate")}>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Space>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Markdown from "react-markdown";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import { connect } from "react-redux";
|
||||
import { FormDatePicker } from "../form-date-picker/form-date-picker.component";
|
||||
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { acceptEula } from "../../redux/user/user.actions";
|
||||
@@ -11,7 +12,6 @@ import { useTranslation } from "react-i18next";
|
||||
import day from "../../utils/day";
|
||||
|
||||
import "./eula.styles.scss";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const Eula = ({ currentEula, currentUser, acceptEula }) => {
|
||||
const [formReady, setFormReady] = useState(false);
|
||||
@@ -216,7 +216,7 @@ const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly onChange={handleChange} onlyToday aria-label={t("eula.labels.date_accepted")} />
|
||||
<FormDatePicker onChange={handleChange} onlyToday aria-label={t("eula.labels.date_accepted")} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { DatePicker } from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, { useRef } from "react";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(FormDatePicker);
|
||||
|
||||
const dateFormat = "MM/DD/YYYY";
|
||||
|
||||
export function FormDatePicker({
|
||||
bodyshop,
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
onlyFuture,
|
||||
onlyToday,
|
||||
isDateOnly = true,
|
||||
...restProps
|
||||
}) {
|
||||
const ref = useRef();
|
||||
|
||||
const handleChange = (newDate) => {
|
||||
if (value !== newDate && onChange) {
|
||||
onChange(isDateOnly ? newDate && newDate.format("YYYY-MM-DD") : newDate);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key.toLowerCase() === "t") {
|
||||
if (onChange) {
|
||||
onChange(isDateOnly ? dayjs().format("YYYY-MM-DD") : dayjs());
|
||||
}
|
||||
} else if (e.key.toLowerCase() === "enter") {
|
||||
if (ref.current && ref.current.blur) ref.current.blur();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBlur = (e) => {
|
||||
const v = e.target.value;
|
||||
if (!v) return;
|
||||
|
||||
const formats = [
|
||||
"MMDDYY",
|
||||
"MMDDYYYY",
|
||||
"MM/DD/YY",
|
||||
"MM/DD/YYYY",
|
||||
"M/DD/YY",
|
||||
"M/DD/YYYY",
|
||||
"MM/D/YY",
|
||||
"MM/D/YYYY",
|
||||
"M/D/YY",
|
||||
"M/D/YYYY",
|
||||
"D/MM/YY",
|
||||
"D/MM/YYYY",
|
||||
"DD/M/YY",
|
||||
"DD/M/YYYY",
|
||||
"D/M/YY",
|
||||
"D/M/YYYY"
|
||||
];
|
||||
|
||||
let _a;
|
||||
|
||||
// Iterate through formats to find the correct one
|
||||
for (let format of formats) {
|
||||
_a = dayjs(v, format);
|
||||
if (v === _a.format(format)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_a.isValid() && value && value.isValid && value.isValid()) {
|
||||
_a.set({
|
||||
hours: value.hours(),
|
||||
minutes: value.minutes(),
|
||||
seconds: value.seconds(),
|
||||
milliseconds: value.milliseconds()
|
||||
});
|
||||
}
|
||||
|
||||
if (_a.isValid() && onChange) {
|
||||
if (onlyFuture) {
|
||||
if (dayjs().subtract(1, "day").isBefore(_a)) {
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
} else {
|
||||
onChange(isDateOnly ? dayjs().format("YYYY-MM-DD") : dayjs());
|
||||
}
|
||||
} else {
|
||||
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div onKeyDown={handleKeyDown}>
|
||||
<DatePicker
|
||||
ref={ref}
|
||||
value={value ? dayjs(value) : null}
|
||||
onChange={handleChange}
|
||||
format={dateFormat}
|
||||
onBlur={onBlur || handleBlur}
|
||||
showToday={false}
|
||||
disabledTime
|
||||
disabledDate={(d) => {
|
||||
if (onlyToday) {
|
||||
return !dayjs().isSame(d, "day");
|
||||
} else if (onlyFuture) {
|
||||
return dayjs().subtract(1, "day").isAfter(d);
|
||||
}
|
||||
}}
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { DatePicker } from "antd";
|
||||
import dayjs from "../../utils/day.js";
|
||||
import React, { useRef } from "react";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors.js";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(FormDateTimePickerEnhanced);
|
||||
|
||||
const dateFormat = "MM/DD/YYYY h:mm a";
|
||||
|
||||
export function FormDateTimePickerEnhanced({
|
||||
bodyshop,
|
||||
value,
|
||||
onBlur,
|
||||
onlyFuture,
|
||||
onlyToday,
|
||||
isDateOnly = true,
|
||||
...restProps
|
||||
}) {
|
||||
const ref = useRef();
|
||||
return (
|
||||
<div>
|
||||
<DatePicker
|
||||
ref={ref}
|
||||
value={value ? dayjs(value) : null}
|
||||
format={dateFormat}
|
||||
onBlur={onBlur}
|
||||
showToday={false}
|
||||
disabledDate={(d) => {
|
||||
if (onlyToday) {
|
||||
return !dayjs().isSame(d, "day");
|
||||
} else if (onlyFuture) {
|
||||
return dayjs().subtract(1, "day").isAfter(d);
|
||||
}
|
||||
}}
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,105 +1,46 @@
|
||||
import { DatePicker } from "antd";
|
||||
import PropTypes from "prop-types";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import React, { forwardRef } from "react";
|
||||
//import DatePicker from "react-datepicker";
|
||||
//import "react-datepicker/src/stylesheets/datepicker.scss";
|
||||
import { Space, TimePicker } from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import { fuzzyMatchDate } from "./formats.js";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
//To be used as a form element only.
|
||||
|
||||
const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, onlyToday, isDateOnly = false, ...restProps }) => {
|
||||
const [isManualInput, setIsManualInput] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChange = useCallback(
|
||||
(newDate) => {
|
||||
if (onChange) {
|
||||
onChange(newDate || null);
|
||||
}
|
||||
setIsManualInput(false);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const handleBlur = useCallback(
|
||||
(e) => {
|
||||
// Bail if this is not a manual input
|
||||
if (!isManualInput) {
|
||||
return;
|
||||
}
|
||||
// Reset manual input flag
|
||||
setIsManualInput(false);
|
||||
|
||||
const v = e?.target?.value;
|
||||
|
||||
if (!v) return;
|
||||
|
||||
let parsedDate = isDateOnly ? fuzzyMatchDate(v)?.startOf("day") : fuzzyMatchDate(v);
|
||||
|
||||
if (parsedDate && onChange) {
|
||||
onChange(parsedDate);
|
||||
}
|
||||
},
|
||||
[isManualInput, isDateOnly, onChange]
|
||||
);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e) => {
|
||||
setIsManualInput(true);
|
||||
|
||||
if (e.key.toLowerCase() === "t" && onChange) {
|
||||
e.preventDefault();
|
||||
setIsManualInput(false);
|
||||
onChange(dayjs());
|
||||
} else if (e.key.toLowerCase() === "enter") {
|
||||
handleBlur(e);
|
||||
}
|
||||
},
|
||||
[onChange, handleBlur]
|
||||
);
|
||||
|
||||
const handleDisabledDate = useCallback(
|
||||
(current) => {
|
||||
if (onlyToday) {
|
||||
return !dayjs().isSame(current, "day");
|
||||
} else if (onlyFuture) {
|
||||
return dayjs().subtract(1, "day").isAfter(current);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[onlyToday, onlyFuture]
|
||||
);
|
||||
const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, ...restProps }, ref) => {
|
||||
// const handleChange = (newDate) => {
|
||||
// if (value !== newDate && onChange) {
|
||||
// onChange(newDate);
|
||||
// }
|
||||
// };
|
||||
|
||||
return (
|
||||
<div onKeyDown={handleKeyDown} id={id} style={{ width: "100%" }}>
|
||||
<DatePicker
|
||||
showTime={
|
||||
isDateOnly
|
||||
? false
|
||||
: {
|
||||
format: "hh:mm a",
|
||||
minuteStep: 15,
|
||||
defaultValue: dayjs(dayjs(), "HH:mm:ss")
|
||||
}
|
||||
}
|
||||
format={isDateOnly ? "MM/DD/YYYY" : "MM/DD/YYYY hh:mm a"}
|
||||
<Space direction="vertical" style={{ width: "100%" }} id={id}>
|
||||
<FormDatePicker
|
||||
{...restProps}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => dayjs().subtract(1, "day").isAfter(d)
|
||||
})}
|
||||
value={value}
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
onlyFuture={onlyFuture}
|
||||
isDateOnly={false}
|
||||
/>
|
||||
|
||||
<TimePicker
|
||||
value={value ? dayjs(value) : null}
|
||||
onChange={handleChange}
|
||||
placeholder={isDateOnly ? t("general.labels.date") : t("general.labels.datetime")}
|
||||
onBlur={onBlur || handleBlur}
|
||||
disabledDate={handleDisabledDate}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => dayjs().isAfter(d)
|
||||
})}
|
||||
onChange={onChange}
|
||||
disableSeconds={true}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
DateTimePicker.propTypes = {
|
||||
value: PropTypes.any,
|
||||
onChange: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
id: PropTypes.string,
|
||||
onlyFuture: PropTypes.bool,
|
||||
onlyToday: PropTypes.bool,
|
||||
isDateOnly: PropTypes.bool
|
||||
};
|
||||
|
||||
export default React.memo(DateTimePicker);
|
||||
export default forwardRef(DateTimePicker);
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import dayjs from "../../utils/day";
|
||||
|
||||
const dateFormats = [
|
||||
"MMDDYYYY",
|
||||
"MMDDYY",
|
||||
"M/D/YYYY",
|
||||
"MM/D/YYYY",
|
||||
"M/DD/YYYY",
|
||||
"MM/DD/YYYY",
|
||||
"M/D/YY",
|
||||
"MM/D/YY",
|
||||
"M/DD/YY",
|
||||
"MM/DD/YY"
|
||||
];
|
||||
|
||||
const timeFormats = ["h:mm A", "h:mmA", "h A", "hA", "hh:mm A", "hh:mm:ss A"];
|
||||
|
||||
const dateTimeFormats = [
|
||||
...["M/D/YYYY", "MM/D/YYYY", "M/DD/YYYY", "MM/DD/YYYY", "M/D/YY", "MM/D/YY", "M/DD/YY", "MM/DD/YY"].flatMap(
|
||||
(dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`)
|
||||
),
|
||||
|
||||
...["MMDDYYYY", "MMDDYY"].flatMap((dateFormat) => timeFormats.map((timeFormat) => `${dateFormat} ${timeFormat}`)),
|
||||
|
||||
"M/D/YYYY",
|
||||
"MM/D/YYYY",
|
||||
"M/DD/YYYY",
|
||||
"MM/DD/YYYY",
|
||||
"M/D/YY",
|
||||
"MM/D/YY",
|
||||
"M/DD/YY",
|
||||
"MM/DD/YY",
|
||||
"MMDDYYYY",
|
||||
"MMDDYY"
|
||||
];
|
||||
|
||||
const sanitizeInput = (input) =>
|
||||
input
|
||||
.trim()
|
||||
.toUpperCase()
|
||||
.replace(/\s*(am|pm)\s*/i, " $1")
|
||||
.replaceAll(".", "/")
|
||||
.replaceAll("-", "/");
|
||||
|
||||
export const fuzzyMatchDate = (dateString) => {
|
||||
const sanitizedInput = sanitizeInput(dateString);
|
||||
|
||||
for (const format of dateFormats) {
|
||||
const parsedDate = dayjs(sanitizedInput, format, true);
|
||||
if (parsedDate.isValid()) {
|
||||
return parsedDate;
|
||||
}
|
||||
}
|
||||
|
||||
for (const format of dateTimeFormats) {
|
||||
const parsedDateTime = dayjs(sanitizedInput, format, true);
|
||||
if (parsedDateTime.isValid()) {
|
||||
return parsedDateTime; // Return the dayjs object
|
||||
}
|
||||
}
|
||||
|
||||
return null; // If no matching format is found
|
||||
};
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
QUERY_SCOREBOARD_ENTRY,
|
||||
UPDATE_SCOREBOARD_ENTRY
|
||||
} from "../../graphql/scoreboard.queries";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
export default function ScoreboardAddButton({ job, disabled, ...otherBtnProps }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -86,7 +86,7 @@ export default function ScoreboardAddButton({ job, disabled, ...otherBtnProps })
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("scoreboard.fields.bodyhrs")}
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
@@ -19,14 +20,7 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||
dispatch(
|
||||
insertAuditTrail({
|
||||
jobid,
|
||||
operation,
|
||||
type
|
||||
})
|
||||
)
|
||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminDatesChange);
|
||||
@@ -93,7 +87,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
||||
<FormFieldsChanged form={form} />
|
||||
<LayoutFormRow header={t("jobs.forms.estdates")}>
|
||||
<Form.Item label={t("jobs.fields.date_estimated")} name="date_estimated">
|
||||
<DateTimePicker format="MM/DD/YYYY" isDateOnly />
|
||||
<FormDatePicker format="MM/DD/YYYY" />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
|
||||
<DateTimePicker />
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Collapse, Form, Input, InputNumber, Select, Space, Switch } from "antd";
|
||||
import {
|
||||
Collapse,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||
@@ -20,7 +29,6 @@ import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.c
|
||||
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -53,7 +61,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.regie_number")} name="regie_number">
|
||||
<Form.Item
|
||||
label={t("jobs.fields.regie_number")}
|
||||
name="regie_number"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.ins_co_nm")} name="ins_co_nm">
|
||||
@@ -105,7 +116,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
|
||||
<FormItemEmail email={getFieldValue("ins_ea")} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
|
||||
<Input />
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Form, Input } from "antd";
|
||||
import React, { useContext } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import JobsCreateVehicleInfoPredefined from "./jobs-create-vehicle-info.predefined.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
export default function JobsCreateVehicleInfoNewComponent({ form }) {
|
||||
const [state] = useContext(JobCreateContext);
|
||||
@@ -113,7 +113,7 @@ export default function JobsCreateVehicleInfoNewComponent({ form }) {
|
||||
<Input disabled={!state.vehicle.new} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vehicles.fields.v_prod_dt")} name={["vehicle", "data", "v_prod_dt"]}>
|
||||
<DateTimePicker isDateOnly disabled={!state.vehicle.new} />
|
||||
<FormDatePicker disabled={!state.vehicle.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
@@ -29,7 +30,7 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
<div>
|
||||
<FormRow header={t("jobs.forms.estdates")}>
|
||||
<Form.Item label={t("jobs.fields.date_estimated")} name="date_estimated">
|
||||
<DateTimePicker disabled={jobRO} isDateOnly />
|
||||
<FormDatePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
||||
<DateTimePicker disabled={jobRO} />
|
||||
@@ -44,7 +45,7 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
|
||||
<FormRow header={t("jobs.forms.scheddates")}>
|
||||
<Form.Item label={t("jobs.fields.date_scheduled")} name="date_scheduled">
|
||||
<DateTimePicker disabled={jobRO} isDateOnly />
|
||||
<FormDatePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Tooltip title={t("jobs.labels.scheduledinchange")}>
|
||||
<Form.Item label={t("jobs.fields.scheduled_in")} name="scheduled_in">
|
||||
@@ -84,6 +85,7 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
rules={[
|
||||
{
|
||||
required: jobInPostProduction
|
||||
//message: t("general.validation.required"),
|
||||
}
|
||||
]}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||
@@ -12,7 +13,6 @@ import Car from "../job-damage-visual/job-damage-visual.component";
|
||||
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -152,7 +152,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
|
||||
<DateTimePicker isDateOnly disabled={jobRO} />
|
||||
<FormDatePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
|
||||
<Input disabled={jobRO} />
|
||||
|
||||
@@ -23,7 +23,7 @@ export function PartnerPingComponent({ bodyshop }) {
|
||||
// Execute the created function directly
|
||||
checkPartnerStatus(bodyshop);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bodyshop?.id]);
|
||||
}, [bodyshop]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { MUTATION_UPDATE_BO_ETA } from "../../graphql/parts-orders.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import { CalendarFilled } from "@ant-design/icons";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -62,7 +62,7 @@ export function PartsOrderBackorderEta({
|
||||
<div>
|
||||
<Form form={form} onFinish={handleFinish}>
|
||||
<Form.Item name="eta">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("general.actions.save")}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { MUTATION_BACKORDER_PART_LINE } from "../../graphql/parts-orders.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -71,7 +71,7 @@ export function PartsOrderLineBackorderButton({ partsOrderStatus, partsLineId, j
|
||||
<div>
|
||||
<Form form={form} onFinish={handleFinish}>
|
||||
<Form.Item name="eta">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Button type="primary" onClick={() => form.submit()}>
|
||||
{t("parts_orders.actions.backordered")}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { DeleteFilled, EyeFilled } from "@ant-design/icons";
|
||||
import { PageHeader } from "@ant-design/pro-layout";
|
||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||
import { Button, Drawer, Grid, Popconfirm, Space, Table } from "antd";
|
||||
|
||||
import queryString from "query-string";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -82,34 +83,47 @@ export function PartsOrderListTableDrawerComponent({
|
||||
sortedInfo: {}
|
||||
});
|
||||
|
||||
const [billData, setBillData] = useState(null);
|
||||
const [returnfrombill, setReturnFromBill] = useState();
|
||||
const [billData, setBillData] = useState();
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const selectedpartsorder = search.partsorderid;
|
||||
|
||||
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
|
||||
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
||||
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
||||
const { refetch } = billsQuery;
|
||||
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
|
||||
const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (selectedPartsOrderRecord?.returnfrombill) {
|
||||
try {
|
||||
const { data } = await billQuery({
|
||||
variables: { billid: selectedPartsOrderRecord.returnfrombill }
|
||||
});
|
||||
setBillData(data);
|
||||
} catch (error) {
|
||||
console.error("Error fetching bill data:", error);
|
||||
}
|
||||
} else setBillData(null);
|
||||
};
|
||||
fetchData();
|
||||
}, [selectedPartsOrderRecord, billQuery]);
|
||||
if (returnfrombill === null) {
|
||||
setBillData(null);
|
||||
} else {
|
||||
const fetchData = async () => {
|
||||
const result = await billQuery({
|
||||
variables: { billid: returnfrombill }
|
||||
});
|
||||
setBillData(result.data);
|
||||
};
|
||||
fetchData();
|
||||
}
|
||||
}, [returnfrombill, billQuery]);
|
||||
|
||||
const recordActions = (record) => (
|
||||
const recordActions = (record, showView = false) => (
|
||||
<Space direction="horizontal" wrap>
|
||||
{showView && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (record.returnfrombill) {
|
||||
setReturnFromBill(record.returnfrombill);
|
||||
} else {
|
||||
setReturnFromBill(null);
|
||||
}
|
||||
handleOnRowClick(record);
|
||||
}}
|
||||
>
|
||||
<EyeFilled />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
disabled={jobRO || record.return || record.vendor.id === bodyshop.inhousevendorid}
|
||||
onClick={() => {
|
||||
@@ -119,14 +133,16 @@ export function PartsOrderListTableDrawerComponent({
|
||||
context: {
|
||||
jobId: job.id,
|
||||
job: job,
|
||||
partsorderlines: record.parts_order_lines.map((pol) => ({
|
||||
joblineid: pol.job_line_id,
|
||||
id: pol.id,
|
||||
line_desc: pol.line_desc,
|
||||
quantity: pol.quantity,
|
||||
act_price: pol.act_price,
|
||||
oem_partno: pol.oem_partno
|
||||
}))
|
||||
partsorderlines: record.parts_order_lines.map((pol) => {
|
||||
return {
|
||||
joblineid: pol.job_line_id,
|
||||
id: pol.id,
|
||||
line_desc: pol.line_desc,
|
||||
quantity: pol.quantity,
|
||||
act_price: pol.act_price,
|
||||
oem_partno: pol.oem_partno
|
||||
};
|
||||
})
|
||||
}
|
||||
});
|
||||
}}
|
||||
@@ -151,6 +167,7 @@ export function PartsOrderListTableDrawerComponent({
|
||||
disabled={jobRO}
|
||||
onConfirm={async () => {
|
||||
//Delete the parts return.!
|
||||
|
||||
await deletePartsOrder({
|
||||
variables: { partsOrderId: record.id },
|
||||
update(cache) {
|
||||
@@ -174,6 +191,7 @@ export function PartsOrderListTableDrawerComponent({
|
||||
disabled={(jobRO ? !record.return : jobRO) || record.vendor.id === bodyshop.inhousevendorid}
|
||||
onClick={() => {
|
||||
logImEXEvent("parts_order_receive_bill");
|
||||
|
||||
setBillEnterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
@@ -181,20 +199,24 @@ export function PartsOrderListTableDrawerComponent({
|
||||
bill: {
|
||||
vendorid: record.vendor.id,
|
||||
is_credit_memo: record.return,
|
||||
billlines: record.parts_order_lines.map((pol) => ({
|
||||
joblineid: pol.job_line_id || "noline",
|
||||
line_desc: pol.line_desc,
|
||||
quantity: pol.quantity,
|
||||
actual_price: pol.act_price,
|
||||
cost_center: pol.jobline?.part_type
|
||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||
? pol.jobline.part_type !== "PAE"
|
||||
? pol.jobline.part_type
|
||||
: null
|
||||
: responsibilityCenters.defaults &&
|
||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||
: null
|
||||
}))
|
||||
billlines: record.parts_order_lines.map((pol) => {
|
||||
return {
|
||||
joblineid: pol.job_line_id || "noline",
|
||||
line_desc: pol.line_desc,
|
||||
quantity: pol.quantity,
|
||||
|
||||
actual_price: pol.act_price,
|
||||
|
||||
cost_center: pol.jobline?.part_type
|
||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||
? pol.jobline.part_type !== "PAE"
|
||||
? pol.jobline.part_type
|
||||
: null
|
||||
: responsibilityCenters.defaults &&
|
||||
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
|
||||
: null
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -221,6 +243,8 @@ export function PartsOrderListTableDrawerComponent({
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
|
||||
|
||||
const rowExpander = (record) => {
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -6,12 +6,12 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import PartsOrderModalPriceChange from "./parts-order-modal-price-change.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -74,7 +74,7 @@ export function PartsOrderModalComponent({ bodyshop, vendorList, sendTypeState,
|
||||
]}
|
||||
label={t("parts_orders.fields.deliver_by")}
|
||||
>
|
||||
<DateTimePicker isDateOnly onlyFuture />
|
||||
<FormDatePicker onlyFuture />
|
||||
</Form.Item>
|
||||
{job && job.special_coverage_policy && (
|
||||
<Tag color="tomato">
|
||||
|
||||
@@ -5,11 +5,11 @@ import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import DatePickerFormItem from "../form-date-picker/form-date-picker.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import PaymentFormTotalPayments from "./payment-form.totalpayments.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -77,7 +77,7 @@ export function PaymentFormComponent({ form, bodyshop, disabled }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly disabled={disabled} />
|
||||
<DatePickerFormItem disabled={disabled} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
|
||||
@@ -28,11 +28,6 @@ import ProductionListColumnCategory from "./production-list-columns.status.categ
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionListColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
|
||||
const getEmployeeName = (employeeId, employees) => {
|
||||
const employee = employees.find((e) => e.id === employeeId);
|
||||
return employee ? `${employee.first_name} ${employee.last_name}` : "";
|
||||
};
|
||||
|
||||
const r = ({ technician, state, activeStatuses, data, bodyshop, refetch, treatments }) => {
|
||||
const { Enhanced_Payroll } = treatments;
|
||||
return [
|
||||
@@ -431,8 +426,8 @@ const r = ({ technician, state, activeStatuses, data, bodyshop, refetch, treatme
|
||||
sortOrder: state.sortedInfo.columnKey === "employee_body" && state.sortedInfo.order,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
getEmployeeName(a.employee_body, bodyshop.employees),
|
||||
getEmployeeName(b.employee_body, bodyshop.employees)
|
||||
bodyshop.employees?.find((e) => e.id === a.employee_body)?.first_name,
|
||||
bodyshop.employees?.find((e) => e.id === b.employee_body)?.first_name
|
||||
),
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment refetch={refetch} record={record} type="employee_body" />
|
||||
@@ -445,8 +440,8 @@ const r = ({ technician, state, activeStatuses, data, bodyshop, refetch, treatme
|
||||
sortOrder: state.sortedInfo.columnKey === "employee_prep" && state.sortedInfo.order,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
getEmployeeName(a.employee_prep, bodyshop.employees),
|
||||
getEmployeeName(b.employee_prep, bodyshop.employees)
|
||||
bodyshop.employees?.find((e) => e.id === a.employee_prep)?.first_name,
|
||||
bodyshop.employees?.find((e) => e.id === b.employee_prep)?.first_name
|
||||
),
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment record={record} refetch={refetch} type="employee_prep" />
|
||||
@@ -465,8 +460,8 @@ const r = ({ technician, state, activeStatuses, data, bodyshop, refetch, treatme
|
||||
sortOrder: state.sortedInfo.columnKey === "employee_csr" && state.sortedInfo.order,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
getEmployeeName(a.employee_csr, bodyshop.employees),
|
||||
getEmployeeName(b.employee_csr, bodyshop.employees)
|
||||
bodyshop.employees?.find((e) => e.id === a.employee_csr)?.first_name,
|
||||
bodyshop.employees?.find((e) => e.id === b.employee_csr)?.first_name
|
||||
),
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment refetch={refetch} record={record} type="employee_csr" />
|
||||
@@ -479,8 +474,8 @@ const r = ({ technician, state, activeStatuses, data, bodyshop, refetch, treatme
|
||||
sortOrder: state.sortedInfo.columnKey === "employee_refinish" && state.sortedInfo.order,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
getEmployeeName(a.employee_refinish, bodyshop.employees),
|
||||
getEmployeeName(b.employee_refinish, bodyshop.employees)
|
||||
bodyshop.employees?.find((e) => e.id === a.employee_refinish)?.first_name,
|
||||
bodyshop.employees?.find((e) => e.id === b.employee_refinish)?.first_name
|
||||
),
|
||||
render: (text, record) => (
|
||||
<ProductionListEmployeeAssignment record={record} refetch={refetch} type="employee_refinish" />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Card, Dropdown, Space } from "antd";
|
||||
import { Button, Card, Dropdown, Space, TimePicker } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import dayjs from "../../utils/day";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
export default function ProductionListDate({ record, field, time, pastIndicator }) {
|
||||
const [updateAlert] = useMutation(UPDATE_JOB);
|
||||
@@ -57,14 +57,22 @@ export default function ProductionListDate({ record, field, time, pastIndicator
|
||||
label: (
|
||||
<Card style={{ padding: "1rem" }} onClick={(e) => e.stopPropagation()}>
|
||||
<Space direction={"vertical"}>
|
||||
<DateTimePicker
|
||||
<FormDatePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && dayjs(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
format={time ? "MM/DD/YYYY hh:mm a" : "MM/DD/YYYY"}
|
||||
format="MM/DD/YYYY"
|
||||
isDateOnly={!time}
|
||||
showTime={time ? { format: "hh:mm a", minuteStep: 15 } : false}
|
||||
/>
|
||||
{time && (
|
||||
<TimePicker
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && dayjs(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
minuteStep={15}
|
||||
format="hh:mm a"
|
||||
/>
|
||||
)}
|
||||
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { Button, Form, Input, notification, Popover, Space } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { isFunction } from "lodash";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function ProductionListSaveConfigButton({ columns, bodyshop, tableState, onSave }) {
|
||||
const [updateShop] = useMutation(UPDATE_SHOP);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSaveConfig = async (values) => {
|
||||
logImEXEvent("production_save_config");
|
||||
setLoading(true);
|
||||
const result = await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: [
|
||||
...bodyshop.production_config.filter((b) => b.name !== values.name),
|
||||
//Assign it to the name
|
||||
{
|
||||
name: values.name,
|
||||
columns: {
|
||||
columnKeys: columns.map((i) => {
|
||||
return { key: i.key, width: i.width };
|
||||
}),
|
||||
tableState
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!!!result.errors) {
|
||||
notification["success"]({ message: t("bodyshop.successes.save") });
|
||||
if (onSave && isFunction(onSave)) {
|
||||
onSave();
|
||||
}
|
||||
} else {
|
||||
notification["error"]({
|
||||
message: t("bodyshop.errors.saving", {
|
||||
error: JSON.stringify(result.errors)
|
||||
})
|
||||
});
|
||||
}
|
||||
form.resetFields();
|
||||
setOpen(false);
|
||||
setLoading(false);
|
||||
};
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form layout="vertical" form={form} onFinish={handleSaveConfig}>
|
||||
<Form.Item label={t("production.labels.viewname")} name="name" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<Button type="primary" danger onClick={() => form.submit()} loading={loading}>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover open={open} content={popMenu}>
|
||||
<Button loading={loading} onClick={() => setOpen(true)}>
|
||||
{t("production.actions.saveconfig")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ProductionListSaveConfigButton);
|
||||
@@ -1,505 +0,0 @@
|
||||
import { DeleteOutlined, ExclamationCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_ACTIVE_PROD_LIST_VIEW } from "../../graphql/associations.queries";
|
||||
import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { isFunction } from "lodash";
|
||||
|
||||
const { confirm } = Modal;
|
||||
|
||||
export function ProductionListConfigManager({
|
||||
refetch,
|
||||
bodyshop,
|
||||
technician,
|
||||
currentUser,
|
||||
state,
|
||||
data,
|
||||
columns,
|
||||
setColumns,
|
||||
setState,
|
||||
onSave,
|
||||
hasUnsavedChanges,
|
||||
setHasUnsavedChanges
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
|
||||
const [updateShop] = useMutation(UPDATE_SHOP);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [isAddingNewProfile, setIsAddingNewProfile] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [activeView, setActiveView] = useState(() => {
|
||||
const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
|
||||
return assoc && assoc.default_prod_list_view;
|
||||
});
|
||||
|
||||
const defaultState = {
|
||||
sortedInfo: {
|
||||
columnKey: "ro_number",
|
||||
order: null
|
||||
},
|
||||
filteredInfo: {}
|
||||
};
|
||||
|
||||
const ensureDefaultState = (state) => {
|
||||
return {
|
||||
sortedInfo: state?.sortedInfo || defaultState.sortedInfo,
|
||||
filteredInfo: state?.filteredInfo || defaultState.filteredInfo,
|
||||
...state
|
||||
};
|
||||
};
|
||||
|
||||
const createDefaultView = async () => {
|
||||
const defaultConfig = {
|
||||
name: t("production.constants.main_profile"),
|
||||
columns: {
|
||||
columnKeys: [
|
||||
{ key: "ro_number", width: 100 },
|
||||
{ key: "ownr", width: 100 },
|
||||
{ key: "vehicle", width: 100 },
|
||||
{ key: "ins_co_nm", width: 100 },
|
||||
{ key: "actual_in", width: 100 },
|
||||
{ key: "scheduled_completion", width: 100 },
|
||||
{ key: "labhrs", width: 100 },
|
||||
{ key: "employee_body", width: 100 },
|
||||
{ key: "larhrs", width: 100 },
|
||||
{ key: "employee_refinish", width: 100 },
|
||||
{ key: "tt", width: 100 },
|
||||
{ key: "status", width: 100 },
|
||||
{ key: "sublets", width: 100 },
|
||||
{ key: "viewdetail", width: 100 }
|
||||
],
|
||||
tableState: ensureDefaultState(state)
|
||||
}
|
||||
};
|
||||
|
||||
const result = await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: [defaultConfig]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
await updateActiveProdView(t("production.constants.main_profile"));
|
||||
window.location.reload(); // Reload the page
|
||||
} else {
|
||||
notification.error({
|
||||
message: t("bodyshop.errors.creatingdefaultview", {
|
||||
error: JSON.stringify(result.errors)
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
treatments: { Enhanced_Payroll }
|
||||
} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid
|
||||
});
|
||||
|
||||
const updateActiveProdView = async (viewName) => {
|
||||
const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
|
||||
if (assoc) {
|
||||
await updateDefaultProdView({
|
||||
variables: { assocId: assoc.id, view: viewName },
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
id: cache.identify(bodyshop),
|
||||
fields: {
|
||||
associations(existingAssociations) {
|
||||
return existingAssociations.map((a) => {
|
||||
if (a.useremail !== currentUser.email) return a;
|
||||
return { ...a, default_prod_list_view: viewName };
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
setActiveView(viewName);
|
||||
setHasUnsavedChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = async (value) => {
|
||||
if (hasUnsavedChanges) {
|
||||
confirm({
|
||||
title: t("general.labels.unsavedchanges"),
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: t("general.messages.unsavedchangespopup"),
|
||||
onOk: () => proceedWithSelect(value),
|
||||
onCancel() {
|
||||
// Do nothing if canceled
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await proceedWithSelect(value);
|
||||
}
|
||||
};
|
||||
|
||||
const proceedWithSelect = async (value) => {
|
||||
if (value === "add_new") {
|
||||
setIsAddingNewProfile(true);
|
||||
setOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedConfig = bodyshop.production_config.find((pc) => pc.name === value);
|
||||
|
||||
// If the selected profile doesn't exist, revert to the main profile
|
||||
if (!selectedConfig) {
|
||||
const mainProfileConfig = bodyshop.production_config.find(
|
||||
(pc) => pc.name === t("production.constants.main_profile")
|
||||
);
|
||||
|
||||
if (mainProfileConfig) {
|
||||
await updateActiveProdView(t("production.constants.main_profile"));
|
||||
setColumns(
|
||||
mainProfileConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
bodyshop,
|
||||
refetch,
|
||||
technician,
|
||||
state: ensureDefaultState(state),
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
})
|
||||
);
|
||||
const newState = ensureDefaultState(mainProfileConfig.columns.tableState);
|
||||
setState(newState);
|
||||
|
||||
if (onSave && isFunction(onSave)) {
|
||||
onSave();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If the selected profile exists, proceed as normal
|
||||
if (selectedConfig) {
|
||||
const newColumns = selectedConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
bodyshop,
|
||||
refetch,
|
||||
technician,
|
||||
state: ensureDefaultState(state),
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
});
|
||||
setColumns(newColumns);
|
||||
const newState = ensureDefaultState(selectedConfig.columns.tableState);
|
||||
setState(newState);
|
||||
|
||||
await updateActiveProdView(value);
|
||||
if (onSave && isFunction(onSave)) {
|
||||
onSave();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrash = async (name) => {
|
||||
if (name === t("production.constants.main_profile")) return;
|
||||
|
||||
const remainingConfigs = bodyshop.production_config.filter((b) => b.name !== name);
|
||||
|
||||
await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: remainingConfigs
|
||||
}
|
||||
},
|
||||
awaitRefetchQueries: true
|
||||
});
|
||||
|
||||
if (name === activeView) {
|
||||
// Only switch profiles if the deleted profile was the active profile
|
||||
if (remainingConfigs.length > 0) {
|
||||
const nextConfig = remainingConfigs[0];
|
||||
await updateActiveProdView(nextConfig.name);
|
||||
setColumns(
|
||||
nextConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state: ensureDefaultState(state),
|
||||
refetch,
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
})
|
||||
);
|
||||
setState(ensureDefaultState(nextConfig.columns.tableState));
|
||||
} else {
|
||||
await updateActiveProdView(null);
|
||||
setColumns([]);
|
||||
setState(defaultState);
|
||||
}
|
||||
} else {
|
||||
// Revert back to the active view and load its columns and state
|
||||
const activeConfig = bodyshop.production_config.find((pc) => pc.name === activeView);
|
||||
if (activeConfig) {
|
||||
await updateActiveProdView(activeView);
|
||||
setColumns(
|
||||
activeConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state: ensureDefaultState(state),
|
||||
refetch,
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
})
|
||||
);
|
||||
setState(ensureDefaultState(activeConfig.columns.tableState));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveConfig = async (values) => {
|
||||
logImEXEvent("production_save_config");
|
||||
setLoading(true);
|
||||
|
||||
const profileName = isAddingNewProfile ? values.name : activeView;
|
||||
|
||||
const result = await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: [
|
||||
...bodyshop.production_config.filter((b) => b.name !== profileName),
|
||||
{
|
||||
name: profileName,
|
||||
columns: {
|
||||
columnKeys: columns.map((i) => ({ key: i.key, width: i.width })),
|
||||
tableState: ensureDefaultState(state)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
notification.success({ message: t("bodyshop.successes.save") });
|
||||
if (isAddingNewProfile) {
|
||||
await updateActiveProdView(profileName);
|
||||
}
|
||||
if (onSave && isFunction(onSave)) {
|
||||
onSave();
|
||||
}
|
||||
setHasUnsavedChanges(false);
|
||||
} else {
|
||||
notification.error({
|
||||
message: t("bodyshop.errors.saving", {
|
||||
error: JSON.stringify(result.errors)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
form.resetFields();
|
||||
setOpen(false);
|
||||
setLoading(false);
|
||||
setIsAddingNewProfile(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const validateAndSetDefaultView = () => {
|
||||
const configExists = bodyshop.production_config.some((pc) => pc.name === activeView);
|
||||
|
||||
if (!configExists) {
|
||||
// If the default view doesn't exist, revert to the main profile
|
||||
const mainProfileConfig = bodyshop.production_config.find(
|
||||
(pc) => pc.name === t("production.constants.main_profile")
|
||||
);
|
||||
|
||||
if (mainProfileConfig) {
|
||||
setActiveView(t("production.constants.main_profile"));
|
||||
|
||||
setColumns(
|
||||
mainProfileConfig.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
bodyshop,
|
||||
refetch,
|
||||
technician,
|
||||
state: ensureDefaultState(state),
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
})
|
||||
);
|
||||
setState(ensureDefaultState(mainProfileConfig.columns.tableState));
|
||||
|
||||
updateActiveProdView(t("production.constants.main_profile"));
|
||||
}
|
||||
} else {
|
||||
// If the default view exists, set it as active
|
||||
setActiveView(activeView);
|
||||
}
|
||||
};
|
||||
|
||||
if (!bodyshop.production_config || bodyshop.production_config.length === 0) {
|
||||
createDefaultView().catch((e) => {
|
||||
console.error("Something went wrong saving the production list view Config.");
|
||||
});
|
||||
} else {
|
||||
validateAndSetDefaultView();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [activeView, bodyshop.production_config]);
|
||||
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form layout="vertical" form={form} onFinish={handleSaveConfig}>
|
||||
{isAddingNewProfile && (
|
||||
<Form.Item
|
||||
label={t("production.labels.viewname")}
|
||||
name="name"
|
||||
rules={[
|
||||
{ required: true, message: t("production.errors.name_required") },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (!value) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const nameExists = bodyshop.production_config.some((pc) => pc.name === value);
|
||||
if (nameExists) {
|
||||
return Promise.reject(new Error(t("production.errors.name_exists")));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Space wrap>
|
||||
<Button
|
||||
type="primary"
|
||||
danger
|
||||
onClick={() => form.submit()}
|
||||
loading={loading}
|
||||
disabled={form.getFieldsError().some(({ errors }) => errors.length)}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
{!isAddingNewProfile && (
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => {
|
||||
setIsAddingNewProfile(true);
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
{t("general.actions.saveas")}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setIsAddingNewProfile(false);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
{t("general.actions.cancel")}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Space>
|
||||
<Button loading={loading} onClick={() => setOpen(true)} disabled={isAddingNewProfile || !hasUnsavedChanges}>
|
||||
{t("production.actions.saveconfig")}
|
||||
</Button>
|
||||
<Popover open={open} content={popMenu} placement="bottom">
|
||||
<Select
|
||||
style={{
|
||||
minWidth: "150px"
|
||||
}}
|
||||
onSelect={handleSelect}
|
||||
placeholder={t("production.labels.selectview")}
|
||||
optionLabelProp="label"
|
||||
popupMatchSelectWidth={false}
|
||||
value={activeView}
|
||||
disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile
|
||||
>
|
||||
{bodyshop.production_config
|
||||
.slice()
|
||||
.sort((a, b) =>
|
||||
a.name === t("production.constants.main_profile")
|
||||
? -1
|
||||
: b.name === t("production.constants.main_profile")
|
||||
? 1
|
||||
: 0
|
||||
) //
|
||||
.map((config) => (
|
||||
<Select.Option key={config.name} label={config.name}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||
<span
|
||||
style={{
|
||||
flex: 1,
|
||||
maxWidth: "80%",
|
||||
marginRight: "1rem",
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
>
|
||||
{config.name}
|
||||
</span>
|
||||
{config.name !== t("production.constants.main_profile") && (
|
||||
<Popconfirm
|
||||
placement="right"
|
||||
title={t("general.labels.areyousure")}
|
||||
onConfirm={() => handleTrash(config.name)}
|
||||
onCancel={(e) => e.stopPropagation()}
|
||||
>
|
||||
<DeleteOutlined onClick={(e) => e.stopPropagation()} />
|
||||
</Popconfirm>
|
||||
)}
|
||||
</div>
|
||||
</Select.Option>
|
||||
))}
|
||||
<Select.Option key="add_new" label={t("production.labels.addnewprofile")}>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<PlusOutlined style={{ marginRight: "0.5rem" }} />
|
||||
{t("production.labels.addnewprofile")}
|
||||
</div>
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Popover>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import { DeleteOutlined } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Popconfirm, Select } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_ACTIVE_PROD_LIST_VIEW } from "../../graphql/associations.queries";
|
||||
import { UPDATE_SHOP } from "../../graphql/bodyshop.queries";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { isFunction } from "lodash";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
currentUser: selectCurrentUser
|
||||
});
|
||||
|
||||
export function ProductionListTable({
|
||||
refetch,
|
||||
bodyshop,
|
||||
technician,
|
||||
currentUser,
|
||||
state,
|
||||
data,
|
||||
setColumns,
|
||||
setState,
|
||||
onProfileChange
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW);
|
||||
const [updateShop] = useMutation(UPDATE_SHOP);
|
||||
|
||||
const {
|
||||
treatments: { Enhanced_Payroll }
|
||||
} = useSplitTreatments({
|
||||
attributes: {},
|
||||
names: ["Enhanced_Payroll"],
|
||||
splitKey: bodyshop.imexshopid
|
||||
});
|
||||
|
||||
const handleSelect = async (value, option) => {
|
||||
const newColumns = bodyshop.production_config
|
||||
.filter((pc) => pc.name === value)[0]
|
||||
.columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
bodyshop,
|
||||
refetch,
|
||||
technician,
|
||||
state,
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
});
|
||||
setColumns(newColumns);
|
||||
const newState = bodyshop.production_config.filter((pc) => pc.name === value)[0].columns.tableState;
|
||||
setState(newState);
|
||||
|
||||
const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
|
||||
|
||||
if (assoc) {
|
||||
await updateDefaultProdView({
|
||||
variables: { assocId: assoc.id, view: value },
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
id: cache.identify(bodyshop),
|
||||
fields: {
|
||||
associations(existingAssociations, { readField }) {
|
||||
return existingAssociations.map((a) => {
|
||||
if (a.useremail !== currentUser.email) return a;
|
||||
return { ...a, default_prod_list_view: value };
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (onProfileChange && isFunction(onProfileChange)) {
|
||||
onProfileChange({ value, option, newColumns, newState, assoc });
|
||||
}
|
||||
};
|
||||
|
||||
const handleTrash = async (name) => {
|
||||
await updateShop({
|
||||
variables: {
|
||||
id: bodyshop.id,
|
||||
shop: {
|
||||
production_config: bodyshop.production_config.filter((b) => b.name !== name)
|
||||
}
|
||||
},
|
||||
awaitRefetchQueries: true
|
||||
});
|
||||
|
||||
setColumns(
|
||||
bodyshop.production_config[0].columns.columnKeys.map((k) => {
|
||||
return {
|
||||
...ProductionListColumns({
|
||||
technician,
|
||||
state,
|
||||
refetch,
|
||||
data: data,
|
||||
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
|
||||
treatments: { Enhanced_Payroll }
|
||||
}).find((e) => e.key === k.key),
|
||||
width: k.width
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
setState(bodyshop.production_config[0].columns.tableState);
|
||||
};
|
||||
const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email);
|
||||
|
||||
const defaultView = assoc && assoc.default_prod_list_view;
|
||||
return (
|
||||
<div style={{ width: "10rem" }}>
|
||||
<Select
|
||||
onSelect={handleSelect}
|
||||
placeholder={t("production.labels.selectview")}
|
||||
optionLabelProp="label"
|
||||
popupMatchSelectWidth={false}
|
||||
defaultValue={defaultView}
|
||||
>
|
||||
{bodyshop.production_config.map((config) => (
|
||||
<Select.Option key={config.name} label={config.name}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
flex: 1,
|
||||
maxWidth: "80%",
|
||||
marginRight: "1rem",
|
||||
textOverflow: "ellipsis"
|
||||
}}
|
||||
>
|
||||
{config.name}
|
||||
</span>
|
||||
|
||||
<Popconfirm
|
||||
placement="right"
|
||||
title={t("general.labels.areyousure")}
|
||||
onConfirm={() => handleTrash(config.name)}
|
||||
>
|
||||
<DeleteOutlined
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(ProductionListTable);
|
||||
@@ -10,14 +10,15 @@ import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selecto
|
||||
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
||||
import ProductionListSaveConfigButton from "../production-list-save-config-button/production-list-save-config-button.component";
|
||||
import ProductionListPrint from "./production-list-print.component";
|
||||
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
|
||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import Prompt from "../../utils/prompt.js";
|
||||
import _ from "lodash";
|
||||
import AlertComponent from "../alert/alert.component.jsx";
|
||||
import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -269,24 +270,24 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
|
||||
data={data}
|
||||
onColumnAdd={addColumn}
|
||||
/>
|
||||
|
||||
<ProductionListConfigManager
|
||||
<ProductionListSaveConfigButton
|
||||
columns={columns}
|
||||
setColumns={setColumns}
|
||||
state={state}
|
||||
setState={setState}
|
||||
refetch={refetch}
|
||||
data={data}
|
||||
bodyshop={bodyshop}
|
||||
technician={technician}
|
||||
currentUser={currentUser}
|
||||
setHasUnsavedChanges={setHasUnsavedChanges}
|
||||
hasUnsavedChanges={hasUnsavedChanges}
|
||||
tableState={state}
|
||||
onSave={() => {
|
||||
setHasUnsavedChanges(false);
|
||||
initialStateRef.current = state;
|
||||
}}
|
||||
/>
|
||||
<ProductionListTableViewSelect
|
||||
state={state}
|
||||
setState={setState}
|
||||
setColumns={setColumns}
|
||||
onProfileChange={() => {
|
||||
initialStateRef.current = state;
|
||||
setHasUnsavedChanges(false);
|
||||
}}
|
||||
refetch={refetch}
|
||||
data={data}
|
||||
/>
|
||||
<Input
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
placeholder={t("general.labels.search")}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { getOrderOperatorsByType, getWhereOperatorsByType } from "../../utils/graphQLmodifier";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { generateInternalReflections } from "./report-center-modal-utils";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import { FormDatePicker } from "../form-date-picker/form-date-picker.component.jsx";
|
||||
|
||||
export default function ReportCenterModalFiltersSortersComponent({ form, bodyshop }) {
|
||||
return (
|
||||
@@ -196,8 +196,7 @@ function FiltersSection({ filters, form, bodyshop }) {
|
||||
// We have a type of date, so we will use a date picker
|
||||
if (type === "date") {
|
||||
return (
|
||||
<DateTimePicker
|
||||
isDateOnly
|
||||
<FormDatePicker
|
||||
disabled={!operator}
|
||||
onChange={(date) => form.setFieldValue(fieldPath, date)}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import dayjs from "../../utils/day";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
export default function ScoreboardEntryEdit({ entry }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -52,7 +52,7 @@ export default function ScoreboardEntryEdit({ entry }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("scoreboard.fields.bodyhrs")}
|
||||
|
||||
@@ -5,7 +5,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_VACATION } from "../../graphql/employees.queries";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
|
||||
export default function ShopEmployeeAddVacation({ employee }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -64,7 +64,7 @@ export default function ShopEmployeeAddVacation({ employee }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.vacation.end")}
|
||||
@@ -90,7 +90,7 @@ export default function ShopEmployeeAddVacation({ employee }) {
|
||||
})
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
|
||||
@@ -21,12 +21,12 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CiecaSelect from "../../utils/Ciecaselect";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.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";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -266,10 +266,10 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("employees.fields.termination_date")} name="termination_date">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("employees.fields.user_email")}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Col, Form, Input, Row, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FormDatePicker } from "../form-date-picker/form-date-picker.component.jsx";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
||||
import dayjs from "../../utils/day";
|
||||
import { connect } from "react-redux";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component.jsx";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component.jsx";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import { FormDateTimePickerEnhanced } from "../form-date-time-picker-enhanced/form-date-time-picker-enhanced.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -245,8 +246,7 @@ export function TaskUpsertModalComponent({
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label={t("tasks.fields.due_date")} name="due_date">
|
||||
<DateTimePicker
|
||||
isDateOnly
|
||||
<FormDatePicker
|
||||
onlyFuture
|
||||
format="MM/DD/YYYY"
|
||||
presets={generatePresets(selectedJobDetails)}
|
||||
@@ -278,7 +278,12 @@ export function TaskUpsertModalComponent({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker onlyFuture presets={generatePresets(selectedJobDetails)} />
|
||||
<FormDateTimePickerEnhanced
|
||||
onlyFuture
|
||||
showTime
|
||||
minuteStep={15}
|
||||
presets={generatePresets(selectedJobDetails)}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button, Card, DatePicker, Form, Popover, Radio, Space } from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -6,12 +7,10 @@ import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import DatePIckerRanges from "../../utils/DatePickerRanges";
|
||||
import dayjs from "../../utils/day";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectTechnician,
|
||||
technician: selectTechnician
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -19,7 +18,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TechJobPrintTickets);
|
||||
|
||||
export function TechJobPrintTickets({ bodyshop, technician, event, attendacePrint }) {
|
||||
export function TechJobPrintTickets({ technician, event, attendacePrint }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -58,8 +57,7 @@ export function TechJobPrintTickets({ bodyshop, technician, event, attendacePrin
|
||||
subject:
|
||||
attendacePrint === true ? Templates.attendance_employee.subject : Templates.timetickets_employee.subject
|
||||
},
|
||||
values.sendby,
|
||||
bodyshop
|
||||
values.sendby // === "email" ? "e" : "p"
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
@@ -7,9 +7,9 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { GET_JOB_INFO_DRAW_CALCULATIONS } from "../../graphql/jobs-lines.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -71,7 +71,7 @@ export function TimeTicketListTeamPay({ bodyshop, context, actions }) {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EditFilled, SyncOutlined } from "@ant-design/icons";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Card, Checkbox, Space, Table } from "antd";
|
||||
import dayjs from "../../utils/day";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -10,10 +10,10 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectAuthLevel, selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import dayjs from "../../utils/day";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import RbacWrapper, { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -165,7 +165,7 @@ export function TimeTicketList({
|
||||
key: "memo",
|
||||
sorter: (a, b) => alphaSort(a.memo, b.memo),
|
||||
sortOrder: state.sortedInfo.columnKey === "memo" && state.sortedInfo.order,
|
||||
render: (text, record) => (record.memo?.startsWith("timetickets.labels") ? t(record.memo) : record.memo)
|
||||
render: (text, record) => (record.clockon || record.clockoff ? t(record.memo) : record.memo)
|
||||
},
|
||||
...(Enhanced_Payroll.treatment === "on"
|
||||
? [
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useLazyQuery } from "@apollo/client";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Form, Input, InputNumber, Select, Switch } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -8,10 +7,8 @@ import { createStructuredSelector } from "reselect";
|
||||
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import { selectAuthLevel, selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||
import {
|
||||
default as DateTimePicker,
|
||||
default as FormDateTimePicker
|
||||
} from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
|
||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||
@@ -19,6 +16,7 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -62,8 +60,8 @@ export function TimeTicketModalComponent({
|
||||
{item.cost_center === "timetickets.labels.shift"
|
||||
? t(item.cost_center)
|
||||
: bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber || Enhanced_Payroll.treatment === "on"
|
||||
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
|
||||
: item.cost_center}
|
||||
? t(`joblines.fields.lbr_types.${item.cost_center.toUpperCase()}`)
|
||||
: item.cost_center}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
@@ -71,7 +69,13 @@ export function TimeTicketModalComponent({
|
||||
};
|
||||
|
||||
const MemoInput = ({ value, ...props }) => {
|
||||
return <Input value={value?.startsWith("timetickets.labels") ? t(value) : value} {...props} />;
|
||||
return (
|
||||
<Input
|
||||
value={value?.startsWith("timetickets.") ? t(value) : value}
|
||||
{...props}
|
||||
disabled={value?.startsWith("timetickets.") || disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -107,7 +111,7 @@ export function TimeTicketModalComponent({
|
||||
}
|
||||
]}
|
||||
>
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="employeeid"
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function TimeTicketShiftActive({ timetickets, refetch, isTechCons
|
||||
renderItem={(ticket) => (
|
||||
<List.Item>
|
||||
<Card
|
||||
title={ticket.memo?.startsWith("timetickets.labels") ? t(ticket.memo) : ticket.memo}
|
||||
title={t(ticket.memo)}
|
||||
actions={[
|
||||
<TechClockOffButton
|
||||
jobId={ticket.jobid}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Form, Input } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
|
||||
export default function VehicleDetailFormComponent({ form, loading }) {
|
||||
const { t } = useTranslation();
|
||||
@@ -102,7 +102,7 @@ export default function VehicleDetailFormComponent({ form, loading }) {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vehicles.fields.v_prod_dt")} name="v_prod_dt">
|
||||
<DateTimePicker isDateOnly />
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("vehicles.fields.v_paint_codes", { number: 1 })} name={["v_paint_codes", "paint_cd1"]}>
|
||||
|
||||
@@ -571,7 +571,7 @@ export function Manage({ conflict, bodyshop }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{import.meta.env.PROD && <ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible} />}
|
||||
<ChatAffixContainer bodyshop={bodyshop} chatVisible={chatVisible} />
|
||||
<Layout style={{ minHeight: "100vh" }} className="layout-container">
|
||||
<UpdateAlert />
|
||||
<HeaderContainer />
|
||||
|
||||
@@ -271,8 +271,7 @@
|
||||
},
|
||||
"errors": {
|
||||
"loading": "Unable to load shop details. Please call technical support.",
|
||||
"saving": "Error encountered while saving. {{message}}",
|
||||
"creatingdefaultview": "Error creating default view."
|
||||
"saving": "Error encountered while saving. {{message}}"
|
||||
},
|
||||
"fields": {
|
||||
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
|
||||
@@ -700,10 +699,7 @@
|
||||
"workingdays": "Working Days"
|
||||
},
|
||||
"successes": {
|
||||
"save": "Shop configuration saved successfully. ",
|
||||
"unsavedchanges": "Unsaved changes will be lost. Are you sure you want to continue?",
|
||||
"areyousure": "Are you sure you want to continue?",
|
||||
"defaultviewcreated": "Default view created successfully."
|
||||
"save": "Shop configuration saved successfully. "
|
||||
},
|
||||
"validation": {
|
||||
"centermustexist": "The chosen responsibility center does not exist.",
|
||||
@@ -1165,8 +1161,7 @@
|
||||
"tryagain": "Try Again",
|
||||
"view": "View",
|
||||
"viewreleasenotes": "See What's Changed",
|
||||
"remove_alert": "Are you sure you want to dismiss the alert?",
|
||||
"saveas": "Save As"
|
||||
"remove_alert": "Are you sure you want to dismiss the alert?"
|
||||
},
|
||||
"errors": {
|
||||
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
||||
@@ -1181,7 +1176,6 @@
|
||||
"vehicle": "Vehicle"
|
||||
},
|
||||
"labels": {
|
||||
"unsavedchanges": "Unsaved changes.",
|
||||
"actions": "Actions",
|
||||
"areyousure": "Are you sure?",
|
||||
"barcode": "Barcode",
|
||||
@@ -1189,8 +1183,6 @@
|
||||
"clear": "Clear",
|
||||
"confirmpassword": "Confirm Password",
|
||||
"created_at": "Created At",
|
||||
"date": "Select Date",
|
||||
"datetime": "Select Date & Time",
|
||||
"email": "Email",
|
||||
"errors": "Errors",
|
||||
"excel": "Excel",
|
||||
@@ -2739,9 +2731,6 @@
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"constants": {
|
||||
"main_profile": "Default"
|
||||
},
|
||||
"options": {
|
||||
"small": "Small",
|
||||
"medium": "Medium",
|
||||
@@ -2791,9 +2780,7 @@
|
||||
"errors": {
|
||||
"boardupdate": "Error encountered updating Job. {{message}}",
|
||||
"removing": "Error removing from production board. {{error}}",
|
||||
"settings": "Error saving board settings: {{error}}",
|
||||
"name_exists": "A Profile with this name already exists. Please choose a different name.",
|
||||
"name_required": "Profile name is required."
|
||||
"settings": "Error saving board settings: {{error}}"
|
||||
},
|
||||
"labels": {
|
||||
"kiosk_mode": "Kiosk Mode",
|
||||
@@ -2847,8 +2834,7 @@
|
||||
"totalhours": "Total Hrs ",
|
||||
"touchtime": "T/T",
|
||||
"viewname": "View Name",
|
||||
"alerts": "Alerts",
|
||||
"addnewprofile": "Add New Profile"
|
||||
"alerts": "Alerts"
|
||||
},
|
||||
"successes": {
|
||||
"removed": "Job removed from production."
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -271,8 +271,7 @@
|
||||
},
|
||||
"errors": {
|
||||
"loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique.",
|
||||
"saving": "",
|
||||
"creatingdefaultview": ""
|
||||
"saving": ""
|
||||
},
|
||||
"fields": {
|
||||
"ReceivableCustomField": "",
|
||||
@@ -700,10 +699,7 @@
|
||||
"workingdays": ""
|
||||
},
|
||||
"successes": {
|
||||
"save": "",
|
||||
"unsavedchanges": "",
|
||||
"areyousure": "",
|
||||
"defaultviewcreated": ""
|
||||
"save": ""
|
||||
},
|
||||
"validation": {
|
||||
"centermustexist": "",
|
||||
@@ -1165,8 +1161,7 @@
|
||||
"tryagain": "",
|
||||
"view": "",
|
||||
"viewreleasenotes": "",
|
||||
"remove_alert": "",
|
||||
"saveas": ""
|
||||
"remove_alert": ""
|
||||
},
|
||||
"errors": {
|
||||
"fcm": "",
|
||||
@@ -1181,7 +1176,6 @@
|
||||
"vehicle": ""
|
||||
},
|
||||
"labels": {
|
||||
"unsavedchanges": "",
|
||||
"actions": "actes",
|
||||
"areyousure": "",
|
||||
"barcode": "code à barre",
|
||||
@@ -1189,8 +1183,6 @@
|
||||
"clear": "",
|
||||
"confirmpassword": "",
|
||||
"created_at": "",
|
||||
"date": "",
|
||||
"datetime": "",
|
||||
"email": "",
|
||||
"errors": "",
|
||||
"excel": "",
|
||||
@@ -2739,9 +2731,6 @@
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"constants": {
|
||||
"main_profile": ""
|
||||
},
|
||||
"options": {
|
||||
"small": "",
|
||||
"medium": "",
|
||||
@@ -2791,9 +2780,7 @@
|
||||
"errors": {
|
||||
"boardupdate": "",
|
||||
"removing": "",
|
||||
"settings": "",
|
||||
"name_exists": "",
|
||||
"name_required": ""
|
||||
"settings": ""
|
||||
},
|
||||
"labels": {
|
||||
"kiosk_mode": "",
|
||||
@@ -2847,8 +2834,7 @@
|
||||
"totalhours": "",
|
||||
"touchtime": "",
|
||||
"viewname": "",
|
||||
"alerts": "",
|
||||
"addnewprofile": ""
|
||||
"alerts": ""
|
||||
},
|
||||
"successes": {
|
||||
"removed": ""
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
- name: Kaizen Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/kaizen'
|
||||
schedule: 30 5 * * *
|
||||
include_in_metadata: true
|
||||
payload: {}
|
||||
headers:
|
||||
- name: x-imex-auth
|
||||
value_from_env: DATAPUMP_AUTH
|
||||
- name: Task Reminders
|
||||
webhook: '{{HASURA_API_URL}}/tasks-remind-handler'
|
||||
schedule: '*/15 * * * *'
|
||||
|
||||
128
package-lock.json
generated
128
package-lock.json
generated
@@ -9,9 +9,9 @@
|
||||
"version": "0.2.0",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.629.0",
|
||||
"@aws-sdk/client-ses": "^3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.629.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.632.0",
|
||||
"@aws-sdk/client-ses": "^3.632.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.632.0",
|
||||
"@opensearch-project/opensearch": "^2.11.0",
|
||||
"aws4": "^1.13.1",
|
||||
"axios": "^1.7.4",
|
||||
@@ -42,7 +42,7 @@
|
||||
"node-mailjet": "^6.0.5",
|
||||
"node-persist": "^4.0.3",
|
||||
"nodemailer": "^6.9.14",
|
||||
"phone": "^3.1.49",
|
||||
"phone": "^3.1.50",
|
||||
"recursive-diff": "^1.0.9",
|
||||
"rimraf": "^6.0.1",
|
||||
"soap": "^1.1.1",
|
||||
@@ -180,24 +180,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-secrets-manager": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.629.0.tgz",
|
||||
"integrity": "sha512-ZZKI9uTQ3WIdbCZK6sveccalLTWgyOZeebi+Gnwl5ztKMk4OfwZKxyhry1DXB8gMrlISniREDb3ZxUZdFNwqfQ==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.632.0.tgz",
|
||||
"integrity": "sha512-WsQhPHHK1yPfALcP1B7nBSGDzky6vFTUEXnUdfzb5Xy2cT+JTBTS6ChtQGqqOuGHDP/3t/9soqZ+L6rUCYBb/Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.629.0",
|
||||
"@aws-sdk/client-sts": "3.629.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||
"@aws-sdk/client-sts": "3.632.0",
|
||||
"@aws-sdk/core": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||
"@aws-sdk/middleware-logger": "3.609.0",
|
||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.632.0",
|
||||
"@aws-sdk/region-config-resolver": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||
"@smithy/config-resolver": "^3.0.5",
|
||||
@@ -245,24 +245,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-ses": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.629.0.tgz",
|
||||
"integrity": "sha512-KreCdUAO/gIzWCgnPV1/dGUvLDDTdXI3fZzjjHUWFa1bE4wENjenNnWGw0qZgc8xB8pgiMdgPn7N+JvxJ7c/ZQ==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.632.0.tgz",
|
||||
"integrity": "sha512-hi01MPJF55LEK7NB1LZrqUV7b5GyjH08EToYuekFvQf9aNoR5mqWuMEDQ/dFAowYhUa2KqCdn67HnPn0ySQxHg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.629.0",
|
||||
"@aws-sdk/client-sts": "3.629.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||
"@aws-sdk/client-sts": "3.632.0",
|
||||
"@aws-sdk/core": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||
"@aws-sdk/middleware-logger": "3.609.0",
|
||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.632.0",
|
||||
"@aws-sdk/region-config-resolver": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||
"@smithy/config-resolver": "^3.0.5",
|
||||
@@ -298,9 +298,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-sso": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.629.0.tgz",
|
||||
"integrity": "sha512-2w8xU4O0Grca5HmT2dXZ5fF0g39RxODtmoqHJDsK5DSt750LqDG4w3ktmBvQs3+SrpkkJOjlX5v/hb2PCxVbww==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.632.0.tgz",
|
||||
"integrity": "sha512-iYWHiKBz44m3chCFvtvHnvCpL2rALzyr1e6tOZV3dLlOKtQtDUlPy6OtnXDu4y+wyJCniy8ivG3+LAe4klzn1Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
@@ -309,10 +309,10 @@
|
||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||
"@aws-sdk/middleware-logger": "3.609.0",
|
||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.632.0",
|
||||
"@aws-sdk/region-config-resolver": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||
"@smithy/config-resolver": "^3.0.5",
|
||||
@@ -347,22 +347,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-sso-oidc": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.629.0.tgz",
|
||||
"integrity": "sha512-3if0LauNJPqubGYf8vnlkp+B3yAeKRuRNxfNbHlE6l510xWGcKK/ZsEmiFmfePzKKSRrDh/cxMFMScgOrXptNg==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.632.0.tgz",
|
||||
"integrity": "sha512-Oh1fIWaoZluihOCb/zDEpRTi+6an82fgJz7fyRBugyLhEtDjmvpCQ3oKjzaOhoN+4EvXAm1ZS/ZgpvXBlIRTgw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/core": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||
"@aws-sdk/middleware-logger": "3.609.0",
|
||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.632.0",
|
||||
"@aws-sdk/region-config-resolver": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||
"@smithy/config-resolver": "^3.0.5",
|
||||
@@ -396,27 +396,27 @@
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-sts": "^3.629.0"
|
||||
"@aws-sdk/client-sts": "^3.632.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/client-sts": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.629.0.tgz",
|
||||
"integrity": "sha512-RjOs371YwnSVGxhPjuluJKaxl4gcPYTAky0nPjwBime0i9/iS9nI8R8l5j7k7ec9tpFWjBPvNnThCU07pvjdzw==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.632.0.tgz",
|
||||
"integrity": "sha512-Ss5cBH09icpTvT+jtGGuQlRdwtO7RyE9BF4ZV/CEPATdd9whtJt4Qxdya8BUnkWR7h5HHTrQHqai3YVYjku41A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-crypto/sha256-browser": "5.2.0",
|
||||
"@aws-crypto/sha256-js": "5.2.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.629.0",
|
||||
"@aws-sdk/client-sso-oidc": "3.632.0",
|
||||
"@aws-sdk/core": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "3.632.0",
|
||||
"@aws-sdk/middleware-host-header": "3.620.0",
|
||||
"@aws-sdk/middleware-logger": "3.609.0",
|
||||
"@aws-sdk/middleware-recursion-detection": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.620.0",
|
||||
"@aws-sdk/middleware-user-agent": "3.632.0",
|
||||
"@aws-sdk/region-config-resolver": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@aws-sdk/util-user-agent-browser": "3.609.0",
|
||||
"@aws-sdk/util-user-agent-node": "3.614.0",
|
||||
"@smithy/config-resolver": "^3.0.5",
|
||||
@@ -507,15 +507,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/credential-provider-ini": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.629.0.tgz",
|
||||
"integrity": "sha512-r9fI7BABARvVDp77DBUImQzYdvarAIdhbvpCEZib0rlpvfWu3zxE9KZcapCAAi0MPjxeDfb7RMehFQIkAP7mYw==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.632.0.tgz",
|
||||
"integrity": "sha512-m6epoW41xa1ajU5OiHcmQHoGVtrbXBaRBOUhlCLZmcaqMLYsboM4iD/WZP8aatKEON5tTnVXh/4StV8D/+wemw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-provider-env": "3.620.1",
|
||||
"@aws-sdk/credential-provider-http": "3.622.0",
|
||||
"@aws-sdk/credential-provider-process": "3.620.1",
|
||||
"@aws-sdk/credential-provider-sso": "3.629.0",
|
||||
"@aws-sdk/credential-provider-sso": "3.632.0",
|
||||
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@smithy/credential-provider-imds": "^3.2.0",
|
||||
@@ -528,20 +528,20 @@
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-sts": "^3.629.0"
|
||||
"@aws-sdk/client-sts": "^3.632.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/credential-provider-node": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.629.0.tgz",
|
||||
"integrity": "sha512-868hnVOLlXOBHk91Rl0jZIRgr/M4WJCa0nOrW9A9yidsQxuZp9P0vshDmm4hMvNZadmPIfo0Rra2MpA4RELoCw==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.632.0.tgz",
|
||||
"integrity": "sha512-cL8fuJWm/xQBO4XJPkeuZzl3XinIn9EExWgzpG48NRMKR5us1RI/ucv7xFbBBaG+r/sDR2HpYBIA3lVIpm1H3Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/credential-provider-env": "3.620.1",
|
||||
"@aws-sdk/credential-provider-http": "3.622.0",
|
||||
"@aws-sdk/credential-provider-ini": "3.629.0",
|
||||
"@aws-sdk/credential-provider-ini": "3.632.0",
|
||||
"@aws-sdk/credential-provider-process": "3.620.1",
|
||||
"@aws-sdk/credential-provider-sso": "3.629.0",
|
||||
"@aws-sdk/credential-provider-sso": "3.632.0",
|
||||
"@aws-sdk/credential-provider-web-identity": "3.621.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@smithy/credential-provider-imds": "^3.2.0",
|
||||
@@ -571,12 +571,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/credential-provider-sso": {
|
||||
"version": "3.629.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.629.0.tgz",
|
||||
"integrity": "sha512-Lf4XOuj6jamxgGZGrVojERh5S+NS2t2S4CUOnAu6tJ5U0GPlpjhINUKlcVxJBpsIXudMGW1nkumAd3+kazCPig==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.632.0.tgz",
|
||||
"integrity": "sha512-P/4wB6j7ym5QCPTL2xlMfvf2NcXSh+z0jmsZP4WW/tVwab4hvgabPPbLeEZDSWZ0BpgtxKGvRq0GSHuGeirQbA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sso": "3.629.0",
|
||||
"@aws-sdk/client-sso": "3.632.0",
|
||||
"@aws-sdk/token-providers": "3.614.0",
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@smithy/property-provider": "^3.1.3",
|
||||
@@ -650,13 +650,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/middleware-user-agent": {
|
||||
"version": "3.620.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz",
|
||||
"integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.632.0.tgz",
|
||||
"integrity": "sha512-yY/sFsHKwG9yzSf/DTclqWJaGPI2gPBJDCGBujSqTG1zlS7Ot4fqi91DZ6088BFWzbOorDzJFcAhAEFzc6LuQg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@aws-sdk/util-endpoints": "3.614.0",
|
||||
"@aws-sdk/util-endpoints": "3.632.0",
|
||||
"@smithy/protocol-http": "^4.1.0",
|
||||
"@smithy/types": "^3.3.0",
|
||||
"tslib": "^2.6.2"
|
||||
@@ -713,9 +713,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-sdk/util-endpoints": {
|
||||
"version": "3.614.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz",
|
||||
"integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==",
|
||||
"version": "3.632.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.632.0.tgz",
|
||||
"integrity": "sha512-LlYMU8pAbcEQphOpE6xaNLJ8kPGhklZZTVzZVpVW477NaaGgoGTMYNXTABYHcxeF5E2lLrxql9OmVpvr8GWN8Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-sdk/types": "3.609.0",
|
||||
"@smithy/types": "^3.3.0",
|
||||
@@ -5797,9 +5798,10 @@
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/phone": {
|
||||
"version": "3.1.49",
|
||||
"resolved": "https://registry.npmjs.org/phone/-/phone-3.1.49.tgz",
|
||||
"integrity": "sha512-S+rHWXSQrllK5eQwz0sDbwfxQ2PzennWPgsP/jdpEPH3k7P5IBJZYjvYfU8e/RF5AwKCgOtzbTGTGJcBSLJVVw==",
|
||||
"version": "3.1.50",
|
||||
"resolved": "https://registry.npmjs.org/phone/-/phone-3.1.50.tgz",
|
||||
"integrity": "sha512-TRmb2bX3sX+rrOrc8FRd8hmy4exoH2Lu3vjBP/dLgwwci1lv7DbjJ2iHMe7X4Hm8Pa0rJcfqTbq/O1vjU4NgxQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
"makeitpretty": "prettier --write \"**/*.{css,js,json,jsx,scss}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.629.0",
|
||||
"@aws-sdk/client-ses": "^3.629.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.629.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.632.0",
|
||||
"@aws-sdk/client-ses": "^3.632.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.632.0",
|
||||
"@opensearch-project/opensearch": "^2.11.0",
|
||||
"aws4": "^1.13.1",
|
||||
"axios": "^1.7.4",
|
||||
@@ -52,7 +52,7 @@
|
||||
"node-mailjet": "^6.0.5",
|
||||
"node-persist": "^4.0.3",
|
||||
"nodemailer": "^6.9.14",
|
||||
"phone": "^3.1.49",
|
||||
"phone": "^3.1.50",
|
||||
"recursive-diff": "^1.0.9",
|
||||
"rimraf": "^6.0.1",
|
||||
"soap": "^1.1.1",
|
||||
|
||||
@@ -56,8 +56,8 @@ exports.default = async (req, res) => {
|
||||
try {
|
||||
const { jobs, bodyshops_by_pk } = await client.request(queries.KAIZEN_QUERY, {
|
||||
bodyshopid: bodyshop.id,
|
||||
start: start ? moment(start).startOf("day") : moment().subtract(5, "days").startOf("day"),
|
||||
...(end && { end: moment(end).endOf("day") })
|
||||
start: start ? moment(start).startOf("hours") : moment().subtract(2, "hours").startOf("hour"),
|
||||
...(end && { end: moment(end).endOf("hours") })
|
||||
});
|
||||
|
||||
const kaizenObject = {
|
||||
@@ -176,19 +176,24 @@ exports.default = async (req, res) => {
|
||||
} finally {
|
||||
sftp.end();
|
||||
}
|
||||
sendServerEmail({
|
||||
subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
||||
text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
||||
Uploaded: ${JSON.stringify(
|
||||
allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
||||
null,
|
||||
2
|
||||
)}
|
||||
`
|
||||
});
|
||||
// sendServerEmail({
|
||||
// subject: `Kaizen Report ${moment().format("MM-DD-YY")}`,
|
||||
// text: `Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}
|
||||
// Uploaded: ${JSON.stringify(
|
||||
// allxmlsToUpload.map((x) => ({ filename: x.filename, count: x.count })),
|
||||
// null,
|
||||
// 2
|
||||
// )}
|
||||
// `,
|
||||
// });
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
res.status(200).json(error);
|
||||
sendServerEmail({
|
||||
subject: `Kaizen Report ${moment().format("MM-DD-YY @ HH:mm:ss")}`,
|
||||
text: `Errors: JSON.stringify(error)}
|
||||
All Errors: ${allErrors.map((e) => JSON.stringify(e, null, 2))}`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -965,17 +965,22 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
}
|
||||
});
|
||||
|
||||
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
|
||||
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
|
||||
if (IsTrueOrYes(pfp["PAN"][`prt_tx_in${tyCounter}`])) {
|
||||
//This amount is taxable for this type.
|
||||
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
|
||||
Dinero({
|
||||
amount: Math.round(job.adjustment_bottom_line * 100)
|
||||
})
|
||||
);
|
||||
if (job.adjustment_bottom_line) {
|
||||
const subtotal_before_adjustment = subtotal.add(Dinero({ amount: Math.round(job.adjustment_bottom_line * -100) }));
|
||||
const percent_of_adjustment =
|
||||
Math.round(
|
||||
subtotal_before_adjustment.toUnit() /
|
||||
(job.adjustment_bottom_line > 0 ? job.adjustment_bottom_line : job.adjustment_bottom_line * -1)
|
||||
) / 100;
|
||||
|
||||
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
|
||||
taxable_adjustment = taxableAmountsByTier[taxTierKey].multiply(percent_of_adjustment);
|
||||
if (job.adjustment_bottom_line > 0) {
|
||||
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].add(taxable_adjustment);
|
||||
} else {
|
||||
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].subtract(taxable_adjustment);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const remainingTaxableAmounts = taxableAmountsByTier;
|
||||
|
||||
Reference in New Issue
Block a user