Compare commits
271 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98bff6d8f6 | ||
|
|
7959dc67ce | ||
|
|
a4116b6c28 | ||
|
|
0d2cdec75c | ||
|
|
269ef25ece | ||
|
|
575fbd5357 | ||
|
|
2ad887fb82 | ||
|
|
40a1a86f72 | ||
|
|
95d43d936c | ||
|
|
9f56568680 | ||
|
|
3428940c72 | ||
|
|
ea604a5e64 | ||
|
|
5b76473cbc | ||
|
|
db5359e086 | ||
|
|
35046f11c2 | ||
|
|
69d8d27ad3 | ||
|
|
f4c4005a2a | ||
|
|
755acd24f0 | ||
|
|
079d6cfee6 | ||
|
|
3e3b3c269a | ||
|
|
39f1af7d4b | ||
|
|
1ea4d616d7 | ||
|
|
fdf0ecf6f6 | ||
|
|
e5f7285253 | ||
|
|
e46c304f7c | ||
|
|
1aa570db90 | ||
|
|
1c2be3c890 | ||
|
|
05e4eacf34 | ||
|
|
e1417b03f9 | ||
|
|
7b99de8046 | ||
|
|
6dc03d7f67 | ||
|
|
2a5e6a51aa | ||
|
|
885477fa68 | ||
|
|
c7db792793 | ||
|
|
31be51ef79 | ||
|
|
e4066c1570 | ||
|
|
ec480f2cb1 | ||
|
|
f5b30c9376 | ||
|
|
412508fbcf | ||
|
|
dbfd9bce54 | ||
|
|
52e0558c79 | ||
|
|
562d0b8641 | ||
|
|
d48d6cdd91 | ||
|
|
9363790541 | ||
|
|
2bceba948d | ||
|
|
168d4246af | ||
|
|
cdf6050ec2 | ||
|
|
f451155689 | ||
|
|
9f06e19346 | ||
|
|
f4be3e9668 | ||
|
|
ee613db0cb | ||
|
|
55b892a74e | ||
|
|
16754d9657 | ||
|
|
33db67122c | ||
|
|
b9b88b0e23 | ||
|
|
992ad71910 | ||
|
|
51d1f926c2 | ||
|
|
c70447f337 | ||
|
|
dc367e1a30 | ||
|
|
ee7997ffbc | ||
|
|
2dbb5adbbb | ||
|
|
cc9f342575 | ||
|
|
252262f4a7 | ||
|
|
f77a16648f | ||
|
|
d6e3c54b68 | ||
|
|
7294e96a77 | ||
|
|
a4f4f45251 | ||
|
|
acc91abc0c | ||
|
|
18d5176cb9 | ||
|
|
d6d6ced7a4 | ||
|
|
52809cc849 | ||
|
|
c525b7ea3f | ||
|
|
e799417aaf | ||
|
|
ef077c2d48 | ||
|
|
e78b114544 | ||
|
|
df170ddd27 | ||
|
|
17928741e3 | ||
|
|
a6b825ffdf | ||
|
|
0d3bc0e95f | ||
|
|
0f62a2d73d | ||
|
|
bcdd32f92f | ||
|
|
6865fcbffc | ||
|
|
8e623c71a9 | ||
|
|
1e06502464 | ||
|
|
f2914868e2 | ||
|
|
561f0313f9 | ||
|
|
1e27c4e1ae | ||
|
|
46676ba8eb | ||
|
|
02a49efbea | ||
|
|
5f2a5e1025 | ||
|
|
42552e4f4f | ||
|
|
f2af78f056 | ||
|
|
269d55bde3 | ||
|
|
71f3dbbeb4 | ||
|
|
73dfb74ed7 | ||
|
|
7dd6baef33 | ||
|
|
519b532091 | ||
|
|
4fb9c37c0d | ||
|
|
c9b63be29f | ||
|
|
84bc735dce | ||
|
|
a44b9417a1 | ||
|
|
6b157ed43c | ||
|
|
9050276ea7 | ||
|
|
683293d042 | ||
|
|
70d009ab49 | ||
|
|
dbc2d10d6d | ||
|
|
059b854db9 | ||
|
|
13569a1785 | ||
|
|
6fd70b165b | ||
|
|
11d94cf286 | ||
|
|
c98a48ea14 | ||
|
|
b8fa80419b | ||
|
|
2a8846297f | ||
|
|
fb322f760f | ||
|
|
0b9f718106 | ||
|
|
57b3b3a9cd | ||
|
|
6513432993 | ||
|
|
80a564d4b6 | ||
|
|
132ecffc40 | ||
|
|
cdf02a8eac | ||
|
|
7d22dcab7c | ||
|
|
45c943a78f | ||
|
|
cf82a8013f | ||
|
|
25d145d864 | ||
|
|
7822e3f90e | ||
|
|
a4a612fbe4 | ||
|
|
07da472e82 | ||
|
|
e9096632a4 | ||
|
|
6475a8bdce | ||
|
|
535f92b7d2 | ||
|
|
00b91616f5 | ||
|
|
b8c34762ed | ||
|
|
9f92347936 | ||
|
|
b657a893ad | ||
|
|
387670212a | ||
|
|
b8e42544ae | ||
|
|
bcea47c2c6 | ||
|
|
b38aaba56c | ||
|
|
8099607d90 | ||
|
|
2d9412e4e8 | ||
|
|
069d508528 | ||
|
|
d68ce67e4f | ||
|
|
a9d38e743f | ||
|
|
878e81dc8f | ||
|
|
5ccf74f99c | ||
|
|
6c823f4914 | ||
|
|
de35155ffe | ||
|
|
470eb19a2f | ||
|
|
947bc33946 | ||
|
|
8bcaabfb57 | ||
|
|
a1f7e7b755 | ||
|
|
c8f8a86a98 | ||
|
|
bb205af019 | ||
|
|
443c6046f9 | ||
|
|
1620b94a7b | ||
|
|
81f94eac6c | ||
|
|
ffada75d9e | ||
|
|
34d773bcd8 | ||
|
|
ec7509670d | ||
|
|
eceac11af2 | ||
|
|
dd64598850 | ||
|
|
650ace6be6 | ||
|
|
1c71a5c5e0 | ||
|
|
9c699a634b | ||
|
|
a47d17bbf5 | ||
|
|
164f67d6ce | ||
|
|
465b9e7177 | ||
|
|
f5a914c318 | ||
|
|
de486d2e73 | ||
|
|
d7f946ec2a | ||
|
|
4d1480bb61 | ||
|
|
25a49473f9 | ||
|
|
8e86e7fba5 | ||
|
|
f9b380a0d4 | ||
|
|
f664a56b16 | ||
|
|
0acfd3c4b1 | ||
|
|
bfc4cb1ad9 | ||
|
|
66dd1a9a2b | ||
|
|
1f42be2e54 | ||
|
|
30d344af6b | ||
|
|
3ca989fd8c | ||
|
|
73c38b3ae4 | ||
|
|
08749b0c92 | ||
|
|
ea73af371e | ||
|
|
ce2086a480 | ||
|
|
63f7106d2b | ||
|
|
8cce6ea6e3 | ||
|
|
77c486b4c9 | ||
|
|
7c8f276bb0 | ||
|
|
e991586254 | ||
|
|
d61ab796a7 | ||
|
|
e634369975 | ||
|
|
453236cf3a | ||
|
|
3530476b07 | ||
|
|
e75d8d1874 | ||
|
|
719fa6a67d | ||
|
|
148cd43c5d | ||
|
|
3d753a2d19 | ||
|
|
693d02de87 | ||
|
|
80b4ef3ae8 | ||
|
|
6b9269eb2d | ||
|
|
15c9529885 | ||
|
|
512cd70d13 | ||
|
|
e5599ff4c4 | ||
|
|
32041ee3fc | ||
|
|
07bf84ed69 | ||
|
|
0c0449aa17 | ||
|
|
26cb527d37 | ||
|
|
33c282051b | ||
|
|
df0f8ef9dc | ||
|
|
e23f13a1b3 | ||
|
|
f5b8bf1d74 | ||
|
|
61766017ea | ||
|
|
706984a53b | ||
|
|
e137feca20 | ||
|
|
9e66d7c929 | ||
|
|
9605bf5c21 | ||
|
|
c2cc7b1e9e | ||
|
|
fe55eccbf9 | ||
|
|
47e17dc78a | ||
|
|
88a71dd647 | ||
|
|
ce0b3a8635 | ||
|
|
bd3d86a6dd | ||
|
|
c3b395c99e | ||
|
|
eb4e5d9576 | ||
|
|
19a03ec080 | ||
|
|
33d5d9b462 | ||
|
|
b5a371d0cf | ||
|
|
b61fd17879 | ||
|
|
6722f8b1e5 | ||
|
|
69ff75157d | ||
|
|
7fad968ad2 | ||
|
|
7c619f5439 | ||
|
|
3b35f38ad5 | ||
|
|
096017c3d6 | ||
|
|
4ff2ab1bc8 | ||
|
|
ef698529d7 | ||
|
|
167d5bd89a | ||
|
|
c328a55453 | ||
|
|
83a976e98f | ||
|
|
d004133ad6 | ||
|
|
1f5c1b9658 | ||
|
|
56dfd174dd | ||
|
|
532cd4937b | ||
|
|
91bc73baf2 | ||
|
|
ab031c01de | ||
|
|
e51f72ff98 | ||
|
|
3eb010285d | ||
|
|
2b172f9999 | ||
|
|
0803f5af35 | ||
|
|
69ac2f0a6c | ||
|
|
0c842e0e15 | ||
|
|
d94678d4f4 | ||
|
|
90814f41a2 | ||
|
|
282dbd0913 | ||
|
|
1343b68cc6 | ||
|
|
ae07f71e76 | ||
|
|
54dc9c8587 | ||
|
|
9f9fa3b952 | ||
|
|
cc7c98336f | ||
|
|
dc22b96bed | ||
|
|
ae9e9f4b72 | ||
|
|
301c680bff | ||
|
|
595159f24d | ||
|
|
3bf1ec25c1 | ||
|
|
40c801592d | ||
|
|
9012e4deec | ||
|
|
ab2323e5c1 | ||
|
|
f31ae9ac6d | ||
|
|
27c24619c3 | ||
|
|
38aef71269 |
@@ -9,10 +9,10 @@ const config = {
|
|||||||
arrowParens: "always",
|
arrowParens: "always",
|
||||||
jsxSingleQuote: false,
|
jsxSingleQuote: false,
|
||||||
bracketSameLine: false,
|
bracketSameLine: false,
|
||||||
endOfLine: "lf",
|
endOfLine: "lf"
|
||||||
importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
|
// importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"],
|
||||||
importOrderSeparation: true,
|
// importOrderSeparation: true,
|
||||||
importOrderSortSpecifiers: true
|
// importOrderSortSpecifiers: true
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
module.exports = config;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
|||||||
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
||||||
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
||||||
VITE_APP_GA_CODE=231099835
|
VITE_APP_GA_CODE=231099835
|
||||||
VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||||
|
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
|
||||||
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
||||||
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
||||||
VITE_APP_CLOUDINARY_API_KEY=957865933348715
|
VITE_APP_CLOUDINARY_API_KEY=957865933348715
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
||||||
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
||||||
VITE_APP_GA_CODE=231099835
|
VITE_APP_GA_CODE=231099835
|
||||||
VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||||
|
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
|
||||||
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
|
||||||
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
|
||||||
VITE_APP_CLOUDINARY_API_KEY=957865933348715
|
VITE_APP_CLOUDINARY_API_KEY=957865933348715
|
||||||
|
|||||||
8
client/.eslintrc
Normal file
8
client/.eslintrc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"react-app"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"no-useless-rename": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
client/package-lock.json
generated
39
client/package-lock.json
generated
@@ -96,6 +96,8 @@
|
|||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^13.6.6",
|
"cypress": "^13.6.6",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-cypress": "^2.15.1",
|
"eslint-plugin-cypress": "^2.15.1",
|
||||||
"memfs": "^4.6.0",
|
"memfs": "^4.6.0",
|
||||||
"os-browserify": "^0.3.0",
|
"os-browserify": "^0.3.0",
|
||||||
@@ -104,6 +106,7 @@
|
|||||||
"source-map-explorer": "^2.5.3",
|
"source-map-explorer": "^2.5.3",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.0.11",
|
||||||
"vite-plugin-babel": "^1.2.0",
|
"vite-plugin-babel": "^1.2.0",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-legacy": "^2.1.0",
|
"vite-plugin-legacy": "^2.1.0",
|
||||||
"vite-plugin-node-polyfills": "^0.19.0",
|
"vite-plugin-node-polyfills": "^0.19.0",
|
||||||
"vite-plugin-pwa": "^0.19.0",
|
"vite-plugin-pwa": "^0.19.0",
|
||||||
@@ -11934,7 +11937,8 @@
|
|||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.57.0",
|
"version": "8.57.0",
|
||||||
"license": "MIT",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
||||||
|
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.6.1",
|
"@eslint-community/regexpp": "^4.6.1",
|
||||||
@@ -11987,7 +11991,8 @@
|
|||||||
},
|
},
|
||||||
"node_modules/eslint-config-react-app": {
|
"node_modules/eslint-config-react-app": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"license": "MIT",
|
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@babel/core": "^7.16.0",
|
||||||
"@babel/eslint-parser": "^7.16.3",
|
"@babel/eslint-parser": "^7.16.3",
|
||||||
@@ -26439,6 +26444,36 @@
|
|||||||
"vite": ">=5.0.0"
|
"vite": ">=5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-eslint": {
|
||||||
|
"version": "1.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-eslint/-/vite-plugin-eslint-1.8.1.tgz",
|
||||||
|
"integrity": "sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@rollup/pluginutils": "^4.2.1",
|
||||||
|
"@types/eslint": "^8.4.5",
|
||||||
|
"rollup": "^2.77.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7",
|
||||||
|
"vite": ">=2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-plugin-eslint/node_modules/rollup": {
|
||||||
|
"version": "2.79.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
||||||
|
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite-plugin-legacy": {
|
"node_modules/vite-plugin-legacy": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
@@ -140,6 +140,8 @@
|
|||||||
"browserslist-to-esbuild": "^2.1.1",
|
"browserslist-to-esbuild": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cypress": "^13.6.6",
|
"cypress": "^13.6.6",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-cypress": "^2.15.1",
|
"eslint-plugin-cypress": "^2.15.1",
|
||||||
"memfs": "^4.6.0",
|
"memfs": "^4.6.0",
|
||||||
"os-browserify": "^0.3.0",
|
"os-browserify": "^0.3.0",
|
||||||
@@ -148,6 +150,7 @@
|
|||||||
"source-map-explorer": "^2.5.3",
|
"source-map-explorer": "^2.5.3",
|
||||||
"vite": "^5.0.11",
|
"vite": "^5.0.11",
|
||||||
"vite-plugin-babel": "^1.2.0",
|
"vite-plugin-babel": "^1.2.0",
|
||||||
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-legacy": "^2.1.0",
|
"vite-plugin-legacy": "^2.1.0",
|
||||||
"vite-plugin-node-polyfills": "^0.19.0",
|
"vite-plugin-node-polyfills": "^0.19.0",
|
||||||
"vite-plugin-pwa": "^0.19.0",
|
"vite-plugin-pwa": "^0.19.0",
|
||||||
|
|||||||
56
client/public/firebase-messaging-sw.js
Normal file
56
client/public/firebase-messaging-sw.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Scripts for firebase and firebase messaging
|
||||||
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js");
|
||||||
|
importScripts("https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js");
|
||||||
|
|
||||||
|
// Initialize the Firebase app in the service worker by passing the generated config
|
||||||
|
let firebaseConfig;
|
||||||
|
switch (this.location.hostname) {
|
||||||
|
case "localhost":
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc",
|
||||||
|
authDomain: "imex-dev.firebaseapp.com",
|
||||||
|
databaseURL: "https://imex-dev.firebaseio.com",
|
||||||
|
projectId: "imex-dev",
|
||||||
|
storageBucket: "imex-dev.appspot.com",
|
||||||
|
messagingSenderId: "759548147434",
|
||||||
|
appId: "1:759548147434:web:e8239868a48ceb36700993",
|
||||||
|
measurementId: "G-K5XRBVVB4S",
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "test.imex.online":
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c",
|
||||||
|
authDomain: "imex-test.firebaseapp.com",
|
||||||
|
projectId: "imex-test",
|
||||||
|
storageBucket: "imex-test.appspot.com",
|
||||||
|
messagingSenderId: "991923618608",
|
||||||
|
appId: "1:991923618608:web:633437569cdad78299bef5",
|
||||||
|
// measurementId: "${config.measurementId}",
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "imex.online":
|
||||||
|
default:
|
||||||
|
firebaseConfig = {
|
||||||
|
apiKey: "AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU",
|
||||||
|
authDomain: "imex-prod.firebaseapp.com",
|
||||||
|
databaseURL: "https://imex-prod.firebaseio.com",
|
||||||
|
projectId: "imex-prod",
|
||||||
|
storageBucket: "imex-prod.appspot.com",
|
||||||
|
messagingSenderId: "253497221485",
|
||||||
|
appId: "1:253497221485:web:3c81c483b94db84b227a64",
|
||||||
|
measurementId: "G-NTWBKG2L0M",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
firebase.initializeApp(firebaseConfig);
|
||||||
|
|
||||||
|
// Retrieve firebase messaging
|
||||||
|
const messaging = firebase.messaging();
|
||||||
|
|
||||||
|
messaging.onBackgroundMessage(function (payload) {
|
||||||
|
// Customize notification here
|
||||||
|
const channel = new BroadcastChannel("imex-sw-messages");
|
||||||
|
channel.postMessage(payload);
|
||||||
|
|
||||||
|
//self.registration.showNotification(notificationTitle, notificationOptions);
|
||||||
|
});
|
||||||
@@ -150,7 +150,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
|
|||||||
<ProductFruits
|
<ProductFruits
|
||||||
workspaceCode={InstanceRenderMgr({
|
workspaceCode={InstanceRenderMgr({
|
||||||
imex: null,
|
imex: null,
|
||||||
rome: null,
|
rome: "9BkbEseqNqxw8jUH",
|
||||||
promanager: "aoJoEifvezYI0Z0P"
|
promanager: "aoJoEifvezYI0Z0P"
|
||||||
})}
|
})}
|
||||||
debug
|
debug
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export function BillDetailEditReturn({ setPartsOrderContext, insertAuditTrail, b
|
|||||||
actions: {},
|
actions: {},
|
||||||
context: {
|
context: {
|
||||||
jobId: data.bills_by_pk.jobid,
|
jobId: data.bills_by_pk.jobid,
|
||||||
|
job: data.bills_by_pk.job,
|
||||||
vendorId: data.bills_by_pk.vendorid,
|
vendorId: data.bills_by_pk.vendorid,
|
||||||
returnFromBill: data.bills_by_pk.id,
|
returnFromBill: data.bills_by_pk.id,
|
||||||
invoiceNumber: data.bills_by_pk.invoice_number,
|
invoiceNumber: data.bills_by_pk.invoice_number,
|
||||||
|
|||||||
@@ -25,31 +25,27 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
notFoundContent={"Removed."}
|
notFoundContent={"Removed."}
|
||||||
{...restProps}
|
options={[
|
||||||
>
|
{ value: "noline", label: t("billlines.labels.other"), name: t("billlines.labels.other") },
|
||||||
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
|
...options.map((item) => ({
|
||||||
{t("billlines.labels.other")}
|
disabled: allowRemoved ? false : item.removed,
|
||||||
</Select.Option>
|
key: item.id,
|
||||||
{options
|
value: item.id,
|
||||||
? options.map((item) => (
|
cost: item.act_price ? item.act_price : 0,
|
||||||
<Option
|
part_type: item.part_type,
|
||||||
disabled={allowRemoved ? false : item.removed}
|
line_desc: item.line_desc,
|
||||||
key={item.id}
|
part_qty: item.part_qty,
|
||||||
value={item.id}
|
oem_partno: item.oem_partno,
|
||||||
cost={item.act_price ? item.act_price : 0}
|
alt_partno: item.alt_partno,
|
||||||
part_type={item.part_type}
|
act_price: item.act_price,
|
||||||
line_desc={item.line_desc}
|
style: {
|
||||||
part_qty={item.part_qty}
|
...(item.removed ? { textDecoration: "line-through" } : {})
|
||||||
oem_partno={item.oem_partno}
|
},
|
||||||
alt_partno={item.alt_partno}
|
name: `${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
||||||
act_price={item.act_price}
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
style={{
|
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
|
||||||
...(item.removed ? { textDecoration: "line-through" } : {})
|
label: (
|
||||||
}}
|
<>
|
||||||
name={`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
|
||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
|
||||||
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
|
|
||||||
>
|
|
||||||
<span>
|
<span>
|
||||||
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
|
||||||
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
item.oem_partno ? ` - ${item.oem_partno}` : ""
|
||||||
@@ -60,14 +56,15 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
|
|||||||
<span style={{ float: "right", paddingleft: "1rem" }}>{`${item.mod_lb_hrs} units`}</span>
|
<span style={{ float: "right", paddingleft: "1rem" }}>{`${item.mod_lb_hrs} units`}</span>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<span style={{ float: "right", paddingleft: "1rem" }}>
|
<span style={{ float: "right", paddingleft: "1rem" }}>
|
||||||
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
|
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
|
||||||
</span>
|
</span>
|
||||||
</Option>
|
</>
|
||||||
))
|
)
|
||||||
: null}
|
}))
|
||||||
</Select>
|
]}
|
||||||
|
{...restProps}
|
||||||
|
></Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default forwardRef(BillLineSearchSelect);
|
export default forwardRef(BillLineSearchSelect);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { TemplateList } from "../../utils/TemplateConstants";
|
|||||||
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
||||||
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
|
import { FaTasks } from "react-icons/fa";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
@@ -21,9 +22,21 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
setBillEnterContext: (context) =>
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
dispatch(
|
||||||
setReconciliationContext: (context) => dispatch(setModalContext({ context: context, modal: "reconciliation" }))
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "billEnter"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setReconciliationContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "reconciliation"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function BillsListTableComponent({
|
export function BillsListTableComponent({
|
||||||
@@ -32,9 +45,9 @@ export function BillsListTableComponent({
|
|||||||
job,
|
job,
|
||||||
billsQuery,
|
billsQuery,
|
||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setPartsOrderContext,
|
|
||||||
setBillEnterContext,
|
setBillEnterContext,
|
||||||
setReconciliationContext
|
setReconciliationContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -48,6 +61,7 @@ export function BillsListTableComponent({
|
|||||||
const Templates = TemplateList("bill");
|
const Templates = TemplateList("bill");
|
||||||
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
const bills = billsQuery.data ? billsQuery.data.bills : [];
|
||||||
const { refetch } = billsQuery;
|
const { refetch } = billsQuery;
|
||||||
|
|
||||||
const recordActions = (record, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
@@ -55,9 +69,22 @@ export function BillsListTableComponent({
|
|||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
context: {
|
||||||
|
jobid: job.id,
|
||||||
|
billid: record.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaTasks />
|
||||||
|
</Button>
|
||||||
<BillDeleteButton bill={record} jobid={job.id} />
|
<BillDeleteButton bill={record} jobid={job.id} />
|
||||||
<BillDetailEditReturnComponent
|
<BillDetailEditReturnComponent
|
||||||
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
data={{ bills_by_pk: { ...record, jobid: job.id, job: job } }}
|
||||||
disabled={record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid || jobRO}
|
disabled={record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid || jobRO}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Col, Form, Input, notification, Row, Space, Spin, Statistic } from "antd";
|
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, notification } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries";
|
import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries";
|
||||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||||
@@ -28,12 +26,12 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
||||||
const { context } = cardPaymentModal;
|
const { context, actions } = cardPaymentModal;
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -42,7 +40,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
skip: true
|
skip: true
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
|
|
||||||
//Initialize the intellipay window.
|
//Initialize the intellipay window.
|
||||||
const SetIntellipayCallbackFunctions = () => {
|
const SetIntellipayCallbackFunctions = () => {
|
||||||
console.log("*** Set IntelliPay callback functions.");
|
console.log("*** Set IntelliPay callback functions.");
|
||||||
@@ -51,16 +48,20 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.intellipay.runOnApproval(async function (response) {
|
window.intellipay.runOnApproval(async function (response) {
|
||||||
console.warn("*** Running On Approval Script ***");
|
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
||||||
form.setFieldValue("paymentResponse", response);
|
//Add a slight delay to allow the refetch to properly get the data.
|
||||||
form.submit();
|
setTimeout(() => {
|
||||||
|
if (actions && actions.refetch && typeof actions.refetch === "function")
|
||||||
|
actions.refetch();
|
||||||
|
setLoading(false);
|
||||||
|
toggleModalVisible();
|
||||||
|
}, 750);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.intellipay.runOnNonApproval(async function (response) {
|
window.intellipay.runOnNonApproval(async function (response) {
|
||||||
// Mutate unsuccessful payment
|
// Mutate unsuccessful payment
|
||||||
|
|
||||||
const { payments } = form.getFieldsValue();
|
const { payments } = form.getFieldsValue();
|
||||||
|
|
||||||
await insertPaymentResponse({
|
await insertPaymentResponse({
|
||||||
variables: {
|
variables: {
|
||||||
paymentResponse: payments.map((payment) => ({
|
paymentResponse: payments.map((payment) => ({
|
||||||
@@ -85,50 +86,9 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
|
||||||
try {
|
|
||||||
await insertPayment({
|
|
||||||
variables: {
|
|
||||||
paymentInput: values.payments.map((payment) => ({
|
|
||||||
amount: payment.amount,
|
|
||||||
transactionid: (values.paymentResponse.paymentid || "").toString(),
|
|
||||||
payer: t("payments.labels.customer"),
|
|
||||||
type: values.paymentResponse.cardbrand,
|
|
||||||
jobid: payment.jobid,
|
|
||||||
date: dayjs(Date.now()),
|
|
||||||
payment_responses: {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
amount: payment.amount,
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
|
|
||||||
jobid: payment.jobid,
|
|
||||||
declinereason: values.paymentResponse.declinereason,
|
|
||||||
ext_paymentid: values.paymentResponse.paymentid.toString(),
|
|
||||||
successful: true,
|
|
||||||
response: values.paymentResponse
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
refetchQueries: ["GET_JOB_BY_PK"]
|
|
||||||
});
|
|
||||||
toggleModalVisible();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
notification.open({
|
|
||||||
type: "error",
|
|
||||||
message: t("payments.errors.inserting", { error: error.message })
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIntelliPayCharge = async () => {
|
const handleIntelliPayCharge = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
//Validate
|
//Validate
|
||||||
try {
|
try {
|
||||||
await form.validateFields();
|
await form.validateFields();
|
||||||
@@ -140,7 +100,8 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
try {
|
try {
|
||||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||||
bodyshop,
|
bodyshop,
|
||||||
refresh: !!window.intellipay
|
refresh: !!window.intellipay,
|
||||||
|
paymentSplitMeta: form.getFieldsValue(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.intellipay) {
|
if (window.intellipay) {
|
||||||
@@ -169,7 +130,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
<Card title="Card Payment">
|
<Card title="Card Payment">
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Form
|
<Form
|
||||||
onFinish={handleFinish}
|
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
initialValues={{
|
initialValues={{
|
||||||
@@ -246,18 +206,14 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
console.log("Updating the owner info section.");
|
|
||||||
//If all of the job ids have been fileld in, then query and update the IP field.
|
//If all of the job ids have been fileld in, then query and update the IP field.
|
||||||
const { payments } = form.getFieldsValue();
|
const { payments } = form.getFieldsValue();
|
||||||
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
|
if (
|
||||||
console.log("**Calling refetch.");
|
payments?.length > 0 &&
|
||||||
|
payments?.filter((p) => p?.jobid).length === payments?.length
|
||||||
|
) {
|
||||||
refetch({ jobids: payments.map((p) => p.jobid) });
|
refetch({ jobids: payments.map((p) => p.jobid) });
|
||||||
}
|
}
|
||||||
console.log(
|
|
||||||
"Acc info",
|
|
||||||
data,
|
|
||||||
payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
@@ -300,6 +256,13 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
type="hidden"
|
type="hidden"
|
||||||
value={totalAmountToCharge?.toFixed(2)}
|
value={totalAmountToCharge?.toFixed(2)}
|
||||||
/>
|
/>
|
||||||
|
<Input
|
||||||
|
className="ipayfield"
|
||||||
|
data-ipayname="comment"
|
||||||
|
type="hidden"
|
||||||
|
value={btoa(JSON.stringify(payments))}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
// data-ipayname="submit"
|
// data-ipayname="submit"
|
||||||
@@ -314,11 +277,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* Lightbox payment response when it is completed */}
|
|
||||||
<Form.Item name="paymentResponse" hidden>
|
|
||||||
<Input type="hidden" />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
</Form>
|
||||||
</Spin>
|
</Spin>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
|
|||||||
<Option value="courtesycars.status.out">{t("courtesycars.status.out")}</Option>
|
<Option value="courtesycars.status.out">{t("courtesycars.status.out")}</Option>
|
||||||
<Option value="courtesycars.status.sold">{t("courtesycars.status.sold")}</Option>
|
<Option value="courtesycars.status.sold">{t("courtesycars.status.sold")}</Option>
|
||||||
<Option value="courtesycars.status.leasereturn">{t("courtesycars.status.leasereturn")}</Option>
|
<Option value="courtesycars.status.leasereturn">{t("courtesycars.status.leasereturn")}</Option>
|
||||||
|
<Option value="courtesycars.status.unavailable">{t("courtesycars.status.unavailable")}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
{
|
{
|
||||||
text: t("courtesycars.status.leasereturn"),
|
text: t("courtesycars.status.leasereturn"),
|
||||||
value: "courtesycars.status.leasereturn"
|
value: "courtesycars.status.leasereturn"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
text: t("courtesycars.status.unavailable"),
|
||||||
|
value: "courtesycars.status.unavailable",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
onFilter: (value, record) => record.status === value,
|
onFilter: (value, record) => record.status === value,
|
||||||
sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Select, Space, Tag } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
//To be used as a form element only.
|
||||||
|
|
||||||
|
const EmployeeSearchSelectEmail = ({ options, ...props }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
showSearch
|
||||||
|
// value={option}
|
||||||
|
style={{
|
||||||
|
width: 400
|
||||||
|
}}
|
||||||
|
optionFilterProp="search"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{options
|
||||||
|
? options.map((o) => (
|
||||||
|
<Option key={o.id} value={o.user_email} search={`${o.employee_number} ${o.first_name} ${o.last_name}`}>
|
||||||
|
<Space>
|
||||||
|
{`${o.employee_number} ${o.first_name} ${o.last_name}`}
|
||||||
|
|
||||||
|
<Tag color="green">
|
||||||
|
{o.flat_rate ? t("timetickets.labels.flat_rate") : t("timetickets.labels.straight_time")}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
</Option>
|
||||||
|
))
|
||||||
|
: null}
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default EmployeeSearchSelectEmail;
|
||||||
@@ -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,7 +1,7 @@
|
|||||||
import React, { forwardRef } from "react";
|
import React, { forwardRef } from "react";
|
||||||
//import DatePicker from "react-datepicker";
|
//import DatePicker from "react-datepicker";
|
||||||
//import "react-datepicker/src/stylesheets/datepicker.scss";
|
//import "react-datepicker/src/stylesheets/datepicker.scss";
|
||||||
import { TimePicker } from "antd";
|
import { Space, TimePicker } from "antd";
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
//To be used as a form element only.
|
//To be used as a form element only.
|
||||||
@@ -14,7 +14,7 @@ const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, ...restProps
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id={id}>
|
<Space direction="vertical" style={{ width: "100%" }} id={id}>
|
||||||
<FormDatePicker
|
<FormDatePicker
|
||||||
{...restProps}
|
{...restProps}
|
||||||
{...(onlyFuture && {
|
{...(onlyFuture && {
|
||||||
@@ -39,7 +39,7 @@ const DateTimePicker = ({ value, onChange, onBlur, id, onlyFuture, ...restProps
|
|||||||
format="hh:mm a"
|
format="hh:mm a"
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</Space>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import axios from "axios";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
|
||||||
|
|
||||||
export default function GlobalSearchOs() {
|
export default function GlobalSearchOs() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useNavigate();
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [data, setData] = useState(false);
|
const [data, setData] = useState(false);
|
||||||
|
|
||||||
@@ -178,15 +177,7 @@ export default function GlobalSearchOs() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoComplete
|
<AutoComplete options={data} onSearch={handleSearch} defaultActiveFirstOption onClear={() => setData([])}>
|
||||||
options={data}
|
|
||||||
onSearch={handleSearch}
|
|
||||||
defaultActiveFirstOption
|
|
||||||
onSelect={(val, opt) => {
|
|
||||||
history(opt.label.props.to);
|
|
||||||
}}
|
|
||||||
onClear={() => setData([])}
|
|
||||||
>
|
|
||||||
<Input.Search
|
<Input.Search
|
||||||
size="large"
|
size="large"
|
||||||
placeholder={t("general.labels.globalsearch")}
|
placeholder={t("general.labels.globalsearch")}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { AutoComplete, Divider, Input, Space } from "antd";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
||||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
@@ -12,7 +12,6 @@ import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.compon
|
|||||||
|
|
||||||
export default function GlobalSearch() {
|
export default function GlobalSearch() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const history = useNavigate();
|
|
||||||
const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY);
|
const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||||
|
|
||||||
const executeSearch = (v) => {
|
const executeSearch = (v) => {
|
||||||
@@ -157,14 +156,7 @@ export default function GlobalSearch() {
|
|||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoComplete
|
<AutoComplete options={options} onSearch={handleSearch} defaultActiveFirstOption>
|
||||||
options={options}
|
|
||||||
onSearch={handleSearch}
|
|
||||||
defaultActiveFirstOption
|
|
||||||
onSelect={(val, opt) => {
|
|
||||||
history(opt.label.props.to);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input.Search
|
<Input.Search
|
||||||
size="large"
|
size="large"
|
||||||
placeholder={t("general.labels.globalsearch")}
|
placeholder={t("general.labels.globalsearch")}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import Icon, {
|
|||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
PhoneOutlined,
|
PhoneOutlined,
|
||||||
|
PlusCircleOutlined,
|
||||||
QuestionCircleFilled,
|
QuestionCircleFilled,
|
||||||
ScheduleOutlined,
|
ScheduleOutlined,
|
||||||
SettingOutlined,
|
SettingOutlined,
|
||||||
@@ -30,7 +31,8 @@ import { Layout, Menu, Switch, Tooltip } from "antd";
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar } from "react-icons/fa";
|
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||||
|
import { FiLogOut } from "react-icons/fi";
|
||||||
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
||||||
import { IoBusinessOutline } from "react-icons/io5";
|
import { IoBusinessOutline } from "react-icons/io5";
|
||||||
import { RiSurveyLine } from "react-icons/ri";
|
import { RiSurveyLine } from "react-icons/ri";
|
||||||
@@ -41,7 +43,6 @@ import { selectRecentItems, selectSelectedHeader } from "../../redux/application
|
|||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { signOutStart } from "../../redux/user/user.actions";
|
import { signOutStart } from "../../redux/user/user.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import { FiLogOut } from "react-icons/fi";
|
|
||||||
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
|
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
@@ -54,12 +55,43 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
setBillEnterContext: (context) =>
|
||||||
setTimeTicketContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "billEnter"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "timeTicket"
|
||||||
|
})
|
||||||
|
),
|
||||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||||
setReportCenterContext: (context) => dispatch(setModalContext({ context: context, modal: "reportCenter" })),
|
setReportCenterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "reportCenter"
|
||||||
|
})
|
||||||
|
),
|
||||||
signOutStart: () => dispatch(signOutStart()),
|
signOutStart: () => dispatch(signOutStart()),
|
||||||
setCardPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "cardPayment" }))
|
setCardPaymentContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "cardPayment"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTaskUpsertContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "taskUpsert"
|
||||||
|
})
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
@@ -73,7 +105,8 @@ function Header({
|
|||||||
setPaymentContext,
|
setPaymentContext,
|
||||||
setReportCenterContext,
|
setReportCenterContext,
|
||||||
recentItems,
|
recentItems,
|
||||||
setCardPaymentContext
|
setCardPaymentContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
treatments: { ImEXPay, DmsAp, Simple_Inventory }
|
treatments: { ImEXPay, DmsAp, Simple_Inventory }
|
||||||
@@ -108,11 +141,13 @@ function Header({
|
|||||||
accountingChildren.push(
|
accountingChildren.push(
|
||||||
{
|
{
|
||||||
key: "bills",
|
key: "bills",
|
||||||
|
id: "header-accounting-bills",
|
||||||
icon: <Icon component={FaFileInvoiceDollar} />,
|
icon: <Icon component={FaFileInvoiceDollar} />,
|
||||||
label: <Link to="/manage/bills">{t("menus.header.bills")}</Link>
|
label: <Link to="/manage/bills">{t("menus.header.bills")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "enterbills",
|
key: "enterbills",
|
||||||
|
id: "header-accounting-enterbills",
|
||||||
icon: <Icon component={GiPayMoney} />,
|
icon: <Icon component={GiPayMoney} />,
|
||||||
label: t("menus.header.enterbills"),
|
label: t("menus.header.enterbills"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -132,6 +167,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "inventory",
|
key: "inventory",
|
||||||
|
id: "header-accounting-inventory",
|
||||||
icon: <Icon component={FaFileInvoiceDollar} />,
|
icon: <Icon component={FaFileInvoiceDollar} />,
|
||||||
label: <Link to="/manage/inventory">{t("menus.header.inventory")}</Link>
|
label: <Link to="/manage/inventory">{t("menus.header.inventory")}</Link>
|
||||||
}
|
}
|
||||||
@@ -150,11 +186,13 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "allpayments",
|
key: "allpayments",
|
||||||
|
id: "header-accounting-allpayments",
|
||||||
icon: <BankFilled />,
|
icon: <BankFilled />,
|
||||||
label: <Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
label: <Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "enterpayments",
|
key: "enterpayments",
|
||||||
|
id: "header-accounting-enterpayments",
|
||||||
icon: <Icon component={FaCreditCard} />,
|
icon: <Icon component={FaCreditCard} />,
|
||||||
label: t("menus.header.enterpayment"),
|
label: t("menus.header.enterpayment"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -170,6 +208,7 @@ function Header({
|
|||||||
if (ImEXPay.treatment === "on") {
|
if (ImEXPay.treatment === "on") {
|
||||||
accountingChildren.push({
|
accountingChildren.push({
|
||||||
key: "entercardpayments",
|
key: "entercardpayments",
|
||||||
|
id: "header-accounting-entercardpayments",
|
||||||
icon: <Icon component={FaCreditCard} />,
|
icon: <Icon component={FaCreditCard} />,
|
||||||
label: t("menus.header.entercardpayment"),
|
label: t("menus.header.entercardpayment"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -194,6 +233,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "timetickets",
|
key: "timetickets",
|
||||||
|
id: "header-accounting-timetickets",
|
||||||
icon: <FieldTimeOutlined />,
|
icon: <FieldTimeOutlined />,
|
||||||
label: <Link to="/manage/timetickets">{t("menus.header.timetickets")}</Link>
|
label: <Link to="/manage/timetickets">{t("menus.header.timetickets")}</Link>
|
||||||
}
|
}
|
||||||
@@ -202,6 +242,7 @@ function Header({
|
|||||||
if (bodyshop?.md_tasks_presets?.use_approvals) {
|
if (bodyshop?.md_tasks_presets?.use_approvals) {
|
||||||
accountingChildren.push({
|
accountingChildren.push({
|
||||||
key: "ttapprovals",
|
key: "ttapprovals",
|
||||||
|
id: "header-accounting-ttapprovals",
|
||||||
icon: <FieldTimeOutlined />,
|
icon: <FieldTimeOutlined />,
|
||||||
label: <Link to="/manage/ttapprovals">{t("menus.header.ttapprovals")}</Link>
|
label: <Link to="/manage/ttapprovals">{t("menus.header.ttapprovals")}</Link>
|
||||||
});
|
});
|
||||||
@@ -211,6 +252,7 @@ function Header({
|
|||||||
key: "entertimetickets",
|
key: "entertimetickets",
|
||||||
icon: <Icon component={GiPlayerTime} />,
|
icon: <Icon component={GiPlayerTime} />,
|
||||||
label: t("menus.header.entertimeticket"),
|
label: t("menus.header.entertimeticket"),
|
||||||
|
id: "header-accounting-entertimetickets",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTimeTicketContext({
|
setTimeTicketContext({
|
||||||
actions: {},
|
actions: {},
|
||||||
@@ -231,6 +273,7 @@ function Header({
|
|||||||
const accountingExportChildren = [
|
const accountingExportChildren = [
|
||||||
{
|
{
|
||||||
key: "receivables",
|
key: "receivables",
|
||||||
|
id: "header-accounting-receivables",
|
||||||
label: <Link to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
label: <Link to="/manage/accounting/receivables">{t("menus.header.accounting-receivables")}</Link>
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -238,6 +281,7 @@ function Header({
|
|||||||
if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber)) || DmsAp.treatment === "on") {
|
if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber)) || DmsAp.treatment === "on") {
|
||||||
accountingExportChildren.push({
|
accountingExportChildren.push({
|
||||||
key: "payables",
|
key: "payables",
|
||||||
|
id: "header-accounting-payables",
|
||||||
label: <Link to="/manage/accounting/payables">{t("menus.header.accounting-payables")}</Link>
|
label: <Link to="/manage/accounting/payables">{t("menus.header.accounting-payables")}</Link>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -245,6 +289,7 @@ function Header({
|
|||||||
if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber))) {
|
if (!((bodyshop && bodyshop.cdk_dealerid) || (bodyshop && bodyshop.pbs_serialnumber))) {
|
||||||
accountingExportChildren.push({
|
accountingExportChildren.push({
|
||||||
key: "payments",
|
key: "payments",
|
||||||
|
id: "header-accounting-payments",
|
||||||
label: <Link to="/manage/accounting/payments">{t("menus.header.accounting-payments")}</Link>
|
label: <Link to="/manage/accounting/payments">{t("menus.header.accounting-payments")}</Link>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -255,6 +300,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "exportlogs",
|
key: "exportlogs",
|
||||||
|
id: "header-accounting-exportlogs",
|
||||||
label: <Link to="/manage/accounting/exportlogs">{t("menus.header.export-logs")}</Link>
|
label: <Link to="/manage/accounting/exportlogs">{t("menus.header.export-logs")}</Link>
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -268,6 +314,7 @@ function Header({
|
|||||||
) {
|
) {
|
||||||
accountingChildren.push({
|
accountingChildren.push({
|
||||||
key: "accountingexport",
|
key: "accountingexport",
|
||||||
|
id: "header-accounting-export",
|
||||||
icon: <ExportOutlined />,
|
icon: <ExportOutlined />,
|
||||||
label: t("menus.header.export"),
|
label: t("menus.header.export"),
|
||||||
children: accountingExportChildren
|
children: accountingExportChildren
|
||||||
@@ -278,10 +325,12 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "home",
|
key: "home",
|
||||||
icon: <HomeFilled />,
|
icon: <HomeFilled />,
|
||||||
|
id: "header-home",
|
||||||
label: <Link to="/manage/">{t("menus.header.home")}</Link>
|
label: <Link to="/manage/">{t("menus.header.home")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "schedule",
|
key: "schedule",
|
||||||
|
id: "header-schedule",
|
||||||
icon: <Icon component={FaCalendarAlt} />,
|
icon: <Icon component={FaCalendarAlt} />,
|
||||||
label: <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
|
label: <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
|
||||||
},
|
},
|
||||||
@@ -293,16 +342,19 @@ function Header({
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "activejobs",
|
key: "activejobs",
|
||||||
|
id: "header-active-jobs",
|
||||||
icon: <FileFilled />,
|
icon: <FileFilled />,
|
||||||
label: <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
label: <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "readyjobs",
|
key: "readyjobs",
|
||||||
|
id: "header-ready-jobs",
|
||||||
icon: <CheckCircleOutlined />,
|
icon: <CheckCircleOutlined />,
|
||||||
label: <Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
|
label: <Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "parts-queue",
|
key: "parts-queue",
|
||||||
|
id: "header-parts-queue",
|
||||||
icon: <ToolFilled />,
|
icon: <ToolFilled />,
|
||||||
label: <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
|
label: <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
|
||||||
},
|
},
|
||||||
@@ -314,6 +366,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "newjob",
|
key: "newjob",
|
||||||
|
id: "header-new-job",
|
||||||
icon: <FileAddOutlined />,
|
icon: <FileAddOutlined />,
|
||||||
label: <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
label: <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
||||||
},
|
},
|
||||||
@@ -322,6 +375,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "alljobs",
|
key: "alljobs",
|
||||||
|
id: "header-all-jobs",
|
||||||
icon: <UnorderedListOutlined />,
|
icon: <UnorderedListOutlined />,
|
||||||
label: <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
label: <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||||
},
|
},
|
||||||
@@ -330,6 +384,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "productionlist",
|
key: "productionlist",
|
||||||
|
id: "header-production-list",
|
||||||
icon: <ScheduleOutlined />,
|
icon: <ScheduleOutlined />,
|
||||||
label: <Link to="/manage/production/list">{t("menus.header.productionlist")}</Link>
|
label: <Link to="/manage/production/list">{t("menus.header.productionlist")}</Link>
|
||||||
},
|
},
|
||||||
@@ -341,6 +396,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "productionboard",
|
key: "productionboard",
|
||||||
|
id: "header-production-board",
|
||||||
icon: <Icon component={BsKanban} />,
|
icon: <Icon component={BsKanban} />,
|
||||||
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
||||||
}
|
}
|
||||||
@@ -358,6 +414,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "scoreboard",
|
key: "scoreboard",
|
||||||
|
id: "header-scoreboard",
|
||||||
icon: <LineChartOutlined />,
|
icon: <LineChartOutlined />,
|
||||||
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||||
}
|
}
|
||||||
@@ -368,15 +425,18 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "customers",
|
key: "customers",
|
||||||
icon: <UserOutlined />,
|
icon: <UserOutlined />,
|
||||||
|
id: "header-customers",
|
||||||
label: t("menus.header.customers"),
|
label: t("menus.header.customers"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "owners",
|
key: "owners",
|
||||||
|
id: "header-owners",
|
||||||
icon: <TeamOutlined />,
|
icon: <TeamOutlined />,
|
||||||
label: <Link to="/manage/owners">{t("menus.header.owners")}</Link>
|
label: <Link to="/manage/owners">{t("menus.header.owners")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "vehicles",
|
key: "vehicles",
|
||||||
|
id: "header-vehicles",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: <Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link>
|
label: <Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link>
|
||||||
}
|
}
|
||||||
@@ -390,21 +450,25 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "ccs",
|
key: "ccs",
|
||||||
|
id: "header-css",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: t("menus.header.courtesycars"),
|
label: t("menus.header.courtesycars"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "courtesycarsall",
|
key: "courtesycarsall",
|
||||||
|
id: "header-courtesycars-all",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "contracts",
|
key: "contracts",
|
||||||
|
id: "header-contracts",
|
||||||
icon: <FileFilled />,
|
icon: <FileFilled />,
|
||||||
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "newcontract",
|
key: "newcontract",
|
||||||
|
id: "header-newcontract",
|
||||||
icon: <FileAddFilled />,
|
icon: <FileAddFilled />,
|
||||||
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
||||||
}
|
}
|
||||||
@@ -417,6 +481,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "accounting",
|
key: "accounting",
|
||||||
|
id: "header-accounting",
|
||||||
icon: <DollarCircleFilled />,
|
icon: <DollarCircleFilled />,
|
||||||
label: t("menus.header.accounting"),
|
label: t("menus.header.accounting"),
|
||||||
children: accountingChildren
|
children: accountingChildren
|
||||||
@@ -425,6 +490,7 @@ function Header({
|
|||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: "phonebook",
|
key: "phonebook",
|
||||||
|
id: "header-phonebook",
|
||||||
icon: <PhoneOutlined />,
|
icon: <PhoneOutlined />,
|
||||||
label: <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
|
label: <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
|
||||||
},
|
},
|
||||||
@@ -436,28 +502,62 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "temporarydocs",
|
key: "temporarydocs",
|
||||||
|
id: "header-temporarydocs",
|
||||||
icon: <PaperClipOutlined />,
|
icon: <PaperClipOutlined />,
|
||||||
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
{
|
||||||
|
key: "tasks",
|
||||||
|
id: "tasks",
|
||||||
|
icon: <FaTasks />,
|
||||||
|
label: t("menus.header.tasks"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: "createTask",
|
||||||
|
icon: <PlusCircleOutlined />,
|
||||||
|
label: t("menus.header.create_task"),
|
||||||
|
onClick: () => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
actions: {},
|
||||||
|
context: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "mytasks",
|
||||||
|
icon: <FaTasks />,
|
||||||
|
label: <Link to="/manage/tasks/mytasks">{t("menus.header.my_tasks")}</Link>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "all_tasks",
|
||||||
|
icon: <FaTasks />,
|
||||||
|
label: <Link to="/manage/tasks/alltasks">{t("menus.header.all_tasks")}</Link>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "shopsubmenu",
|
key: "shopsubmenu",
|
||||||
|
id: "header-shopsubmenu",
|
||||||
icon: <SettingOutlined />,
|
icon: <SettingOutlined />,
|
||||||
label: t("menus.header.shop"),
|
label: t("menus.header.shop"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "shop",
|
key: "shop",
|
||||||
|
id: "header-shop",
|
||||||
icon: <Icon component={GiSettingsKnobs} />,
|
icon: <Icon component={GiSettingsKnobs} />,
|
||||||
label: <Link to="/manage/shop?tab=info">{t("menus.header.shop_config")}</Link>
|
label: <Link to="/manage/shop?tab=info">{t("menus.header.shop_config")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "dashboard",
|
key: "dashboard",
|
||||||
|
id: "header-dashboard",
|
||||||
icon: <DashboardFilled />,
|
icon: <DashboardFilled />,
|
||||||
label: <Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
|
label: <Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "reportcenter",
|
key: "reportcenter",
|
||||||
|
id: "header-reportcenter",
|
||||||
icon: <BarChartOutlined />,
|
icon: <BarChartOutlined />,
|
||||||
label: t("menus.header.reportcenter"),
|
label: t("menus.header.reportcenter"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -469,6 +569,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "shop-vendors",
|
key: "shop-vendors",
|
||||||
|
id: "header-shop-vendors",
|
||||||
icon: <Icon component={IoBusinessOutline} />,
|
icon: <Icon component={IoBusinessOutline} />,
|
||||||
label: <Link to="/manage/shop/vendors">{t("menus.header.shop_vendors")}</Link>
|
label: <Link to="/manage/shop/vendors">{t("menus.header.shop_vendors")}</Link>
|
||||||
},
|
},
|
||||||
@@ -480,6 +581,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "shop-csi",
|
key: "shop-csi",
|
||||||
|
id: "header-shop-csi",
|
||||||
icon: <Icon component={RiSurveyLine} />,
|
icon: <Icon component={RiSurveyLine} />,
|
||||||
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||||
}
|
}
|
||||||
@@ -493,6 +595,7 @@ function Header({
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "signout",
|
key: "signout",
|
||||||
|
id: "header-signout",
|
||||||
icon: <Icon component={FiLogOut} />,
|
icon: <Icon component={FiLogOut} />,
|
||||||
danger: true,
|
danger: true,
|
||||||
label: t("user.actions.signout"),
|
label: t("user.actions.signout"),
|
||||||
@@ -500,6 +603,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "help",
|
key: "help",
|
||||||
|
id: "header-help",
|
||||||
icon: <Icon component={QuestionCircleFilled} />,
|
icon: <Icon component={QuestionCircleFilled} />,
|
||||||
label: t("menus.header.help"),
|
label: t("menus.header.help"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -514,14 +618,22 @@ function Header({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// {
|
...(InstanceRenderManager({
|
||||||
// key: 'rescue',
|
imex: true,
|
||||||
// icon: <Icon component={CarFilled}/>,
|
rome: false,
|
||||||
// label: t("menus.header.rescueme"),
|
promanager: false
|
||||||
// onClick: () => {
|
})
|
||||||
// window.open("https://imexrescue.com/", "_blank");
|
? [
|
||||||
// }
|
{
|
||||||
// },
|
key: "rescue",
|
||||||
|
icon: <Icon component={CarFilled} />,
|
||||||
|
label: t("menus.header.rescueme"),
|
||||||
|
onClick: () => {
|
||||||
|
window.open("https://imexrescue.com/", "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
|
||||||
...(InstanceRenderManager({
|
...(InstanceRenderManager({
|
||||||
imex: true,
|
imex: true,
|
||||||
@@ -531,6 +643,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "shiftclock",
|
key: "shiftclock",
|
||||||
|
id: "header-shiftclock",
|
||||||
icon: <Icon component={GiPlayerTime} />,
|
icon: <Icon component={GiPlayerTime} />,
|
||||||
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||||
}
|
}
|
||||||
@@ -538,6 +651,7 @@ function Header({
|
|||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: "profile",
|
key: "profile",
|
||||||
|
id: "header-profile",
|
||||||
icon: <UserOutlined />,
|
icon: <UserOutlined />,
|
||||||
label: <Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
label: <Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
||||||
}
|
}
|
||||||
@@ -573,6 +687,7 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "recent",
|
key: "recent",
|
||||||
icon: <ClockCircleFilled />,
|
icon: <ClockCircleFilled />,
|
||||||
|
id: "header-recent",
|
||||||
children: recentItems.map((i, idx) => ({
|
children: recentItems.map((i, idx) => ({
|
||||||
key: idx,
|
key: idx,
|
||||||
label: <Link to={i.url}>{i.label}</Link>
|
label: <Link to={i.url}>{i.label}</Link>
|
||||||
@@ -586,6 +701,7 @@ function Header({
|
|||||||
imex: () => {
|
imex: () => {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "beta-switch",
|
key: "beta-switch",
|
||||||
|
id: "header-beta-switch",
|
||||||
style: { marginLeft: "auto" },
|
style: { marginLeft: "auto" },
|
||||||
label: (
|
label: (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Alert, Card, Col, Row, Space, Statistic, Tooltip, Typography } from 'antd';
|
import { Alert, Card, Col, Row, Space, Statistic, Tooltip, Typography } from "antd";
|
||||||
import Dinero from 'dinero.js';
|
import Dinero from "dinero.js";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import InstanceRenderManager from '../../utils/instanceRenderMgr';
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import AlertComponent from '../alert/alert.component';
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LoadingSkeleton from '../loading-skeleton/loading-skeleton.component';
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
import './job-bills-total.styles.scss';
|
import "./job-bills-total.styles.scss";
|
||||||
|
|
||||||
export default function JobBillsTotalComponent({
|
export default function JobBillsTotalComponent({
|
||||||
loading,
|
loading,
|
||||||
@@ -13,16 +13,16 @@ export default function JobBillsTotalComponent({
|
|||||||
partsOrders,
|
partsOrders,
|
||||||
jobTotals,
|
jobTotals,
|
||||||
showWarning,
|
showWarning,
|
||||||
warningCallback,
|
warningCallback
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
if (loading) return <LoadingSkeleton />;
|
if (loading) return <LoadingSkeleton />;
|
||||||
if (!!!jobTotals) {
|
if (!!!jobTotals) {
|
||||||
if (showWarning && warningCallback && typeof warningCallback === 'function') {
|
if (showWarning && warningCallback && typeof warningCallback === "function") {
|
||||||
warningCallback({ key: 'bills', warning: t('jobs.errors.nofinancial') });
|
warningCallback({ key: "bills", warning: t("jobs.errors.nofinancial") });
|
||||||
}
|
}
|
||||||
return <AlertComponent type="error" message={t('jobs.errors.nofinancial')} />;
|
return <AlertComponent type="error" message={t("jobs.errors.nofinancial")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const totals = jobTotals;
|
const totals = jobTotals;
|
||||||
@@ -109,64 +109,60 @@ export default function JobBillsTotalComponent({
|
|||||||
const discrepWithCms = discrepWithLbrAdj.add(totalReturns);
|
const discrepWithCms = discrepWithLbrAdj.add(totalReturns);
|
||||||
const calculatedCreditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
|
const calculatedCreditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
|
||||||
|
|
||||||
if (showWarning && warningCallback && typeof warningCallback === 'function') {
|
if (showWarning && warningCallback && typeof warningCallback === "function") {
|
||||||
if (
|
if (discrepWithCms.getAmount() !== 0) {
|
||||||
discrepWithCms.getAmount() !== 0 ||
|
|
||||||
discrepWithLbrAdj.getAmount() !== 0 ||
|
|
||||||
discrepancy.getAmount() !== 0
|
|
||||||
) {
|
|
||||||
warningCallback({
|
warningCallback({
|
||||||
key: 'bills',
|
key: "bills",
|
||||||
warning: t('jobs.labels.outstanding_reconciliation_discrep'),
|
warning: t("jobs.labels.outstanding_reconciliation_discrep")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (calculatedCreditsNotReceived.getAmount() > 0) {
|
if (calculatedCreditsNotReceived.getAmount() > 0) {
|
||||||
warningCallback({ key: 'cm', warning: t('jobs.labels.outstanding_credit_memos') });
|
warningCallback({ key: "cm", warning: t("jobs.labels.outstanding_credit_memos") });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col md={24} lg={18}>
|
<Col md={24} lg={18}>
|
||||||
<Card title={t('jobs.labels.jobtotals')} style={{ height: '100%' }}>
|
<Card title={t("jobs.labels.jobtotals")} style={{ height: "100%" }}>
|
||||||
<Space wrap size="large">
|
<Space wrap size="large">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.partstotal'),
|
__html: t("jobs.labels.plitooltips.partstotal")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic title={t('jobs.labels.rosaletotal')} value={totalPartsSublet.toFormat()} />
|
<Statistic title={t("jobs.labels.rosaletotal")} value={totalPartsSublet.toFormat()} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Typography.Title>-</Typography.Title>
|
<Typography.Title>-</Typography.Title>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.billtotal'),
|
__html: t("jobs.labels.plitooltips.billtotal")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic title={t('bills.labels.retailtotal')} value={billTotals.toFormat()} />
|
<Statistic title={t("bills.labels.retailtotal")} value={billTotals.toFormat()} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Typography.Title>=</Typography.Title>
|
<Typography.Title>=</Typography.Title>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.discrep1'),
|
__html: t("jobs.labels.plitooltips.discrep1")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t('bills.labels.discrepancy')}
|
title={t("bills.labels.discrepancy")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: discrepancy.getAmount() === 0 ? 'green' : 'red',
|
color: discrepancy.getAmount() === 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={discrepancy.toFormat()}
|
value={discrepancy.toFormat()}
|
||||||
/>
|
/>
|
||||||
@@ -176,27 +172,27 @@ export default function JobBillsTotalComponent({
|
|||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.laboradj'),
|
__html: t("jobs.labels.plitooltips.laboradj")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic title={t('bills.labels.dedfromlbr')} value={lbrAdjustments.toFormat()} />
|
<Statistic title={t("bills.labels.dedfromlbr")} value={lbrAdjustments.toFormat()} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Typography.Title>=</Typography.Title>
|
<Typography.Title>=</Typography.Title>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.discrep2'),
|
__html: t("jobs.labels.plitooltips.discrep2")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t('bills.labels.discrepancy')}
|
title={t("bills.labels.discrepancy")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: discrepWithLbrAdj.getAmount() === 0 ? 'green' : 'red',
|
color: discrepWithLbrAdj.getAmount() === 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={discrepWithLbrAdj.toFormat()}
|
value={discrepWithLbrAdj.toFormat()}
|
||||||
/>
|
/>
|
||||||
@@ -206,27 +202,27 @@ export default function JobBillsTotalComponent({
|
|||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.creditmemos'),
|
__html: t("jobs.labels.plitooltips.creditmemos")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic title={t('bills.labels.totalreturns')} value={totalReturns.toFormat()} />
|
<Statistic title={t("bills.labels.totalreturns")} value={totalReturns.toFormat()} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Typography.Title>=</Typography.Title>
|
<Typography.Title>=</Typography.Title>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.discrep3'),
|
__html: t("jobs.labels.plitooltips.discrep3")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t('bills.labels.discrepancy')}
|
title={t("bills.labels.discrepancy")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: discrepWithCms.getAmount() === 0 ? 'green' : 'red',
|
color: discrepWithCms.getAmount() === 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={discrepWithCms.toFormat()}
|
value={discrepWithCms.toFormat()}
|
||||||
/>
|
/>
|
||||||
@@ -238,40 +234,40 @@ export default function JobBillsTotalComponent({
|
|||||||
discrepWithLbrAdj.getAmount() !== 0 ||
|
discrepWithLbrAdj.getAmount() !== 0 ||
|
||||||
discrepancy.getAmount() !== 0) && (
|
discrepancy.getAmount() !== 0) && (
|
||||||
<Alert
|
<Alert
|
||||||
style={{ margin: '8px 0px' }}
|
style={{ margin: "8px 0px" }}
|
||||||
type="warning"
|
type="warning"
|
||||||
message={t('jobs.labels.outstanding_reconciliation_discrep')}
|
message={t("jobs.labels.outstanding_reconciliation_discrep")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={24} lg={6}>
|
<Col md={24} lg={6}>
|
||||||
<Card title={t('jobs.labels.returntotals')} style={{ height: '100%' }}>
|
<Card title={t("jobs.labels.returntotals")} style={{ height: "100%" }}>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.totalreturns'),
|
__html: t("jobs.labels.plitooltips.totalreturns")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic title={t('bills.labels.totalreturns')} value={totalReturns.toFormat()} />
|
<Statistic title={t("bills.labels.totalreturns")} value={totalReturns.toFormat()} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.calculatedcreditsnotreceived'),
|
__html: t("jobs.labels.plitooltips.calculatedcreditsnotreceived")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t('bills.labels.calculatedcreditsnotreceived')}
|
title={t("bills.labels.calculatedcreditsnotreceived")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: calculatedCreditsNotReceived.getAmount() <= 0 ? 'green' : 'red',
|
color: calculatedCreditsNotReceived.getAmount() <= 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={
|
value={
|
||||||
calculatedCreditsNotReceived.getAmount() >= 0
|
calculatedCreditsNotReceived.getAmount() >= 0
|
||||||
@@ -284,15 +280,15 @@ export default function JobBillsTotalComponent({
|
|||||||
title={
|
title={
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: t('jobs.labels.plitooltips.creditsnotreceived'),
|
__html: t("jobs.labels.plitooltips.creditsnotreceived")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Statistic
|
<Statistic
|
||||||
title={t('bills.labels.creditsnotreceived')}
|
title={t("bills.labels.creditsnotreceived")}
|
||||||
valueStyle={{
|
valueStyle={{
|
||||||
color: totalReturnsMarkedNotReceived.getAmount() <= 0 ? 'green' : 'red',
|
color: totalReturnsMarkedNotReceived.getAmount() <= 0 ? "green" : "red"
|
||||||
}}
|
}}
|
||||||
value={
|
value={
|
||||||
totalReturnsMarkedNotReceived.getAmount() >= 0
|
totalReturnsMarkedNotReceived.getAmount() >= 0
|
||||||
@@ -303,11 +299,7 @@ export default function JobBillsTotalComponent({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Space>
|
</Space>
|
||||||
{showWarning && calculatedCreditsNotReceived.getAmount() > 0 && (
|
{showWarning && calculatedCreditsNotReceived.getAmount() > 0 && (
|
||||||
<Alert
|
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstanding_credit_memos")} />
|
||||||
style={{ margin: '8px 0px' }}
|
|
||||||
type="warning"
|
|
||||||
message={t('jobs.labels.outstanding_credit_memos')}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from "@apollo/client";
|
||||||
import { useSplitTreatments } from '@splitsoftware/splitio-react';
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { GET_LINE_TICKET_BY_PK } from '../../graphql/jobs-lines.queries';
|
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from '../alert/alert.component';
|
import AlertComponent from "../alert/alert.component";
|
||||||
import LaborAllocationsTableComponent from '../labor-allocations-table/labor-allocations-table.component';
|
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
|
||||||
import PayrollLaborAllocationsTable from '../labor-allocations-table/labor-allocations-table.payroll.component';
|
import PayrollLaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.payroll.component";
|
||||||
import LoadingSkeleton from '../loading-skeleton/loading-skeleton.component';
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -24,21 +24,21 @@ export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardLabor
|
|||||||
export function JobCloseRoGuardLabor({ job, jobRO, bodyshop, form, warningCallback }) {
|
export function JobCloseRoGuardLabor({ job, jobRO, bodyshop, form, warningCallback }) {
|
||||||
const { loading, error, data, refetch } = useQuery(GET_LINE_TICKET_BY_PK, {
|
const { loading, error, data, refetch } = useQuery(GET_LINE_TICKET_BY_PK, {
|
||||||
variables: { id: job.id },
|
variables: { id: job.id },
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: 'network-only',
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
const {
|
const {
|
||||||
treatments: { Enhanced_Payroll },
|
treatments: { Enhanced_Payroll }
|
||||||
} = useSplitTreatments({
|
} = useSplitTreatments({
|
||||||
attributes: {},
|
attributes: {},
|
||||||
names: ['Enhanced_Payroll'],
|
names: ["Enhanced_Payroll"],
|
||||||
splitKey: bodyshop.imexshopid,
|
splitKey: bodyshop.imexshopid
|
||||||
});
|
});
|
||||||
|
|
||||||
if (loading) return <LoadingSkeleton />;
|
if (loading) return <LoadingSkeleton />;
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return Enhanced_Payroll.treatment === 'on' ? (
|
return Enhanced_Payroll.treatment === "on" ? (
|
||||||
<PayrollLaborAllocationsTable
|
<PayrollLaborAllocationsTable
|
||||||
jobId={job.id}
|
jobId={job.id}
|
||||||
timetickets={data ? data.timetickets : []}
|
timetickets={data ? data.timetickets : []}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import { Alert, Card } from 'antd';
|
import { Alert, Card } from "antd";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from "react";
|
||||||
import Dinero from 'dinero.js';
|
import Dinero from "dinero.js";
|
||||||
import DataLabel from '../data-label/data-label.component';
|
import DataLabel from "../data-label/data-label.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -41,25 +41,21 @@ export function JobCloseRoGuardProfit({ job, jobRO, bodyshop, form, warningCallb
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (balance.getAmount() !== 0) {
|
if (balance.getAmount() !== 0) {
|
||||||
warningCallback({ key: 'ar', warning: t('jobs.labels.outstanding_ar') });
|
warningCallback({ key: "ar", warning: t("jobs.labels.outstanding_ar") });
|
||||||
}
|
}
|
||||||
}, [balance, t, warningCallback]);
|
}, [balance, t, warningCallback]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t('jobs.labels.accountsreceivable')} style={{ height: '100%' }}>
|
<Card title={t("jobs.labels.accountsreceivable")} style={{ height: "100%" }}>
|
||||||
<DataLabel label={t('payments.labels.totalpayments')}>{total.toFormat()}</DataLabel>
|
<DataLabel label={t("payments.labels.totalpayments")}>{total.toFormat()}</DataLabel>
|
||||||
<DataLabel
|
<DataLabel
|
||||||
valueStyle={{ color: balance.getAmount() !== 0 ? 'red' : 'green' }}
|
valueStyle={{ color: balance.getAmount() !== 0 ? "red" : "green" }}
|
||||||
label={t('payments.labels.balance')}
|
label={t("payments.labels.balance")}
|
||||||
>
|
>
|
||||||
{balance.toFormat()}
|
{balance.toFormat()}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
{balance.getAmount() !== 0 && (
|
{balance.getAmount() !== 0 && (
|
||||||
<Alert
|
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstanding_ar")} />
|
||||||
style={{ margin: '8px 0px' }}
|
|
||||||
type="warning"
|
|
||||||
message={t('jobs.labels.outstanding_ar')}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from "@apollo/client";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_BILLS_BY_JOBID } from '../../graphql/bills.queries';
|
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AlertComponent from '../alert/alert.component';
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobBillsTotalComponent from '../job-bills-total/job-bills-total.component';
|
import JobBillsTotalComponent from "../job-bills-total/job-bills-total.component";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -21,8 +21,8 @@ export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardBills
|
|||||||
export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form, warningCallback }) {
|
export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form, warningCallback }) {
|
||||||
const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, {
|
const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, {
|
||||||
variables: { jobid: job.id },
|
variables: { jobid: job.id },
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: 'network-only',
|
nextFetchPolicy: "network-only"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|||||||
@@ -76,20 +76,21 @@ export function JobCloseRoGuardContainer({ job, jobRO, bodyshop, form }) {
|
|||||||
<Col className="ro-guard-col-multiple" md={24} lg={6}>
|
<Col className="ro-guard-col-multiple" md={24} lg={6}>
|
||||||
<Row gutter={[32, 32]} style={{ height: "100%" }}>
|
<Row gutter={[32, 32]} style={{ height: "100%" }}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<JobCloseRoGuardProfit
|
<JobCloseRoGuardProfit job={job} form={form} warningCallback={warningCallback} />
|
||||||
job={job}
|
|
||||||
form={form} //warningCallback={warningCallback}
|
|
||||||
/>
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<JobCloseRoGuardAr job={job} form={form} warningCallback={warningCallback} />
|
<JobCloseRoGuardAr job={job} form={form} warningCallback={warningCallback} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
<Col className="ro-guard-col-50" md={24} lg={8}>
|
{InstanceRenderManager({
|
||||||
<JobCloseRoGuardSublet job={job} warningCallback={warningCallback} />
|
rome: (
|
||||||
{InstanceRenderManager({ rome: <JobCloseRoGuardPpd job={job} warningCallback={warningCallback} /> })}
|
<Col md={24} lg={8}>
|
||||||
</Col>
|
{/* <JobCloseRoGuardSublet job={job} warningCallback={warningCallback} /> */}
|
||||||
|
<JobCloseRoGuardPpd job={job} warningCallback={warningCallback} />
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
})}
|
||||||
<Col className="ro-guard-col" md={24} lg={10}>
|
<Col className="ro-guard-col" md={24} lg={10}>
|
||||||
<JobCloseRoGuardLabor job={job} form={form} warningCallback={warningCallback} />
|
<JobCloseRoGuardLabor job={job} form={form} warningCallback={warningCallback} />
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { LockOutlined } from '@ant-design/icons';
|
import { Alert, Card } from "antd";
|
||||||
import { Card, Form, Input } from 'antd';
|
import axios from "axios";
|
||||||
import axios from 'axios';
|
import { useTranslation } from "react-i18next";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { connect } from "react-redux";
|
||||||
import { connect } from 'react-redux';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import JobCostingStatistics from "../job-costing-statistics/job-costing-statistics.component";
|
||||||
import JobCostingStatistics from '../job-costing-statistics/job-costing-statistics.component';
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
import LoadingSkeleton from '../loading-skeleton/loading-skeleton.component';
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -31,7 +30,7 @@ export function JobCloseRoGuardProfit({ job, jobRO, bodyshop, form, warningCallb
|
|||||||
try {
|
try {
|
||||||
if (job.id) {
|
if (job.id) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const { data } = await axios.post('/job/costing', { jobid: job.id });
|
const { data } = await axios.post("/job/costing", { jobid: job.id });
|
||||||
setCostingData(data);
|
setCostingData(data);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
@@ -45,16 +44,19 @@ export function JobCloseRoGuardProfit({ job, jobRO, bodyshop, form, warningCallb
|
|||||||
parseFloat(costingData?.summaryData.gppercent) < bodyshop?.md_ro_guard?.totalgppercent_minimum;
|
parseFloat(costingData?.summaryData.gppercent) < bodyshop?.md_ro_guard?.totalgppercent_minimum;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (enforceProfitPassword && typeof warningCallback === 'function') {
|
if (enforceProfitPassword && typeof warningCallback === "function") {
|
||||||
warningCallback({ key: 'profit', warning: t('jobs.labels.profitbypassrequired') });
|
warningCallback({ key: "profit", warning: t("jobs.labels.profitbypassrequired") });
|
||||||
}
|
}
|
||||||
}, [enforceProfitPassword, t, warningCallback]);
|
}, [enforceProfitPassword, t, warningCallback]);
|
||||||
|
|
||||||
if (loading || !costingData) return <LoadingSkeleton />;
|
if (loading || !costingData) return <LoadingSkeleton />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t('jobs.labels.profits')} style={{ height: '100%' }}>
|
<Card title={t("jobs.labels.profits")} style={{ height: "100%" }}>
|
||||||
<JobCostingStatistics summaryData={costingData?.summaryData} onlyGP />
|
<JobCostingStatistics summaryData={costingData?.summaryData} onlyGP />
|
||||||
|
{enforceProfitPassword && (
|
||||||
|
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.profitbypassrequired")} />
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import { Alert, Card, Table } from 'antd';
|
import { Alert, Card, Table } from "antd";
|
||||||
import { t } from 'i18next';
|
import { t } from "i18next";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from '../../utils/CurrencyFormatter';
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from '../../utils/sorters';
|
import { alphaSort } from "../../utils/sorters";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -20,69 +20,56 @@ export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRGuardSublet
|
|||||||
|
|
||||||
export function JobCloseRGuardSublet({ job, jobRO, bodyshop, form, warningCallback }) {
|
export function JobCloseRGuardSublet({ job, jobRO, bodyshop, form, warningCallback }) {
|
||||||
const subletsNotDone = job?.joblines.filter(
|
const subletsNotDone = job?.joblines.filter(
|
||||||
(j) =>
|
(j) => (j.part_type === "PAS" || j.part_type === "PASL") && (!j.sublet_completed || !j.sublet_ignored)
|
||||||
(j.part_type === 'PAS' || j.part_type === 'PASL') &&
|
|
||||||
(!j.sublet_completed || !j.sublet_ignored)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t('joblines.fields.line_desc'),
|
title: t("joblines.fields.line_desc"),
|
||||||
dataIndex: 'line_desc',
|
dataIndex: "line_desc",
|
||||||
fixed: 'left',
|
fixed: "left",
|
||||||
key: 'line_desc',
|
key: "line_desc",
|
||||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
onCell: (record) => ({
|
onCell: (record) => ({
|
||||||
className: record.manual_line && 'job-line-manual',
|
className: record.manual_line && "job-line-manual",
|
||||||
style: {
|
style: {
|
||||||
...(record.critical ? { boxShadow: ' -.5em 0 0 #FFC107' } : {}),
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {})
|
||||||
},
|
}
|
||||||
}),
|
}),
|
||||||
ellipsis: true,
|
ellipsis: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('joblines.fields.act_price'),
|
title: t("joblines.fields.act_price"),
|
||||||
dataIndex: 'act_price',
|
dataIndex: "act_price",
|
||||||
key: 'act_price',
|
key: "act_price",
|
||||||
sorter: (a, b) => a.act_price - b.act_price,
|
sorter: (a, b) => a.act_price - b.act_price,
|
||||||
|
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
render: (text, record) => <CurrencyFormatter>{record.act_price}</CurrencyFormatter>,
|
render: (text, record) => <CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('joblines.fields.part_qty'),
|
title: t("joblines.fields.part_qty"),
|
||||||
dataIndex: 'part_qty',
|
dataIndex: "part_qty",
|
||||||
key: 'part_qty',
|
key: "part_qty"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('joblines.fields.notes'),
|
title: t("joblines.fields.notes"),
|
||||||
dataIndex: 'notes',
|
dataIndex: "notes",
|
||||||
key: 'notes',
|
key: "notes"
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (subletsNotDone.length > 0) {
|
if (subletsNotDone.length > 0) {
|
||||||
warningCallback({ key: 'sublet', warning: t('jobs.labels.outstanding_sublets') });
|
warningCallback({ key: "sublet", warning: t("jobs.labels.outstanding_sublets") });
|
||||||
}
|
}
|
||||||
}, [subletsNotDone.length, warningCallback]);
|
}, [subletsNotDone.length, warningCallback]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t('jobs.labels.subletsnotcompleted')}>
|
<Card title={t("jobs.labels.subletsnotcompleted")}>
|
||||||
<Table
|
<Table dataSource={subletsNotDone} columns={columns} pagination={false} rowKey="id" bordered size="small" />
|
||||||
dataSource={subletsNotDone}
|
|
||||||
columns={columns}
|
|
||||||
pagination={false}
|
|
||||||
rowKey="id"
|
|
||||||
bordered
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
{subletsNotDone.length > 0 && (
|
{subletsNotDone.length > 0 && (
|
||||||
<Alert
|
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstanding_sublets")} />
|
||||||
style={{ margin: '8px 0px' }}
|
|
||||||
type="warning"
|
|
||||||
message={t('jobs.labels.outstanding_sublets')}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectJobReadOnly } from '../../redux/application/application.selectors';
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import JobLifecycleComponent from '../job-lifecycle/job-lifecycle.component';
|
import JobLifecycleComponent from "../job-lifecycle/job-lifecycle.component";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd";
|
import { Col, Row, Skeleton, Space, Timeline, Typography } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -7,17 +7,19 @@ import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
||||||
|
import TaskListContainer from "../task-list/task-list.container.jsx";
|
||||||
|
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
});
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
|
||||||
|
|
||||||
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
||||||
@@ -43,13 +45,25 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
|||||||
? data.parts_order_lines.map((line) => ({
|
? data.parts_order_lines.map((line) => ({
|
||||||
key: line.id,
|
key: line.id,
|
||||||
children: (
|
children: (
|
||||||
<Space split={<Divider type="vertical" />} wrap>
|
<Row wrap>
|
||||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
|
<Col span={4}>
|
||||||
{line.parts_order.order_number}
|
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}>
|
||||||
</Link>
|
{line.parts_order.order_number}
|
||||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
</Link>
|
||||||
{line.parts_order.vendor.name}
|
</Col>
|
||||||
</Space>
|
<Col span={4}>
|
||||||
|
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>{line.parts_order.vendor.name}</Col>
|
||||||
|
{line.backordered_eta ? (
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("parts_orders.fields.backordered_eta")}: `}
|
||||||
|
<DateFormatter>{line.backordered_eta}</DateFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
) : null}
|
||||||
|
</Row>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
: [
|
: [
|
||||||
@@ -61,48 +75,6 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
|||||||
}
|
}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={24} lg={8}>
|
|
||||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
|
||||||
<Timeline
|
|
||||||
items={
|
|
||||||
data.billlines.length > 0
|
|
||||||
? data.billlines.map((line) => ({
|
|
||||||
key: line.id,
|
|
||||||
children: (
|
|
||||||
<Row wrap>
|
|
||||||
<Col span={4}>
|
|
||||||
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
|
||||||
{line.bill.invoice_number}
|
|
||||||
</Link>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_price")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_cost")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<DateFormatter>{line.bill.date}</DateFormatter>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}> {line.bill.vendor.name}</Col>
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
: [
|
|
||||||
{
|
|
||||||
key: "no-orders",
|
|
||||||
children: t("bills.labels.nobilllines")
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col md={24} lg={8}>
|
<Col md={24} lg={8}>
|
||||||
<Typography.Title level={4}>{t("parts_dispatch.labels.parts_dispatch")}</Typography.Title>
|
<Typography.Title level={4}>{t("parts_dispatch.labels.parts_dispatch")}</Typography.Title>
|
||||||
<Timeline
|
<Timeline
|
||||||
@@ -111,23 +83,84 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
|||||||
? data.parts_dispatch_lines.map((line) => ({
|
? data.parts_dispatch_lines.map((line) => ({
|
||||||
key: line.id,
|
key: line.id,
|
||||||
children: (
|
children: (
|
||||||
<Space split={<Divider type="vertical" />} wrap>
|
<Row>
|
||||||
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
<Col span={8}>
|
||||||
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
|
||||||
<Space>
|
</Col>
|
||||||
{t("parts_dispatch_lines.fields.accepted_at")}
|
<Col span={8}>
|
||||||
<DateFormatter>{line.accepted_at}</DateFormatter>
|
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
|
||||||
</Space>
|
</Col>
|
||||||
</Space>
|
<Col span={8}>
|
||||||
|
<Space>
|
||||||
|
{t("parts_dispatch_lines.fields.accepted_at")}
|
||||||
|
<DateFormatter>{line.accepted_at}</DateFormatter>
|
||||||
|
</Space>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
: {
|
: [
|
||||||
key: "dispatch-lines",
|
{
|
||||||
children: t("parts_orders.labels.notyetordered")
|
key: "dispatch-lines",
|
||||||
}
|
children: t("parts_dispatch.labels.notyetdispatched")
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
<FeatureWrapper featureName="bills" noauth={() => null}>
|
||||||
|
<Col md={24} lg={8}>
|
||||||
|
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||||
|
<Timeline
|
||||||
|
items={
|
||||||
|
data.billlines.length > 0
|
||||||
|
? data.billlines.map((line) => ({
|
||||||
|
key: line.id,
|
||||||
|
children: (
|
||||||
|
<Row wrap>
|
||||||
|
<Col span={4}>
|
||||||
|
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
||||||
|
{line.bill.invoice_number}
|
||||||
|
</Link>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_price")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_cost")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<DateFormatter>{line.bill.date}</DateFormatter>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}> {line.bill.vendor.name}</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: "no-orders",
|
||||||
|
children: t("bills.labels.nobilllines")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</FeatureWrapper>
|
||||||
|
<Col md={24} lg={24}>
|
||||||
|
<TaskListContainer
|
||||||
|
parentJobId={jobid}
|
||||||
|
relationshipType={"joblineid"}
|
||||||
|
relationshipId={jobline.id}
|
||||||
|
query={{ QUERY_JOBLINE_TASKS_PAGINATED }}
|
||||||
|
titleTranslation="tasks.titles.job_tasks"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-
|
|||||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||||
import JobLinesExpander from "./job-lines-expander.component";
|
import JobLinesExpander from "./job-lines-expander.component";
|
||||||
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
||||||
|
import { FaTasks } from "react-icons/fa";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -52,7 +54,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
|
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
|
||||||
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" }))
|
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobLinesComponent({
|
export function JobLinesComponent({
|
||||||
@@ -67,7 +70,8 @@ export function JobLinesComponent({
|
|||||||
job,
|
job,
|
||||||
setJobLineEditContext,
|
setJobLineEditContext,
|
||||||
form,
|
form,
|
||||||
setBillEnterContext
|
setBillEnterContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
|
||||||
const {
|
const {
|
||||||
@@ -331,6 +335,24 @@ export function JobLinesComponent({
|
|||||||
>
|
>
|
||||||
<EditFilled />
|
<EditFilled />
|
||||||
</Button>
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
context: {
|
||||||
|
jobid: job.id,
|
||||||
|
joblineid: record.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaTasks />
|
||||||
|
</Button>
|
||||||
|
{(record.manual_line || jobIsPrivate) && (
|
||||||
|
<>
|
||||||
<Button
|
<Button
|
||||||
disabled={jobRO}
|
disabled={jobRO}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -452,7 +474,7 @@ export function JobLinesComponent({
|
|||||||
vendorid: bodyshop.inhousevendorid,
|
vendorid: bodyshop.inhousevendorid,
|
||||||
invoice_number: "ih",
|
invoice_number: "ih",
|
||||||
isinhouse: true,
|
isinhouse: true,
|
||||||
date: new dayjs(),
|
date: dayjs(),
|
||||||
total: 0,
|
total: 0,
|
||||||
billlines: selectedLines.map((p) => {
|
billlines: selectedLines.map((p) => {
|
||||||
return {
|
return {
|
||||||
@@ -531,7 +553,7 @@ export function JobLinesComponent({
|
|||||||
>
|
>
|
||||||
{t("joblines.actions.new")}
|
{t("joblines.actions.new")}
|
||||||
</Button>
|
</Button>
|
||||||
{bodyshop.region_config.toLowerCase().startsWith("us") && <JobSendPartPriceChangeComponent job={job} />}
|
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} /> })}
|
||||||
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export function JobLineConvertToLabor({ children, jobline, job, insertAuditTrail
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
// date: new dayjs(),
|
// date: dayjs(),
|
||||||
// bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
// bodyhrs: Math.round(v.bodyhrs * 10) / 10,
|
||||||
// painthrs: Math.round(v.painthrs * 10) / 10,
|
// painthrs: Math.round(v.painthrs * 10) / 10,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { LoadingOutlined } from "@ant-design/icons";
|
||||||
import { useLazyQuery } from "@apollo/client";
|
import { useLazyQuery } from "@apollo/client";
|
||||||
import { Select, Space, Spin, Tag } from "antd";
|
import { Select, Space, Spin, Tag } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
@@ -6,8 +7,6 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import { SearchOutlined } from "@ant-design/icons";
|
|
||||||
import { LoadingOutlined } from "@ant-design/icons";
|
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ const JobSearchSelect = (
|
|||||||
filterOption={false}
|
filterOption={false}
|
||||||
onSearch={handleSearch}
|
onSearch={handleSearch}
|
||||||
//loading={loading || idLoading}
|
//loading={loading || idLoading}
|
||||||
suffixIcon={loading && <Spin />}
|
suffixIcon={(loading || idLoading) && <Spin />}
|
||||||
notFoundContent={loading ? <LoadingOutlined /> : null}
|
notFoundContent={loading ? <LoadingOutlined /> : null}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
@@ -86,9 +85,9 @@ const JobSearchSelect = (
|
|||||||
<span>
|
<span>
|
||||||
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
|
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
|
||||||
o.ro_number || t("general.labels.na")
|
o.ro_number || t("general.labels.na")
|
||||||
} | ${OwnerNameDisplayFunction(o)} | ${
|
} | ${OwnerNameDisplayFunction(o)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
|
||||||
o.v_model_yr || ""
|
o.v_model_desc || ""
|
||||||
} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`}
|
}`}
|
||||||
</span>
|
</span>
|
||||||
<Tag>
|
<Tag>
|
||||||
<strong>{o.status}</strong>
|
<strong>{o.status}</strong>
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
bold: true
|
bold: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
// TODO: was removed by Patrick during a CI bug fix.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
|||||||
@@ -15,23 +15,43 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
|||||||
const linesToUpdate = [];
|
const linesToUpdate = [];
|
||||||
|
|
||||||
newLines.forEach((newLine) => {
|
newLines.forEach((newLine) => {
|
||||||
const matchingIndex = existingLines.findIndex((eL) => eL.unq_seq === newLine.unq_seq);
|
const matchingIndexLines = [];
|
||||||
|
existingLines.forEach((eL, index) => {
|
||||||
|
if (eL.unq_seq === newLine.unq_seq) {
|
||||||
|
matchingIndexLines.push({ index, record: eL });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
//Should do a check to make sure there is only 1 matching unq sequence number.
|
if (matchingIndexLines.length === 0) {
|
||||||
|
|
||||||
if (matchingIndex >= 0) {
|
|
||||||
//Found a relevant matching line. Add it to lines to update.
|
|
||||||
linesToUpdate.push({
|
|
||||||
id: existingLines[matchingIndex].id,
|
|
||||||
newData: { ...newLine, removed: false, act_price_before_ppc: null }
|
|
||||||
});
|
|
||||||
|
|
||||||
//Splice out item we found for performance.
|
|
||||||
existingLines.splice(matchingIndex, 1);
|
|
||||||
} else {
|
|
||||||
//Didn't find a match. Must be a new line.
|
//Didn't find a match. Must be a new line.
|
||||||
linesToInsert.push(newLine);
|
linesToInsert.push(newLine);
|
||||||
}
|
}
|
||||||
|
//If we find only 1, we can use the old logic.
|
||||||
|
else if (matchingIndexLines.length === 1) {
|
||||||
|
//Found a relevant matching line. Add it to lines to update.
|
||||||
|
linesToUpdate.push({
|
||||||
|
id: matchingIndexLines[0].record.id,
|
||||||
|
newData: { ...newLine, removed: false, act_price_before_ppc: null }
|
||||||
|
});
|
||||||
|
//Splice out item we found for performance.
|
||||||
|
existingLines.splice(matchingIndexLines[0].index, 1);
|
||||||
|
} else if (matchingIndexLines.length === 2) {
|
||||||
|
//if we find 2, we need to separate it out by the non-refinish lines and splice one out.
|
||||||
|
//Find the mathcing one depending on whether this is the refiniish one or not.
|
||||||
|
const matchingLine = matchingIndexLines.find((i) =>
|
||||||
|
newLine.mod_lbr_ty === "LAR" ? i.record.mod_lbr_ty === "LAR" : i.record.mod_lbr_ty !== "LAR"
|
||||||
|
);
|
||||||
|
|
||||||
|
linesToUpdate.push({
|
||||||
|
id: matchingLine.record.id,
|
||||||
|
newData: { ...newLine, removed: false, act_price_before_ppc: null }
|
||||||
|
});
|
||||||
|
//Splice out item we found for performance.
|
||||||
|
existingLines.splice(matchingLine.index, 1);
|
||||||
|
} else {
|
||||||
|
//We found more than 2 matching lines. We should never get here. Throw a warning and back out!
|
||||||
|
throw new Error("Too many matching lines found. Ensure EMS file is valid.");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Wahtever is left in the existing lines, are lines that should be removed.
|
//Wahtever is left in the existing lines, are lines that should be removed.
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
|
|||||||
executeFunction: true,
|
executeFunction: true,
|
||||||
rome: ResolveCCCLineIssues,
|
rome: ResolveCCCLineIssues,
|
||||||
promanager: ResolveCCCLineIssues,
|
promanager: ResolveCCCLineIssues,
|
||||||
args: [(supp, bodyshop)]
|
args: [supp, bodyshop]
|
||||||
});
|
});
|
||||||
|
|
||||||
await InstanceRenderManager({
|
await InstanceRenderManager({
|
||||||
@@ -608,13 +608,12 @@ function ResolveCCCLineIssues(estData, bodyshop) {
|
|||||||
//Web Est seems to have additional costs with UNQ_SEQ 0. Keep them all?
|
//Web Est seems to have additional costs with UNQ_SEQ 0. Keep them all?
|
||||||
if (line.unq_seq === 0) return;
|
if (line.unq_seq === 0) return;
|
||||||
if (index0ActPrice !== line.act_price) {
|
if (index0ActPrice !== line.act_price) {
|
||||||
line.notes += ` | Price override.`;
|
// line.notes += ` | Price override.`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const indexInEstData = estData.joblines.data.findIndex((l) => l.unq_seq === line.unq_seq);
|
const indexInEstData = estData.joblines.data.findIndex((l) => l.unq_seq === line.unq_seq);
|
||||||
estData.joblines.data[
|
//estData.joblines.data[indexInEstData].notes +=
|
||||||
indexInEstData
|
// ` | Act Price delete. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`;
|
||||||
].notes += ` | Scrubbed due to the line_ref issue. (prev act price = ${estData.joblines.data[indexInEstData].act_price})`;
|
|
||||||
estData.joblines.data[indexInEstData].act_price = 0;
|
estData.joblines.data[indexInEstData].act_price = 0;
|
||||||
estData.joblines.data[indexInEstData].db_price = 0;
|
estData.joblines.data[indexInEstData].db_price = 0;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
||||||
|
import axios from "axios";
|
||||||
|
import parsePhoneNumber from "libphonenumber-js";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -8,27 +11,24 @@ import { Link, useNavigate } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
||||||
|
import { GET_CURRENT_QUESTIONSET_ID, INSERT_CSI } from "../../graphql/csi.queries";
|
||||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||||
|
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||||
import axios from "axios";
|
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
|
||||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
|
||||||
import { GET_CURRENT_QUESTIONSET_ID, INSERT_CSI } from "../../graphql/csi.queries";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
|
||||||
import parsePhoneNumber from "libphonenumber-js";
|
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
|
||||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -39,13 +39,57 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })),
|
setScheduleContext: (context) => dispatch(setModalContext({ context: context, modal: "schedule" })),
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
setBillEnterContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "billEnter"
|
||||||
|
})
|
||||||
|
),
|
||||||
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
setPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||||
setJobCostingContext: (context) => dispatch(setModalContext({ context: context, modal: "jobCosting" })),
|
setJobCostingContext: (context) =>
|
||||||
setTimeTicketContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
dispatch(
|
||||||
setCardPaymentContext: (context) => dispatch(setModalContext({ context: context, modal: "cardPayment" })),
|
setModalContext({
|
||||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type })),
|
context: context,
|
||||||
setTimeTicketTaskContext: (context) => dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
|
modal: "jobCosting"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "timeTicket"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setCardPaymentContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "cardPayment"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid,
|
||||||
|
operation,
|
||||||
|
type
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTimeTicketTaskContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "timeTicketTask"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTaskUpsertContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "taskUpsert"
|
||||||
|
})
|
||||||
|
),
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||||
setMessage: (text) => dispatch(setMessage(text))
|
setMessage: (text) => dispatch(setMessage(text))
|
||||||
@@ -67,7 +111,8 @@ export function JobsDetailHeaderActions({
|
|||||||
setEmailOptions,
|
setEmailOptions,
|
||||||
openChatByPhone,
|
openChatByPhone,
|
||||||
setMessage,
|
setMessage,
|
||||||
setTimeTicketTaskContext
|
setTimeTicketTaskContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -596,6 +641,7 @@ export function JobsDetailHeaderActions({
|
|||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
key: "schedule",
|
key: "schedule",
|
||||||
|
id: "job-actions-schedule",
|
||||||
disabled: !jobInPreProduction || !job.converted || jobRO,
|
disabled: !jobInPreProduction || !job.converted || jobRO,
|
||||||
label: t("jobs.actions.schedule"),
|
label: t("jobs.actions.schedule"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -612,6 +658,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "cancelallappointments",
|
key: "cancelallappointments",
|
||||||
|
id: "job-actions-cancelallappointments",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
if (job.status !== bodyshop.md_ro_statuses.default_scheduled) {
|
if (job.status !== bodyshop.md_ro_statuses.default_scheduled) {
|
||||||
return;
|
return;
|
||||||
@@ -625,6 +672,7 @@ export function JobsDetailHeaderActions({
|
|||||||
imex: [
|
imex: [
|
||||||
{
|
{
|
||||||
key: "intake",
|
key: "intake",
|
||||||
|
id: "job-actions-intake",
|
||||||
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
|
disabled: !!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO,
|
||||||
label:
|
label:
|
||||||
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
|
!!job.intakechecklist || !jobInPreProduction || !job.converted || jobRO ? (
|
||||||
@@ -635,6 +683,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "deliver",
|
key: "deliver",
|
||||||
|
id: "job-actions-deliver",
|
||||||
disabled: !jobInProduction || jobRO,
|
disabled: !jobInProduction || jobRO,
|
||||||
label: !jobInProduction ? (
|
label: !jobInProduction ? (
|
||||||
t("jobs.actions.deliver")
|
t("jobs.actions.deliver")
|
||||||
@@ -644,6 +693,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "checklist",
|
key: "checklist",
|
||||||
|
id: "job-actions-checklist",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
label: <Link to={`/manage/jobs/${job.id}/checklist`}>{t("jobs.actions.viewchecklist")}</Link>
|
||||||
}
|
}
|
||||||
@@ -652,6 +702,7 @@ export function JobsDetailHeaderActions({
|
|||||||
promanager: [
|
promanager: [
|
||||||
{
|
{
|
||||||
key: "toggleproduction",
|
key: "toggleproduction",
|
||||||
|
id: "job-actions-toggleproduction",
|
||||||
disabled: !job.converted || jobRO,
|
disabled: !job.converted || jobRO,
|
||||||
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
|
label: <JobsDetailHeaderActionsToggleProduction job={job} refetch={refetch} />
|
||||||
}
|
}
|
||||||
@@ -665,6 +716,7 @@ export function JobsDetailHeaderActions({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "entertimetickets",
|
key: "entertimetickets",
|
||||||
|
id: "job-actions-entertimetickets",
|
||||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||||
label: t("timetickets.actions.enter"),
|
label: t("timetickets.actions.enter"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -688,6 +740,7 @@ export function JobsDetailHeaderActions({
|
|||||||
if (bodyshop.md_tasks_presets.enable_tasks) {
|
if (bodyshop.md_tasks_presets.enable_tasks) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "claimtimetickettasks",
|
key: "claimtimetickettasks",
|
||||||
|
id: "job-actions-claimtimetickettasks",
|
||||||
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
disabled: !job.converted || (!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setTimeTicketTaskContext({
|
setTimeTicketTaskContext({
|
||||||
@@ -701,6 +754,7 @@ export function JobsDetailHeaderActions({
|
|||||||
|
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "enterpayments",
|
key: "enterpayments",
|
||||||
|
id: "job-actions-enterpayments",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("menus.header.enterpayment"),
|
label: t("menus.header.enterpayment"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -716,22 +770,24 @@ export function JobsDetailHeaderActions({
|
|||||||
if (ImEXPay.treatment === "on") {
|
if (ImEXPay.treatment === "on") {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "entercardpayments",
|
key: "entercardpayments",
|
||||||
|
id: "job-actions-entercardpayments",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("menus.header.entercardpayment"),
|
label: t("menus.header.entercardpayment"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
logImEXEvent("job_header_enter_card_payment");
|
logImEXEvent("job_header_enter_card_payment");
|
||||||
|
|
||||||
setCardPaymentContext({
|
setCardPaymentContext({
|
||||||
actions: {},
|
actions: { refetch },
|
||||||
context: { jobid: job.id }
|
context: { jobid: job.id }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "courtesycars" })) {
|
if (HasFeatureAccess({ featureName: "courtesycars", bodyshop })) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "cccontract",
|
key: "cccontract",
|
||||||
|
id: "job-actions-cccontract",
|
||||||
disabled: jobRO || !job.converted,
|
disabled: jobRO || !job.converted,
|
||||||
label: (
|
label: (
|
||||||
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
|
<Link state={{ jobId: job.id }} to="/manage/courtesycars/contracts/new">
|
||||||
@@ -741,16 +797,29 @@ export function JobsDetailHeaderActions({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menuItems.push({
|
||||||
|
key: "createtask",
|
||||||
|
id: "job-actions-createtask",
|
||||||
|
label: t("menus.header.create_task"),
|
||||||
|
onClick: () =>
|
||||||
|
setTaskUpsertContext({
|
||||||
|
actions: {},
|
||||||
|
context: { jobid: job.id }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
menuItems.push(
|
menuItems.push(
|
||||||
job.inproduction
|
job.inproduction
|
||||||
? {
|
? {
|
||||||
key: "addtoproduction",
|
key: "removefromproduction",
|
||||||
|
id: "job-actions-removefromproduction",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("jobs.actions.removefromproduction"),
|
label: t("jobs.actions.removefromproduction"),
|
||||||
onClick: () => AddToProduction(client, job.id, refetch, true)
|
onClick: () => AddToProduction(client, job.id, refetch, true)
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
key: "addtoproduction",
|
key: "addtoproduction",
|
||||||
|
id: "job-actions-addtoproduction",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("jobs.actions.addtoproduction"),
|
label: t("jobs.actions.addtoproduction"),
|
||||||
onClick: () => AddToProduction(client, job.id, refetch)
|
onClick: () => AddToProduction(client, job.id, refetch)
|
||||||
@@ -760,12 +829,14 @@ export function JobsDetailHeaderActions({
|
|||||||
menuItems.push(
|
menuItems.push(
|
||||||
{
|
{
|
||||||
key: "togglesuspend",
|
key: "togglesuspend",
|
||||||
|
id: "job-actions-togglesuspend",
|
||||||
onClick: handleSuspend,
|
onClick: handleSuspend,
|
||||||
label: job.suspended ? t("production.actions.unsuspend") : t("production.actions.suspend")
|
label: job.suspended ? t("production.actions.unsuspend") : t("production.actions.suspend")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "toggleAlert",
|
key: "toggleAlert",
|
||||||
onClick: handleAlertToggle,
|
onClick: handleAlertToggle,
|
||||||
|
id: "job-actions-togglealert",
|
||||||
label:
|
label:
|
||||||
job.production_vars && job.production_vars.alert
|
job.production_vars && job.production_vars.alert
|
||||||
? t("production.labels.alertoff")
|
? t("production.labels.alertoff")
|
||||||
@@ -777,6 +848,7 @@ export function JobsDetailHeaderActions({
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "duplicate",
|
key: "duplicate",
|
||||||
|
id: "job-actions-duplicate",
|
||||||
label: (
|
label: (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("jobs.labels.duplicateconfirm")}
|
title={t("jobs.labels.duplicateconfirm")}
|
||||||
@@ -792,6 +864,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "duplicatenolines",
|
key: "duplicatenolines",
|
||||||
|
id: "job-actions-duplicatenolines",
|
||||||
label: (
|
label: (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("jobs.labels.duplicateconfirm")}
|
title={t("jobs.labels.duplicateconfirm")}
|
||||||
@@ -815,6 +888,7 @@ export function JobsDetailHeaderActions({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "postbills",
|
key: "postbills",
|
||||||
|
id: "job-actions-postbills",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("jobs.actions.postbills"),
|
label: t("jobs.actions.postbills"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -833,6 +907,7 @@ export function JobsDetailHeaderActions({
|
|||||||
|
|
||||||
{
|
{
|
||||||
key: "addtopartsqueue",
|
key: "addtopartsqueue",
|
||||||
|
id: "job-actions-addtopartsqueue",
|
||||||
disabled: !job.converted || !jobInProduction || jobRO,
|
disabled: !job.converted || !jobInProduction || jobRO,
|
||||||
label: t("jobs.actions.addtopartsqueue"),
|
label: t("jobs.actions.addtopartsqueue"),
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
@@ -858,6 +933,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "closejob",
|
key: "closejob",
|
||||||
|
id: "job-actions-closejob",
|
||||||
disabled: !jobInPostProduction,
|
disabled: !jobInPostProduction,
|
||||||
label: !jobInPostProduction ? (
|
label: !jobInPostProduction ? (
|
||||||
t("menus.jobsactions.closejob")
|
t("menus.jobsactions.closejob")
|
||||||
@@ -873,6 +949,7 @@ export function JobsDetailHeaderActions({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "admin",
|
key: "admin",
|
||||||
|
id: "job-actions-admin",
|
||||||
label: (
|
label: (
|
||||||
<Link
|
<Link
|
||||||
to={{
|
to={{
|
||||||
@@ -894,6 +971,7 @@ export function JobsDetailHeaderActions({
|
|||||||
) {
|
) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "exportcustdata",
|
key: "exportcustdata",
|
||||||
|
id: "job-actions-exportcustdata",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("jobs.actions.exportcustdata"),
|
label: t("jobs.actions.exportcustdata"),
|
||||||
onClick: handleExportCustData
|
onClick: handleExportCustData
|
||||||
@@ -904,18 +982,21 @@ export function JobsDetailHeaderActions({
|
|||||||
const children = [
|
const children = [
|
||||||
{
|
{
|
||||||
key: "email",
|
key: "email",
|
||||||
|
id: "job-actions-email",
|
||||||
disabled: !!!job.ownr_ea,
|
disabled: !!!job.ownr_ea,
|
||||||
label: t("general.labels.email"),
|
label: t("general.labels.email"),
|
||||||
onClick: handleCreateCsi
|
onClick: handleCreateCsi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "text",
|
key: "text",
|
||||||
|
id: "job-actions-text",
|
||||||
disabled: !!!job.ownr_ph1,
|
disabled: !!!job.ownr_ph1,
|
||||||
label: t("general.labels.text"),
|
label: t("general.labels.text"),
|
||||||
onClick: handleCreateCsi
|
onClick: handleCreateCsi
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "generate",
|
key: "generate",
|
||||||
|
id: "job-actions-generate",
|
||||||
disabled: job.csiinvites && job.csiinvites.length > 0,
|
disabled: job.csiinvites && job.csiinvites.length > 0,
|
||||||
label: t("jobs.actions.generatecsi"),
|
label: t("jobs.actions.generatecsi"),
|
||||||
onClick: handleCreateCsi
|
onClick: handleCreateCsi
|
||||||
@@ -949,6 +1030,7 @@ export function JobsDetailHeaderActions({
|
|||||||
}
|
}
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "sendcsi",
|
key: "sendcsi",
|
||||||
|
id: "job-actions-sendcsi",
|
||||||
label: t("jobs.actions.sendcsi"),
|
label: t("jobs.actions.sendcsi"),
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
children
|
children
|
||||||
@@ -957,6 +1039,7 @@ export function JobsDetailHeaderActions({
|
|||||||
|
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "jobcosting",
|
key: "jobcosting",
|
||||||
|
id: "job-actions-jobcosting",
|
||||||
disabled: !job.converted,
|
disabled: !job.converted,
|
||||||
label: t("jobs.labels.jobcosting"),
|
label: t("jobs.labels.jobcosting"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -974,6 +1057,7 @@ export function JobsDetailHeaderActions({
|
|||||||
if (job && !job.converted) {
|
if (job && !job.converted) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "deletejob",
|
key: "deletejob",
|
||||||
|
id: "job-actions-deletejob",
|
||||||
label: (
|
label: (
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("jobs.labels.deleteconfirm")}
|
title={t("jobs.labels.deleteconfirm")}
|
||||||
@@ -990,6 +1074,7 @@ export function JobsDetailHeaderActions({
|
|||||||
|
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "manualevent",
|
key: "manualevent",
|
||||||
|
id: "job-actions-manualevent",
|
||||||
onClick: (e) => {
|
onClick: (e) => {
|
||||||
setVisibility(true);
|
setVisibility(true);
|
||||||
},
|
},
|
||||||
@@ -999,6 +1084,7 @@ export function JobsDetailHeaderActions({
|
|||||||
if (!jobRO && job.converted) {
|
if (!jobRO && job.converted) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "voidjob",
|
key: "voidjob",
|
||||||
|
id: "job-actions-voidjob",
|
||||||
label: (
|
label: (
|
||||||
<RbacWrapper action="jobs:void" noauth>
|
<RbacWrapper action="jobs:void" noauth>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
|||||||
}),
|
}),
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status)
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.vehicle"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
|||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
import { setJoyRideSteps } from "../../redux/application/application.actions";
|
import { setJoyRideSteps } from "../../redux/application/application.actions";
|
||||||
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
@@ -176,7 +174,6 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
|||||||
[],
|
[],
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status)
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.vehicle"),
|
title: t("jobs.fields.vehicle"),
|
||||||
dataIndex: "vehicle",
|
dataIndex: "vehicle",
|
||||||
@@ -316,34 +313,6 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
|||||||
title={t("titles.bc.jobs-active")}
|
title={t("titles.bc.jobs-active")}
|
||||||
extra={
|
extra={
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{InstanceRenderManager({
|
|
||||||
promanager: (
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
setJoyRideSteps([
|
|
||||||
{
|
|
||||||
target: "#active-jobs-list",
|
|
||||||
content: "This is where you will see all work coming in and currently here."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
target: "#header-jobs",
|
|
||||||
spotlightClicks: true,
|
|
||||||
disableOverlayClose: true,
|
|
||||||
content:
|
|
||||||
"The jobs menu lets you access additional pages to see more information. You can import new jobs, search all jobs, or manage your current production."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
target: "#header-jobs-available",
|
|
||||||
content: "You can find jobs to import here."
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Start Walk Through
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Button onClick={() => refetch()}>
|
<Button onClick={() => refetch()}>
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Link } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
|
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -75,7 +76,18 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
|||||||
})),
|
})),
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.actual_completion"),
|
||||||
|
dataIndex: "actual_completion",
|
||||||
|
key: "actual_completion",
|
||||||
|
render: (text, record) => (
|
||||||
|
<DateTimeFormatter>{record.actual_completion}</DateTimeFormatter>
|
||||||
|
),
|
||||||
|
sorter: (a, b) => dateSort(a.actual_completion, b.actual_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actual_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_total"),
|
title: t("jobs.fields.clm_total"),
|
||||||
dataIndex: "clm_total",
|
dataIndex: "clm_total",
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
|
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
|
||||||
import { PageHeader } from "@ant-design/pro-layout";
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
|
||||||
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
||||||
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
||||||
@@ -26,6 +27,7 @@ import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/
|
|||||||
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
|
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
|
||||||
import PrintWrapper from "../print-wrapper/print-wrapper.component";
|
import PrintWrapper from "../print-wrapper/print-wrapper.component";
|
||||||
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
|
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
|
||||||
|
import { FaTasks } from "react-icons/fa";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobRO: selectJobReadOnly,
|
jobRO: selectJobReadOnly,
|
||||||
@@ -33,8 +35,21 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
|
setBillEnterContext: (context) =>
|
||||||
setPartsReceiveContext: (context) => dispatch(setModalContext({ context: context, modal: "partsReceive" }))
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "billEnter"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setPartsReceiveContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "partsReceive"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function PartsOrderListTableComponent({
|
export function PartsOrderListTableComponent({
|
||||||
@@ -44,7 +59,8 @@ export function PartsOrderListTableComponent({
|
|||||||
job,
|
job,
|
||||||
billsQuery,
|
billsQuery,
|
||||||
handleOnRowClick,
|
handleOnRowClick,
|
||||||
setPartsReceiveContext
|
setPartsReceiveContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
@@ -66,19 +82,46 @@ export function PartsOrderListTableComponent({
|
|||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {}
|
sortedInfo: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [returnfrombill, setReturnFromBill] = useState();
|
||||||
|
const [billData, setBillData] = useState();
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const selectedpartsorder = search.partsorderid;
|
const selectedpartsorder = search.partsorderid;
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
|
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
|
||||||
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
||||||
|
|
||||||
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
||||||
const { refetch } = billsQuery;
|
const { refetch } = billsQuery;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
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, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space wrap>
|
<Space direction="horizontal" wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
<Button onClick={() => handleOnRowClick(record)}>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (record.returnfrombill) {
|
||||||
|
setReturnFromBill(record.returnfrombill);
|
||||||
|
} else {
|
||||||
|
setReturnFromBill(null);
|
||||||
|
}
|
||||||
|
handleOnRowClick(record);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EyeFilled />
|
<EyeFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -108,7 +151,19 @@ export function PartsOrderListTableComponent({
|
|||||||
>
|
>
|
||||||
{t("parts_orders.actions.receive")}
|
{t("parts_orders.actions.receive")}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
title={t("tasks.buttons.create")}
|
||||||
|
onClick={() => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
context: {
|
||||||
|
jobid: job.id,
|
||||||
|
partsorderid: record.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FaTasks />
|
||||||
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t("parts_orders.labels.confirmdelete")}
|
title={t("parts_orders.labels.confirmdelete")}
|
||||||
disabled={jobRO}
|
disabled={jobRO}
|
||||||
@@ -148,7 +203,7 @@ export function PartsOrderListTableComponent({
|
|||||||
is_credit_memo: record.return,
|
is_credit_memo: record.return,
|
||||||
billlines: record.parts_order_lines.map((pol) => {
|
billlines: record.parts_order_lines.map((pol) => {
|
||||||
return {
|
return {
|
||||||
joblineid: pol.job_line_id,
|
joblineid: pol.job_line_id || "noline",
|
||||||
line_desc: pol.line_desc,
|
line_desc: pol.line_desc,
|
||||||
quantity: pol.quantity,
|
quantity: pol.quantity,
|
||||||
|
|
||||||
@@ -368,7 +423,14 @@ export function PartsOrderListTableComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`} extra={recordActions(record)} />
|
<PageHeader
|
||||||
|
title={
|
||||||
|
billData
|
||||||
|
? `${record.vendor.name} - ${record.order_number} - ${t("bills.labels.returnfrombill")}: ${billData.bills_by_pk.invoice_number}`
|
||||||
|
: `${record.vendor.name} - ${record.order_number}`
|
||||||
|
}
|
||||||
|
extra={recordActions(record)}
|
||||||
|
/>
|
||||||
<Table
|
<Table
|
||||||
scroll={{
|
scroll={{
|
||||||
x: true //y: "50rem"
|
x: true //y: "50rem"
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export function PartsOrderModalComponent({ bodyshop, vendorList, sendTypeState,
|
|||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber min={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("parts_orders.fields.act_price")}
|
label={t("parts_orders.fields.act_price")}
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export function PartsOrderModalContainer({
|
|||||||
vendorid: bodyshop.inhousevendorid,
|
vendorid: bodyshop.inhousevendorid,
|
||||||
invoice_number: "ih",
|
invoice_number: "ih",
|
||||||
isinhouse: true,
|
isinhouse: true,
|
||||||
date: new dayjs(),
|
date: dayjs(),
|
||||||
total: 0,
|
total: 0,
|
||||||
billlines: values.parts_order_lines.data.map((p) => {
|
billlines: values.parts_order_lines.data.map((p) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -119,8 +119,14 @@ const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Descriptions title={t("job_payments.titles.descriptions")} contentStyle={{ fontWeight: "600" }} column={4}>
|
<Descriptions
|
||||||
<Descriptions.Item label={t("job_payments.titles.payer")}>{record.payer}</Descriptions.Item>
|
title={t("job_payments.titles.descriptions")}
|
||||||
|
contentStyle={{ fontWeight: "600" }}
|
||||||
|
column={4}
|
||||||
|
>
|
||||||
|
<Descriptions.Item label={t("job_payments.titles.hint")}>
|
||||||
|
{payment_response?.response?.methodhint}
|
||||||
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.payername")}>
|
<Descriptions.Item label={t("job_payments.titles.payername")}>
|
||||||
{payment_response?.response?.nameOnCard ?? ""}
|
{payment_response?.response?.nameOnCard ?? ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@@ -132,7 +138,7 @@ const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
|||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.transactionid")}>{record.transactionid}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.transactionid")}>{record.transactionid}</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
|
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
|
||||||
{payment_response?.response?.paymentreferenceid ?? ""}
|
{payment_response?.ext_paymentid ?? ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>{record.type}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>{record.type}</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>{record.paymentnum}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>{record.paymentnum}</Descriptions.Item>
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||||
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
|
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
|
||||||
import { selectPayment } from "../../redux/modals/modals.selectors";
|
import { selectPayment } from "../../redux/modals/modals.selectors";
|
||||||
|
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -58,8 +58,10 @@ const PaymentMarkForExportButton = ({ bodyshop, payment, refetch, setPaymentCont
|
|||||||
refetch
|
refetch
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
|
...paymentModal.context,
|
||||||
...paymentModal.context,
|
...paymentModal.context,
|
||||||
...payment,
|
...payment,
|
||||||
|
smartRefetch: true,
|
||||||
exportedat: today
|
exportedat: today
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,32 +1,30 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import {useMutation} from "@apollo/client";
|
||||||
import { Button, Form, Modal, notification, Space } from "antd";
|
import {Button, Form, Modal, notification, Space} from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import { INSERT_NEW_PAYMENT, UPDATE_PAYMENT } from "../../graphql/payments.queries";
|
import {INSERT_NEW_PAYMENT, UPDATE_PAYMENT} from "../../graphql/payments.queries";
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
import {toggleModalVisible} from "../../redux/modals/modals.actions";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import {selectPayment} from "../../redux/modals/modals.selectors";
|
||||||
import { selectPayment } from "../../redux/modals/modals.selectors";
|
import {selectBodyshop} from "../../redux/user/user.selectors";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import {GenerateDocument} from "../../utils/RenderTemplate";
|
||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import {TemplateList} from "../../utils/TemplateConstants";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
|
||||||
import PaymentForm from "../payment-form/payment-form.component";
|
import PaymentForm from "../payment-form/payment-form.component";
|
||||||
import PaymentMarkForExportButton from "../payment-mark-export-button/payment-mark-export-button-component";
|
import PaymentMarkForExportButton
|
||||||
|
from "../payment-mark-export-button/payment-mark-export-button-component";
|
||||||
import PaymentReexportButton from "../payment-reexport-button/payment-reexport-button.component";
|
import PaymentReexportButton from "../payment-reexport-button/payment-reexport-button.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
paymentModal: selectPayment,
|
paymentModal: selectPayment,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
currentUser: selectCurrentUser
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("payment"))
|
toggleModalVisible: () => dispatch(toggleModalVisible("payment"))
|
||||||
});
|
});
|
||||||
|
|
||||||
function PaymentModalContainer({ paymentModal, toggleModalVisible, bodyshop, currentUser, setEmailOptions }) {
|
function PaymentModalContainer({paymentModal, toggleModalVisible, bodyshop }) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [enterAgain, setEnterAgain] = useState(false);
|
const [enterAgain, setEnterAgain] = useState(false);
|
||||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||||
@@ -34,7 +32,6 @@ function PaymentModalContainer({ paymentModal, toggleModalVisible, bodyshop, cur
|
|||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { context, actions, open } = paymentModal;
|
const { context, actions, open } = paymentModal;
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
@@ -84,9 +81,9 @@ function PaymentModalContainer({ paymentModal, toggleModalVisible, bodyshop, cur
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!!!updatedPayment.errors) {
|
if (!!!updatedPayment.errors) {
|
||||||
notification["success"]({ message: t("payments.successes.payment") });
|
notification["success"]({ message: t("payments.successes.paymentupdate") });
|
||||||
} else {
|
} else {
|
||||||
notification["error"]({ message: t("payments.errors.payment") });
|
notification["error"]({ message: t("payments.errors.paymentupdate") });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { Button, notification } from "antd";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
|
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectPayment } from "../../redux/modals/modals.selectors";
|
import { selectPayment } from "../../redux/modals/modals.selectors";
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
paymentModal: selectPayment
|
paymentModal: selectPayment
|
||||||
@@ -40,6 +40,7 @@ const PaymentReexportButton = ({ paymentModal, payment, refetch, setPaymentConte
|
|||||||
refetch
|
refetch
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
|
...paymentModal.context,
|
||||||
...paymentModal.context,
|
...paymentModal.context,
|
||||||
...payment,
|
...payment,
|
||||||
exportedat: null
|
exportedat: null
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
|
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
import { pageLimit } from "../../utils/config";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
import { Button, Card, Form, InputNumber, notification, Popover, Radio } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
notification,
|
||||||
|
Popover,
|
||||||
|
Radio,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -95,10 +104,16 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
|
|||||||
>
|
>
|
||||||
<InputNumber min={1} precision={0} max={99} />
|
<InputNumber min={1} precision={0} max={99} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Button type="primary" loading={loading} onClick={handleOk}>
|
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||||
{t("general.actions.print")}
|
<Space>
|
||||||
</Button>
|
<Button type="primary" loading={loading} onClick={handleOk}>
|
||||||
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
|
{t("general.actions.print")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCancel}>
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,23 +2,24 @@ import { useLazyQuery } from "@apollo/client";
|
|||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography } from "antd";
|
import { Button, Card, Col, DatePicker, Form, Input, Radio, Row, Typography } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
|
import { QUERY_ACTIVE_EMPLOYEES, QUERY_ACTIVE_EMPLOYEES_WITH_EMAIL } from "../../graphql/employees.queries";
|
||||||
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
||||||
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
import { selectReportCenter } from "../../redux/modals/modals.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import DatePickerRanges from "../../utils/DatePickerRanges";
|
import DatePickerRanges from "../../utils/DatePickerRanges";
|
||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
import EmployeeSearchSelectEmail from "../employee-search-select/employee-search-select-email.component";
|
||||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
|
||||||
import "./report-center-modal.styles.scss";
|
|
||||||
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
|
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||||
|
import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component";
|
||||||
|
import "./report-center-modal.styles.scss";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
reportCenterModal: selectReportCenter,
|
reportCenterModal: selectReportCenter,
|
||||||
@@ -66,6 +67,13 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
skip: !(open && Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")].idtype)
|
skip: !(open && Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")].idtype)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [callEmployeeWithEmailQuery, { data: employeeWithEmailData, called: employeeWithEmailCalled }] = useLazyQuery(
|
||||||
|
QUERY_ACTIVE_EMPLOYEES_WITH_EMAIL,
|
||||||
|
{
|
||||||
|
skip: !(open && Templates[form.getFieldValue("key")] && Templates[form.getFieldValue("key")].idtype)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const start = values.dates ? values.dates[0] : null;
|
const start = values.dates ? values.dates[0] : null;
|
||||||
@@ -197,6 +205,7 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
}
|
}
|
||||||
if (!vendorCalled && idtype === "vendor") callVendorQuery();
|
if (!vendorCalled && idtype === "vendor") callVendorQuery();
|
||||||
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
|
if (!employeeCalled && idtype === "employee") callEmployeeQuery();
|
||||||
|
if (!employeeWithEmailCalled && idtype === "employeeWithEmail") callEmployeeWithEmailQuery();
|
||||||
if (idtype === "vendor")
|
if (idtype === "vendor")
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -227,6 +236,22 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
<EmployeeSearchSelect options={employeeData ? employeeData.employees : []} />
|
<EmployeeSearchSelect options={employeeData ? employeeData.employees : []} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
|
//This was introduced with tasks before assigned_to was shifted to UUID. Keeping in place for reference in the future if needed.
|
||||||
|
if (idtype === "employeeWithEmail")
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
name="id"
|
||||||
|
label={t("reportcenter.labels.employee")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<EmployeeSearchSelectEmail options={employeeWithEmailData ? employeeWithEmailData.employees : []} />
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
else return null;
|
else return null;
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -59,25 +59,33 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
{loadData && loadData.allJobsOut ? (
|
{loadData && loadData.allJobsOut ? (
|
||||||
loadData.allJobsOut.map((j) => (
|
loadData.allJobsOut.map((j) => (
|
||||||
<tr key={j.id}>
|
<tr key={j.id}>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link> (
|
||||||
|
{j.status})
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<OwnerNameDisplay ownerObject={j} />
|
<OwnerNameDisplay ownerObject={j} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
{`(${(j.labhrs.aggregate.sum.mod_lb_hrs + j.larhrs.aggregate.sum.mod_lb_hrs).toFixed(
|
{`(${j.labhrs?.aggregate?.sum?.mod_lb_hrs?.toFixed(1) || 0}/${
|
||||||
1
|
j.larhrs?.aggregate?.sum?.mod_lb_hrs?.toFixed(1) || 0
|
||||||
)} ${t("general.labels.hours")})`}
|
}/${(
|
||||||
|
j.labhrs.aggregate?.sum?.mod_lb_hrs +
|
||||||
|
j.larhrs.aggregate?.sum?.mod_lb_hrs
|
||||||
|
).toFixed(1)} ${t("general.labels.hours")})`}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<DateTimeFormatter>{j.scheduled_completion}</DateTimeFormatter>
|
<DateTimeFormatter>
|
||||||
|
{j.scheduled_completion}
|
||||||
|
</DateTimeFormatter>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{t("appointments.labels.nocompletingjobs")}</td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
|
{t("appointments.labels.nocompletingjobs")}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -92,26 +100,30 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
{loadData && loadData.allJobsIn ? (
|
{loadData && loadData.allJobsIn ? (
|
||||||
loadData.allJobsIn.map((j) => (
|
loadData.allJobsIn.map((j) => (
|
||||||
<tr key={j.id}>
|
<tr key={j.id}>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||||
{j.status}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<OwnerNameDisplay ownerObject={j} />
|
<OwnerNameDisplay ownerObject={j} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
{`(${(j.labhrs.aggregate.sum.mod_lb_hrs + j.larhrs.aggregate.sum.mod_lb_hrs).toFixed(
|
{`(${j.labhrs?.aggregate?.sum.mod_lb_hrs?.toFixed(1) || 0}/${
|
||||||
1
|
j.larhrs?.aggregate?.sum?.mod_lb_hrs?.toFixed(1) || 0
|
||||||
)} ${t("general.labels.hours")})`}
|
}/${(
|
||||||
|
j.labhrs?.aggregate?.sum?.mod_lb_hrs +
|
||||||
|
j.larhrs?.aggregate?.sum?.mod_lb_hrs
|
||||||
|
).toFixed(1)} ${t("general.labels.hours")})`}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
<DateTimeFormatter>{j.scheduled_in}</DateTimeFormatter>
|
<DateTimeFormatter>{j.scheduled_in}</DateTimeFormatter>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td>{t("appointments.labels.noarrivingjobs")}</td>
|
<td style={{ padding: "2.5px" }}>
|
||||||
|
{t("appointments.labels.noarrivingjobs")}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -121,27 +133,33 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
|
|
||||||
const LoadComponent = loadData ? (
|
const LoadComponent = loadData ? (
|
||||||
<div>
|
<div>
|
||||||
<Space align="center">
|
<Space align="center">
|
||||||
<Popover
|
<Popover
|
||||||
placement={"bottom"}
|
placement={"bottom"}
|
||||||
content={jobsInPopup}
|
content={jobsInPopup}
|
||||||
trigger="hover"
|
trigger="hover"
|
||||||
title={t("appointments.labels.arrivingjobs")}
|
title={t("appointments.labels.arrivingjobs")}
|
||||||
>
|
>
|
||||||
<Icon component={MdFileDownload} style={{ color: "green" }} />
|
<Icon component={MdFileDownload} style={{ color: "green" }} />
|
||||||
{(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(2)}
|
{(loadData.allHoursInBody || 0) &&
|
||||||
</Popover>
|
loadData.allHoursInBody.toFixed(1)}
|
||||||
<Popover
|
/
|
||||||
placement={"bottom"}
|
{(loadData.allHoursInRefinish || 0) &&
|
||||||
content={jobsOutPopup}
|
loadData.allHoursInRefinish.toFixed(1)}
|
||||||
trigger="hover"
|
/{(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(1)}
|
||||||
title={t("appointments.labels.completingjobs")}
|
</Popover>
|
||||||
>
|
<Popover
|
||||||
<Icon component={MdFileUpload} style={{ color: "red" }} />
|
placement={"bottom"}
|
||||||
{(loadData.allHoursOut || 0) && loadData.allHoursOut.toFixed(2)}
|
content={jobsOutPopup}
|
||||||
</Popover>
|
trigger="hover"
|
||||||
<ScheduleCalendarHeaderGraph loadData={loadData} />
|
title={t("appointments.labels.completingjobs")}
|
||||||
</Space>
|
>
|
||||||
|
<Icon component={MdFileUpload} style={{ color: "red" }} />
|
||||||
|
{(loadData.allHoursOut || 0) && loadData.allHoursOut.toFixed(1)}
|
||||||
|
</Popover>
|
||||||
|
<ScheduleCalendarHeaderGraph loadData={loadData} />
|
||||||
|
</Space>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<ul style={{ listStyleType: "none", columns: "2 auto", padding: 0 }}>
|
<ul style={{ listStyleType: "none", columns: "2 auto", padding: 0 }}>
|
||||||
{Object.keys(ATSToday).map((key, idx) => (
|
{Object.keys(ATSToday).map((key, idx) => (
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { useSplitTreatments } from '@splitsoftware/splitio-react';
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Tabs } from 'antd';
|
import { Button, Card, Tabs } from "antd";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ShopInfoGeneral from './shop-info.general.component';
|
import ShopInfoGeneral from "./shop-info.general.component";
|
||||||
import ShopInfoIntakeChecklistComponent from './shop-info.intake.component';
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from './shop-info.laborrates.component';
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
import ShopInfoOrderStatusComponent from './shop-info.orderstatus.component';
|
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||||
import ShopInfoPartsScan from './shop-info.parts-scan';
|
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||||
import ShopInfoRbacComponent from './shop-info.rbac.component';
|
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||||
import ShopInfoResponsibilityCenterComponent from './shop-info.responsibilitycenters.component';
|
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||||
import ShopInfoROStatusComponent from './shop-info.rostatus.component';
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
import ShopInfoSchedulingComponent from './shop-info.scheduling.component';
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from './shop-info.speedprint.component';
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import ShopInfoTaskPresets from './shop-info.task-presets.component';
|
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
||||||
import queryString from 'query-string';
|
import queryString from "query-string";
|
||||||
import InstanceRenderManager from '../../utils/instanceRenderMgr';
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import ShopInfoRoGuard from './shop-info.roguard.component';
|
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -47,44 +47,52 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "general",
|
key: "general",
|
||||||
label: t("bodyshop.labels.shopinfo"),
|
label: t("bodyshop.labels.shopinfo"),
|
||||||
children: <ShopInfoGeneral form={form} />
|
children: <ShopInfoGeneral form={form} />,
|
||||||
|
id: "tab-shop-general"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "speedprint",
|
key: "speedprint",
|
||||||
label: t("bodyshop.labels.speedprint"),
|
label: t("bodyshop.labels.speedprint"),
|
||||||
children: <ShopInfoSpeedPrint form={form} />
|
children: <ShopInfoSpeedPrint form={form} />,
|
||||||
|
id: "tab-shop-speedprint"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "rbac",
|
key: "rbac",
|
||||||
label: t("bodyshop.labels.rbac"),
|
label: t("bodyshop.labels.rbac"),
|
||||||
children: <ShopInfoRbacComponent form={form} />
|
children: <ShopInfoRbacComponent form={form} />,
|
||||||
|
id: "tab-shop-rbac"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "roStatus",
|
key: "roStatus",
|
||||||
label: t("bodyshop.labels.jobstatuses"),
|
label: t("bodyshop.labels.jobstatuses"),
|
||||||
children: <ShopInfoROStatusComponent form={form} />
|
children: <ShopInfoROStatusComponent form={form} />,
|
||||||
|
id: "tab-shop-rostatus"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "scheduling",
|
key: "scheduling",
|
||||||
label: t("bodyshop.labels.scheduling"),
|
label: t("bodyshop.labels.scheduling"),
|
||||||
children: <ShopInfoSchedulingComponent form={form} />
|
children: <ShopInfoSchedulingComponent form={form} />,
|
||||||
|
id: "tab-shop-scheduling"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "orderStatus",
|
key: "orderStatus",
|
||||||
label: t("bodyshop.labels.orderstatuses"),
|
label: t("bodyshop.labels.orderstatuses"),
|
||||||
children: <ShopInfoOrderStatusComponent form={form} />
|
children: <ShopInfoOrderStatusComponent form={form} />,
|
||||||
|
id: "tab-shop-orderstatus"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "responsibilityCenters",
|
key: "responsibilityCenters",
|
||||||
label: t("bodyshop.labels.responsibilitycenters.title"),
|
label: t("bodyshop.labels.responsibilitycenters.title"),
|
||||||
children: <ShopInfoResponsibilityCenterComponent form={form} />
|
children: <ShopInfoResponsibilityCenterComponent form={form} />,
|
||||||
|
id: "tab-shop-responsibilitycenters"
|
||||||
},
|
},
|
||||||
...InstanceRenderManager({
|
...InstanceRenderManager({
|
||||||
imex: [
|
imex: [
|
||||||
{
|
{
|
||||||
key: "checklists",
|
key: "checklists",
|
||||||
label: t("bodyshop.labels.checklists"),
|
label: t("bodyshop.labels.checklists"),
|
||||||
children: <ShopInfoIntakeChecklistComponent form={form} />
|
children: <ShopInfoIntakeChecklistComponent form={form} />,
|
||||||
|
id: "tab-shop-checklists"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rome: "USE_IMEX",
|
rome: "USE_IMEX",
|
||||||
@@ -93,14 +101,16 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "laborrates",
|
key: "laborrates",
|
||||||
label: t("bodyshop.labels.laborrates"),
|
label: t("bodyshop.labels.laborrates"),
|
||||||
children: <ShopInfoLaborRates form={form} />
|
children: <ShopInfoLaborRates form={form} />,
|
||||||
|
id: "tab-shop-laborrates"
|
||||||
},
|
},
|
||||||
...(CriticalPartsScanning.treatment === "on"
|
...(CriticalPartsScanning.treatment === "on"
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "partsscan",
|
key: "partsscan",
|
||||||
label: t("bodyshop.labels.partsscan"),
|
label: t("bodyshop.labels.partsscan"),
|
||||||
children: <ShopInfoPartsScan form={form} />
|
children: <ShopInfoPartsScan form={form} />,
|
||||||
|
id: "tab-shop-partsscan"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@@ -109,21 +119,23 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "task-presets",
|
key: "task-presets",
|
||||||
label: t("bodyshop.labels.task-presets"),
|
label: t("bodyshop.labels.task-presets"),
|
||||||
children: <ShopInfoTaskPresets form={form} />
|
children: <ShopInfoTaskPresets form={form} />,
|
||||||
|
id: "tab-shop-task-presets"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...InstanceRenderManager({
|
...InstanceRenderManager({
|
||||||
imex: [
|
imex: [
|
||||||
{
|
{
|
||||||
key: 'roguard',
|
key: "roguard",
|
||||||
label: t('bodyshop.labels.roguard.title'),
|
label: t("bodyshop.labels.roguard.title"),
|
||||||
children: <ShopInfoRoGuard form={form} />,
|
children: <ShopInfoRoGuard form={form} />,
|
||||||
},
|
id: "tab-shop-roguard"
|
||||||
],
|
}
|
||||||
rome: 'USE_IMEX',
|
],
|
||||||
promanager: [],
|
rome: "USE_IMEX",
|
||||||
}),
|
promanager: []
|
||||||
|
})
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-forma
|
|||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import FeatureWrapper, { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
// TODO: Client Update, this might break
|
// TODO: Client Update, this might break
|
||||||
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -144,285 +145,289 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
<FeatureWrapper featureName="export" noauth={() => null}>
|
||||||
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
||||||
<Switch />
|
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
||||||
</Form.Item>
|
<Switch />
|
||||||
{InstanceRenderManager({
|
</Form.Item>
|
||||||
imex: (
|
{InstanceRenderManager({
|
||||||
<Form.Item shouldUpdate noStyle>
|
imex: (
|
||||||
{() => (
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.qbo_usa")}
|
||||||
|
shouldUpdate
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["accountingconfig", "qbo_usa"]}
|
||||||
|
>
|
||||||
|
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.accountingtiers")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "tiers"]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value={2}>2</Radio>
|
||||||
|
<Radio value={3}>3</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item shouldUpdate>
|
||||||
|
{() => {
|
||||||
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.qbo_usa")}
|
label={t("bodyshop.labels.2tiersetup")}
|
||||||
shouldUpdate
|
shouldUpdate
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "qbo_usa"]}
|
{
|
||||||
|
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "twotierpref"]}
|
||||||
>
|
>
|
||||||
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
||||||
|
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||||
|
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
);
|
||||||
</Form.Item>
|
}}
|
||||||
)
|
</Form.Item>
|
||||||
})}
|
<Form.Item
|
||||||
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
label={t("bodyshop.labels.printlater")}
|
||||||
<Input />
|
valuePropName="checked"
|
||||||
</Form.Item>
|
name={["accountingconfig", "printlater"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.labels.accountingtiers")}
|
<Switch />
|
||||||
rules={[
|
</Form.Item>
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.labels.emaillater")}
|
||||||
//message: t("general.validation.required"),
|
valuePropName="checked"
|
||||||
}
|
name={["accountingconfig", "emaillater"]}
|
||||||
]}
|
>
|
||||||
name={["accountingconfig", "tiers"]}
|
<Switch />
|
||||||
>
|
</Form.Item>
|
||||||
<Radio.Group>
|
<Form.Item
|
||||||
<Radio value={2}>2</Radio>
|
label={t("bodyshop.fields.inhousevendorid")}
|
||||||
<Radio value={3}>3</Radio>
|
name={"inhousevendorid"}
|
||||||
</Radio.Group>
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item shouldUpdate>
|
required: true
|
||||||
{() => {
|
//message: t("general.validation.required"),
|
||||||
return (
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.default_adjustment_rate")}
|
||||||
|
name={"default_adjustment_rate"}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
|
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.2tiersetup")}
|
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||||
shouldUpdate
|
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
name={["accountingconfig", "twotierpref"]}
|
|
||||||
>
|
>
|
||||||
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
<InputNumber />
|
||||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
|
||||||
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
)
|
||||||
}}
|
})}
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
<Form.Item
|
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||||
label={t("bodyshop.labels.printlater")}
|
name={["bill_tax_rates", "state_tax_rate"]}
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "printlater"]}
|
{
|
||||||
>
|
required: true
|
||||||
<Switch />
|
//message: t("general.validation.required"),
|
||||||
</Form.Item>
|
}
|
||||||
<Form.Item
|
]}
|
||||||
label={t("bodyshop.labels.emaillater")}
|
>
|
||||||
valuePropName="checked"
|
<InputNumber />
|
||||||
name={["accountingconfig", "emaillater"]}
|
</Form.Item>
|
||||||
>
|
<Form.Item
|
||||||
<Switch />
|
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||||
</Form.Item>
|
name={["bill_tax_rates", "local_tax_rate"]}
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("bodyshop.fields.inhousevendorid")}
|
{
|
||||||
name={"inhousevendorid"}
|
required: true
|
||||||
rules={[
|
//message: t("general.validation.required"),
|
||||||
{
|
}
|
||||||
required: true
|
]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
}
|
<InputNumber />
|
||||||
]}
|
</Form.Item>
|
||||||
>
|
<Form.Item
|
||||||
<Input />
|
name={["md_payment_types"]}
|
||||||
</Form.Item>
|
label={t("bodyshop.fields.md_payment_types")}
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("bodyshop.fields.default_adjustment_rate")}
|
{
|
||||||
name={"default_adjustment_rate"}
|
required: true,
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} precision={2} />
|
|
||||||
</Form.Item>
|
|
||||||
{InstanceRenderManager({
|
|
||||||
imex: (
|
|
||||||
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
{InstanceRenderManager({
|
|
||||||
imex: (
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "state_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "local_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_payment_types"]}
|
|
||||||
label={t("bodyshop.fields.md_payment_types")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_categories"]}
|
|
||||||
label={t("bodyshop.fields.md_categories")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField1"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField2"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField3"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_classes"]}
|
|
||||||
label={t("bodyshop.fields.md_classes")}
|
|
||||||
rules={[
|
|
||||||
({ getFieldValue }) => {
|
|
||||||
return {
|
|
||||||
required: getFieldValue("enforce_class"),
|
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
type: "array"
|
type: "array"
|
||||||
};
|
}
|
||||||
}
|
]}
|
||||||
]}
|
>
|
||||||
>
|
<Select mode="tags" />
|
||||||
<Select mode="tags" />
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
{ClosingPeriod.treatment === "on" && (
|
name={["md_categories"]}
|
||||||
<>
|
label={t("bodyshop.fields.md_categories")}
|
||||||
<Form.Item
|
rules={[
|
||||||
name={["accountingconfig", "ClosingPeriod"]}
|
{
|
||||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
//message: t("general.validation.required"),
|
||||||
>
|
type: "array"
|
||||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
}
|
||||||
</Form.Item>
|
]}
|
||||||
</>
|
>
|
||||||
)}
|
<Select mode="tags" />
|
||||||
</LayoutFormRow>
|
</Form.Item>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
||||||
<Form.Item
|
<Switch />
|
||||||
label={t("bodyshop.fields.dailypainttarget")}
|
</Form.Item>
|
||||||
name={["scoreboard_target", "dailyPaintTarget"]}
|
<Form.Item
|
||||||
rules={[
|
name={["accountingconfig", "ReceivableCustomField1"]}
|
||||||
{
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
||||||
required: true
|
>
|
||||||
//message: t("general.validation.required"),
|
{ReceivableCustomFieldSelect}
|
||||||
}
|
</Form.Item>
|
||||||
]}
|
<Form.Item
|
||||||
>
|
name={["accountingconfig", "ReceivableCustomField2"]}
|
||||||
<InputNumber min={0} precision={0} />
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
||||||
</Form.Item>
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ReceivableCustomField3"]}
|
||||||
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
||||||
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_classes"]}
|
||||||
|
label={t("bodyshop.fields.md_classes")}
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => {
|
||||||
|
return {
|
||||||
|
required: getFieldValue("enforce_class"),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
|
{ClosingPeriod.treatment === "on" && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ClosingPeriod"]}
|
||||||
|
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||||
|
>
|
||||||
|
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
</FeatureWrapper>
|
||||||
|
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
||||||
|
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.dailypainttarget")}
|
||||||
|
name={["scoreboard_target", "dailyPaintTarget"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={0} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.dailybodytarget")}
|
label={t("bodyshop.fields.dailybodytarget")}
|
||||||
name={["scoreboard_target", "dailyBodyTarget"]}
|
name={["scoreboard_target", "dailyBodyTarget"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} precision={0} />
|
<InputNumber min={0} precision={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.lastnumberworkingdays")}
|
label={t("bodyshop.fields.lastnumberworkingdays")}
|
||||||
name={["scoreboard_target", "lastNumberWorkingDays"]}
|
name={["scoreboard_target", "lastNumberWorkingDays"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={12} precision={0} />
|
<InputNumber min={0} max={12} precision={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.ignoreblockeddays")}
|
label={t("bodyshop.fields.ignoreblockeddays")}
|
||||||
name={["scoreboard_target", "ignoreblockeddays"]}
|
name={["scoreboard_target", "ignoreblockeddays"]}
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.prodtargethrs")}
|
label={t("bodyshop.fields.prodtargethrs")}
|
||||||
name={["prodtargethrs"]}
|
name={["prodtargethrs"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={1} precision={1} />
|
<InputNumber min={1} precision={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
</FeatureWrapper>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={["md_referral_sources"]}
|
name={["md_referral_sources"]}
|
||||||
@@ -567,27 +572,32 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<Select mode="tags" />
|
<Select mode="tags" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
name={["tt_allow_post_to_invoiced"]}
|
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
<>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["tt_allow_post_to_invoiced"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
<Form.Item
|
>
|
||||||
name={["tt_enforce_hours_for_tech_console"]}
|
<Switch />
|
||||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
</Form.Item>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["tt_enforce_hours_for_tech_console"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
<Form.Item
|
>
|
||||||
name={["bill_allow_post_to_closed"]}
|
<Switch />
|
||||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
</Form.Item>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["bill_allow_post_to_closed"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={["md_ded_notes"]}
|
name={["md_ded_notes"]}
|
||||||
label={t("bodyshop.fields.md_ded_notes")}
|
label={t("bodyshop.fields.md_ded_notes")}
|
||||||
@@ -1042,111 +1052,118 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
|
<FeatureWrapper featureName="courtesycars" noauth={() => null}>
|
||||||
<Form.List name={["md_ccc_rates"]}>
|
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
|
||||||
{(fields, { add, remove, move }) => {
|
<Form.List name={["md_ccc_rates"]}>
|
||||||
return (
|
{(fields, { add, remove, move }) => {
|
||||||
<div>
|
return (
|
||||||
{fields.map((field, index) => (
|
<div>
|
||||||
<Form.Item key={field.key}>
|
{fields.map((field, index) => (
|
||||||
<LayoutFormRow noDivider>
|
<Form.Item key={field.key}>
|
||||||
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
<LayoutFormRow noDivider>
|
||||||
<Input />
|
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
||||||
</Form.Item>
|
<Input />
|
||||||
<Form.Item label={t("contracts.fields.actax")} key={`${index}actax`} name={[field.name, "actax"]}>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.actax")}
|
||||||
<Form.Item
|
key={`${index}actax`}
|
||||||
label={t("contracts.fields.dailyfreekm")}
|
name={[field.name, "actax"]}
|
||||||
key={`${index}dailyfreekm`}
|
>
|
||||||
name={[field.name, "dailyfreekm"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.dailyfreekm")}
|
||||||
<Form.Item
|
key={`${index}dailyfreekm`}
|
||||||
label={t("contracts.fields.refuelcharge")}
|
name={[field.name, "dailyfreekm"]}
|
||||||
key={`${index}refuelcharge`}
|
>
|
||||||
name={[field.name, "refuelcharge"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.refuelcharge")}
|
||||||
<Form.Item
|
key={`${index}refuelcharge`}
|
||||||
label={t("contracts.fields.excesskmrate")}
|
name={[field.name, "refuelcharge"]}
|
||||||
key={`${index}excesskmrate`}
|
>
|
||||||
name={[field.name, "excesskmrate"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.excesskmrate")}
|
||||||
<Form.Item
|
key={`${index}excesskmrate`}
|
||||||
label={t("contracts.fields.cleanupcharge")}
|
name={[field.name, "excesskmrate"]}
|
||||||
key={`${index}cleanupcharge`}
|
>
|
||||||
name={[field.name, "cleanupcharge"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.cleanupcharge")}
|
||||||
<Form.Item
|
key={`${index}cleanupcharge`}
|
||||||
label={t("contracts.fields.damagewaiver")}
|
name={[field.name, "cleanupcharge"]}
|
||||||
key={`${index}damagewaiver`}
|
>
|
||||||
name={[field.name, "damagewaiver"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.damagewaiver")}
|
||||||
<Form.Item
|
key={`${index}damagewaiver`}
|
||||||
label={t("contracts.fields.federaltax")}
|
name={[field.name, "damagewaiver"]}
|
||||||
key={`${index}federaltax`}
|
>
|
||||||
name={[field.name, "federaltax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.federaltax")}
|
||||||
<Form.Item
|
key={`${index}federaltax`}
|
||||||
label={t("contracts.fields.statetax")}
|
name={[field.name, "federaltax"]}
|
||||||
key={`${index}statetax`}
|
>
|
||||||
name={[field.name, "statetax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.statetax")}
|
||||||
<Form.Item
|
key={`${index}statetax`}
|
||||||
label={t("contracts.fields.localtax")}
|
name={[field.name, "statetax"]}
|
||||||
key={`${index}localtax`}
|
>
|
||||||
name={[field.name, "localtax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.localtax")}
|
||||||
<Form.Item
|
key={`${index}localtax`}
|
||||||
label={t("contracts.fields.coverage")}
|
name={[field.name, "localtax"]}
|
||||||
key={`${index}coverage`}
|
>
|
||||||
name={[field.name, "coverage"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.coverage")}
|
||||||
|
key={`${index}coverage`}
|
||||||
|
name={[field.name, "coverage"]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormListMoveArrows move={move} index={index} total={fields.length} />
|
<FormListMoveArrows move={move} index={index} total={fields.length} />
|
||||||
</Space>
|
</Space>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
</div>
|
||||||
<Form.Item>
|
);
|
||||||
<Button
|
}}
|
||||||
type="dashed"
|
</Form.List>
|
||||||
onClick={() => {
|
</LayoutFormRow>
|
||||||
add();
|
</FeatureWrapper>
|
||||||
}}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
>
|
|
||||||
{t("general.actions.add")}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
|
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
|
||||||
<Form.List name={["md_jobline_presets"]}>
|
<Form.List name={["md_jobline_presets"]}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Form, InputNumber } from "antd";
|
import { Form, InputNumber } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -29,210 +30,219 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
return (
|
return (
|
||||||
<RbacWrapper action="shop:rbac">
|
<RbacWrapper action="shop:rbac">
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item
|
{...HasFeatureAccess({ featureName: "export", bodyshop }) ? [
|
||||||
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:exportlog"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:exportlog"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.payables")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.payables")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:payables"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:payables"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.payments")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.payments")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:payments"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:payments"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.receivables")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.receivables")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:receivables"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:receivables"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.bills.delete")}
|
]:[]}
|
||||||
rules={[
|
{...HasFeatureAccess({ featureName: "bills", bodyshop }) ? [
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.delete")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:delete"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:delete"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:enter"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:enter"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.list")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.list")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:list"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:list"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.reexport")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.reexport")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:reexport"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:reexport"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.view")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.contracts.create")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>
|
||||||
{
|
]:[]}
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop }) ? [
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.create")}
|
||||||
name={["md_rbac", "contracts:create"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.contracts.detail")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:create"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.detail")}
|
||||||
name={["md_rbac", "contracts:detail"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.contracts.list")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:detail"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.list")}
|
||||||
name={["md_rbac", "contracts:list"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:list"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
||||||
name={["md_rbac", "courtesycar:create"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:create"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
||||||
name={["md_rbac", "courtesycar:detail"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:detail"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
||||||
name={["md_rbac", "courtesycar:list"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.csi.export")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:list"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>
|
||||||
}
|
]:[]}
|
||||||
]}
|
{...HasFeatureAccess({ featureName: "csi", bodyshop }) ? [
|
||||||
name={["md_rbac", "csi:export"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.rbac.csi.export")}
|
||||||
<InputNumber />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item
|
required: true
|
||||||
label={t("bodyshop.fields.rbac.csi.page")}
|
//message: t("general.validation.required"),
|
||||||
rules={[
|
}
|
||||||
{
|
]}
|
||||||
required: true
|
name={["md_rbac", "csi:export"]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
}
|
<InputNumber />
|
||||||
]}
|
</Form.Item>,
|
||||||
name={["md_rbac", "csi:page"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.rbac.csi.page")}
|
||||||
<InputNumber />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "csi:page"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
]:[]}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -425,18 +435,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.list")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:list"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -510,18 +508,21 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "visualboard", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.production.board")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.production.board")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "production:board"]}
|
]}
|
||||||
>
|
name={["md_rbac", "production:board"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.production.list")}
|
label={t("bodyshop.fields.rbac.production.list")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -546,102 +547,142 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "scoreboard", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.scoreboard.view")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.scoreboard.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "scoreboard:view"]}
|
]}
|
||||||
>
|
name={["md_rbac", "scoreboard:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
)}
|
||||||
rules={[
|
{...HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? [
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "shiftclock:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "shiftclock:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.shop.config")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.shop.config")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "shop:config"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "shop:config"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:edit"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:editcommitted"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:editcommitted"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "ttapprovals:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "ttapprovals:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "ttapprovals:approve"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "ttapprovals:approve"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:enter"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.list")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:list"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
]:[]}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.vendors")}
|
label={t("bodyshop.fields.rbac.shop.vendors")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -690,7 +731,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{/* <Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.templates")}
|
label={t("bodyshop.fields.rbac.shop.templates")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@@ -701,79 +742,22 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
name={["md_rbac", "shop:templates"]}
|
name={["md_rbac", "shop:templates"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item> */}
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "media", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.shop.vendors")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "shop:vendors"]}
|
]}
|
||||||
>
|
name={["md_rbac", "temporarydocs:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
)}
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "temporarydocs:view"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:enter"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.list")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:list"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.users.editaccess")}
|
label={t("bodyshop.fields.rbac.users.editaccess")}
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
|||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LayoutFormRow>
|
<Space size='large' wrap>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item key={field.key}>
|
<Form.Item key={field.key}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
@@ -386,7 +386,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
|||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
</LayoutFormRow>
|
</Space>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Result } from "antd";
|
import { Result } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -16,19 +16,5 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopSubStatus);
|
|||||||
export function ShopSubStatus({ bodyshop }) {
|
export function ShopSubStatus({ bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sub_status } = bodyshop;
|
const { sub_status } = bodyshop;
|
||||||
return (
|
return <Result status="403" title={t(`general.labels.sub_status.${sub_status}`)} />;
|
||||||
<Result
|
|
||||||
status="403"
|
|
||||||
title={t(`general.labels.sub_status.${sub_status}`)}
|
|
||||||
extra={
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
alert("Not implemented yet.");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("general.actions.submitticket")}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
382
client/src/components/task-list/task-list.component.jsx
Normal file
382
client/src/components/task-list/task-list.component.jsx
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
import { Button, Card, Space, Switch, Table } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useCallback, useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { pageLimit } from "../../utils/config";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
import {
|
||||||
|
CheckCircleFilled,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
DeleteFilled,
|
||||||
|
DeleteOutlined,
|
||||||
|
EditFilled,
|
||||||
|
ExclamationCircleFilled,
|
||||||
|
PlusCircleFilled,
|
||||||
|
SyncOutlined
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter.jsx";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task List Component
|
||||||
|
* @param dueDate
|
||||||
|
* @returns {Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const DueDateRecord = ({ dueDate }) => {
|
||||||
|
if (!dueDate) return <></>;
|
||||||
|
|
||||||
|
const dueDateDayjs = dayjs(dueDate);
|
||||||
|
const relativeDueDate = dueDateDayjs.fromNow();
|
||||||
|
const isBeforeToday = dueDateDayjs.isBefore(dayjs());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div title={relativeDueDate} style={isBeforeToday ? { color: "red" } : {}}>
|
||||||
|
<DateFormatter>{dueDate}</DateFormatter>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RemindAtRecord = ({ remindAt }) => {
|
||||||
|
if (!remindAt) return <></>;
|
||||||
|
|
||||||
|
const remindAtDayjs = dayjs(remindAt);
|
||||||
|
const relativeRemindAtDate = remindAtDayjs.fromNow();
|
||||||
|
const isBeforeToday = remindAtDayjs.isBefore(dayjs());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div title={relativeRemindAtDate} style={isBeforeToday ? { color: "red" } : {}}>
|
||||||
|
<DateTimeFormatter>{remindAt}</DateTimeFormatter>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority Label Component
|
||||||
|
* @param priority
|
||||||
|
* @returns {Element}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const PriorityLabel = ({ priority }) => {
|
||||||
|
switch (priority) {
|
||||||
|
case 1:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
High <ExclamationCircleFilled style={{ marginLeft: "5px", color: "red" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Medium <ExclamationCircleFilled style={{ marginLeft: "5px", color: "yellow" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Low <ExclamationCircleFilled style={{ marginLeft: "5px", color: "green" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
None <ExclamationCircleFilled style={{ marginLeft: "5px" }} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
// Existing dispatch props...
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskListComponent);
|
||||||
|
|
||||||
|
function TaskListComponent({
|
||||||
|
bodyshop,
|
||||||
|
loading,
|
||||||
|
tasks,
|
||||||
|
total,
|
||||||
|
titleTranslation,
|
||||||
|
refetch,
|
||||||
|
toggleCompletedStatus,
|
||||||
|
setTaskUpsertContext,
|
||||||
|
toggleDeletedStatus,
|
||||||
|
relationshipType,
|
||||||
|
relationshipId,
|
||||||
|
onlyMine,
|
||||||
|
parentJobId,
|
||||||
|
query,
|
||||||
|
showRo = true
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
// Extract Query Params
|
||||||
|
const { page, sortcolumn, sortorder, deleted, completed, mine } = search;
|
||||||
|
|
||||||
|
const history = useNavigate();
|
||||||
|
const columns = [];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// This is a hack to force the page to change if the query params change (partssublet for example)
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
|
columns.push({
|
||||||
|
title: t("tasks.fields.created_at"),
|
||||||
|
dataIndex: "created_at",
|
||||||
|
key: "created_at",
|
||||||
|
width: "10%",
|
||||||
|
defaultSortOrder: "descend",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "created_at" && sortorder,
|
||||||
|
render: (text, record) => <DateTimeFormatter>{record.created_at}</DateTimeFormatter>
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!onlyMine) {
|
||||||
|
columns.push({
|
||||||
|
title: t("tasks.fields.assigned_to"),
|
||||||
|
dataIndex: "assigned_to",
|
||||||
|
key: "assigned_to",
|
||||||
|
width: "8%",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "assigned_to" && sortorder,
|
||||||
|
render: (text, record) => {
|
||||||
|
const employee = bodyshop?.employees?.find((e) => e.id === record.assigned_to);
|
||||||
|
return employee ? `${employee.first_name} ${employee.last_name}` : t("general.labels.na");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showRo) {
|
||||||
|
columns.push({
|
||||||
|
title: t("tasks.fields.job.ro_number"),
|
||||||
|
dataIndex: ["job", "ro_number"],
|
||||||
|
key: "job.ro_number",
|
||||||
|
width: "8%",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.job ? (
|
||||||
|
<Link to={`/manage/jobs/${record.job.id}?tab=tasks`}>{record.job.ro_number || t("general.labels.na")}</Link>
|
||||||
|
) : (
|
||||||
|
t("general.labels.na")
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.push(
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.jobline"),
|
||||||
|
dataIndex: ["jobline", "id"],
|
||||||
|
key: "jobline.id",
|
||||||
|
width: "8%",
|
||||||
|
render: (text, record) => record?.jobline?.line_desc || ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.parts_order"),
|
||||||
|
dataIndex: ["parts_order", "id"],
|
||||||
|
key: "part_order.id",
|
||||||
|
width: "8%",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.parts_order ? (
|
||||||
|
<Link to={`/manage/jobs/${record.job.id}?partsorderid=${record.parts_order.id}&tab=partssublet`}>
|
||||||
|
{record.parts_order.order_number && record.parts_order.vendor && record.parts_order.vendor.name
|
||||||
|
? `${record.parts_order.order_number} - ${record.parts_order.vendor.name}`
|
||||||
|
: t("general.labels.na")}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.bill"),
|
||||||
|
dataIndex: ["bill", "id"],
|
||||||
|
key: "bill.id",
|
||||||
|
width: "10%",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.bill ? (
|
||||||
|
<Link to={`/manage/jobs/${record.job.id}?billid=${record.bill.id}&tab=partssublet`}>
|
||||||
|
{record.bill.invoice_number && record.bill.vendor && record.bill.vendor.name
|
||||||
|
? `${record.bill.invoice_number} - ${record.bill.vendor.name}`
|
||||||
|
: t("general.labels.na")}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.title"),
|
||||||
|
dataIndex: "title",
|
||||||
|
key: "title",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "title" && sortorder
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.due_date"),
|
||||||
|
dataIndex: "due_date",
|
||||||
|
key: "due_date",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "due_date" && sortorder,
|
||||||
|
width: "8%",
|
||||||
|
render: (text, record) => <DueDateRecord dueDate={record.due_date} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.remind_at"),
|
||||||
|
dataIndex: "remind_at",
|
||||||
|
key: "remind_at",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "remind_at" && sortorder,
|
||||||
|
width: "10%",
|
||||||
|
render: (text, record) => <RemindAtRecord remindAt={record.remind_at} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.priority"),
|
||||||
|
dataIndex: "priority",
|
||||||
|
key: "priority",
|
||||||
|
sorter: true,
|
||||||
|
sortOrder: sortcolumn === "priority" && sortorder,
|
||||||
|
width: "8%",
|
||||||
|
render: (text, record) => <PriorityLabel priority={record.priority} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("tasks.fields.actions"),
|
||||||
|
key: "toggleCompleted",
|
||||||
|
width: "5%",
|
||||||
|
render: (text, record) => (
|
||||||
|
<Space direction="horizontal">
|
||||||
|
<Button
|
||||||
|
title={t("tasks.buttons.edit")}
|
||||||
|
onClick={() => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
context: {
|
||||||
|
existingTask: record,
|
||||||
|
query
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<EditFilled />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
title={t("tasks.buttons.complete")}
|
||||||
|
onClick={() => toggleCompletedStatus(record.id, record.completed)}
|
||||||
|
>
|
||||||
|
{record.completed ? <CheckCircleOutlined /> : <CheckCircleFilled />}
|
||||||
|
</Button>
|
||||||
|
<Button title={t("tasks.buttons.delete")} onClick={() => toggleDeletedStatus(record.id, record.deleted)}>
|
||||||
|
{record.deleted ? <DeleteOutlined /> : <DeleteFilled />}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCreateTask = useCallback(() => {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
actions: {},
|
||||||
|
context: {
|
||||||
|
jobid: parentJobId,
|
||||||
|
[relationshipType]: relationshipId,
|
||||||
|
query
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [parentJobId, relationshipId, relationshipType, setTaskUpsertContext, query]);
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
search.page = pagination.current;
|
||||||
|
search.sortcolumn = sorter.columnKey;
|
||||||
|
search.sortorder = sorter.order;
|
||||||
|
history({ search: queryString.stringify(search) });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSwitchChange = useCallback(
|
||||||
|
(param, value) => {
|
||||||
|
if (value) {
|
||||||
|
search[param] = "true";
|
||||||
|
} else {
|
||||||
|
delete search[param];
|
||||||
|
}
|
||||||
|
history({ search: queryString.stringify(search) });
|
||||||
|
},
|
||||||
|
[history, search]
|
||||||
|
);
|
||||||
|
|
||||||
|
const expandableRow = (record) => {
|
||||||
|
return (
|
||||||
|
<Card title={t("tasks.fields.description")} size="small">
|
||||||
|
{record.description}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra actions for the tasks
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
const tasksExtra = useCallback(() => {
|
||||||
|
return (
|
||||||
|
<Space direction="horizontal">
|
||||||
|
{!onlyMine && (
|
||||||
|
<Switch
|
||||||
|
checkedChildren={t("tasks.buttons.myTasks")}
|
||||||
|
unCheckedChildren={t("tasks.buttons.allTasks")}
|
||||||
|
title={t("tasks.titles.mine")}
|
||||||
|
checked={mine === "true"}
|
||||||
|
onChange={(value) => handleSwitchChange("mine", value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Switch
|
||||||
|
checkedChildren={<CheckCircleFilled />}
|
||||||
|
unCheckedChildren={<CheckCircleOutlined />}
|
||||||
|
title={t("tasks.titles.completed")}
|
||||||
|
checked={completed === "true"}
|
||||||
|
onChange={(value) => handleSwitchChange("completed", value)}
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
checkedChildren={<DeleteFilled />}
|
||||||
|
unCheckedChildren={<DeleteOutlined />}
|
||||||
|
title={t("tasks.titles.deleted")}
|
||||||
|
checked={deleted === "true"}
|
||||||
|
onChange={(value) => handleSwitchChange("deleted", value)}
|
||||||
|
/>
|
||||||
|
<Button title={t("tasks.buttons.refresh")} onClick={() => refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button title={t("tasks.buttons.create")} onClick={handleCreateTask}>
|
||||||
|
<PlusCircleFilled />
|
||||||
|
{t("tasks.buttons.create")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}, [refetch, deleted, completed, mine, onlyMine, t, handleSwitchChange, handleCreateTask]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={titleTranslation} extra={tasksExtra()}>
|
||||||
|
<Table
|
||||||
|
loading={loading}
|
||||||
|
pagination={{
|
||||||
|
pageSize: pageLimit,
|
||||||
|
current: parseInt(page || 1),
|
||||||
|
total: total,
|
||||||
|
responsive: true,
|
||||||
|
showQuickJumper: true
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
scroll={{ x: true }}
|
||||||
|
dataSource={tasks}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
expandable={{
|
||||||
|
expandedRowRender: expandableRow,
|
||||||
|
rowExpandable: (record) => record.description
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
193
client/src/components/task-list/task-list.container.jsx
Normal file
193
client/src/components/task-list/task-list.container.jsx
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import queryString from "query-string";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
|
import { MUTATION_TOGGLE_TASK_COMPLETED, MUTATION_TOGGLE_TASK_DELETED } from "../../graphql/tasks.queries.js";
|
||||||
|
import { pageLimit } from "../../utils/config.js";
|
||||||
|
import AlertComponent from "../alert/alert.component.jsx";
|
||||||
|
import React from "react";
|
||||||
|
import TaskListComponent from "./task-list.component.jsx";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect, useDispatch } from "react-redux";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskListContainer);
|
||||||
|
|
||||||
|
export function TaskListContainer({
|
||||||
|
bodyshop,
|
||||||
|
titleTranslation,
|
||||||
|
query,
|
||||||
|
relationshipType,
|
||||||
|
relationshipId,
|
||||||
|
currentUser,
|
||||||
|
onlyMine,
|
||||||
|
parentJobId,
|
||||||
|
showRo = true,
|
||||||
|
disableJobRefetch = false
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const {
|
||||||
|
page,
|
||||||
|
sortcolumn,
|
||||||
|
sortorder,
|
||||||
|
deleted,
|
||||||
|
completed //mine
|
||||||
|
} = searchParams;
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const { loading, error, data, refetch } = useQuery(query[Object.keys(query)[0]], {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
variables: {
|
||||||
|
bodyshop: bodyshop.id,
|
||||||
|
[relationshipType]: relationshipId,
|
||||||
|
deleted: deleted === "true",
|
||||||
|
completed: completed === "true", //TODO: Find where mine is set.
|
||||||
|
assigned_to: onlyMine ? bodyshop?.employees?.find((e) => e.user_email === currentUser.email)?.id : undefined, // replace currentUserID with the actual ID of the current user
|
||||||
|
offset: page ? (page - 1) * pageLimit : 0,
|
||||||
|
limit: pageLimit,
|
||||||
|
order: [
|
||||||
|
{
|
||||||
|
[sortcolumn || "created_at"]: sortorder ? (sortorder === "descend" ? "desc" : "asc") : "desc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task completed mutation
|
||||||
|
*/
|
||||||
|
const [toggleTaskCompleted] = useMutation(MUTATION_TOGGLE_TASK_COMPLETED);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task completed status
|
||||||
|
* @param id
|
||||||
|
* @param currentStatus
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const toggleCompletedStatus = async (id, currentStatus) => {
|
||||||
|
const completed_at = !currentStatus ? dayjs().toISOString() : null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const toggledTaskObject = {
|
||||||
|
variables: {
|
||||||
|
id: id,
|
||||||
|
completed: !currentStatus,
|
||||||
|
completed_at: completed_at
|
||||||
|
},
|
||||||
|
refetchQueries: [Object.keys(query)[0]]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!disableJobRefetch) {
|
||||||
|
toggledTaskObject.refetchQueries.push("GET_JOB_BY_PK");
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggledTask = await toggleTaskCompleted(toggledTaskObject);
|
||||||
|
|
||||||
|
if (!toggledTask.errors) {
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
||||||
|
operation: toggledTask?.data?.update_tasks_by_pk?.completed
|
||||||
|
? AuditTrailMapping.tasksCompleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email)
|
||||||
|
: AuditTrailMapping.tasksUncompleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email),
|
||||||
|
type: toggledTask?.data?.update_tasks_by_pk?.completed ? "tasksCompleted" : "tasksUncompleted"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
notification["success"]({
|
||||||
|
message: t("tasks.successes.completed")
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("tasks.failures.completed")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task deleted mutation
|
||||||
|
*/
|
||||||
|
const [toggleTaskDeleted] = useMutation(MUTATION_TOGGLE_TASK_DELETED);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task deleted status
|
||||||
|
* @param id
|
||||||
|
* @param currentStatus
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const toggleDeletedStatus = async (id, currentStatus) => {
|
||||||
|
const deleted_at = !currentStatus ? dayjs().toISOString() : null;
|
||||||
|
try {
|
||||||
|
const toggledTaskObject = {
|
||||||
|
variables: {
|
||||||
|
id: id,
|
||||||
|
deleted: !currentStatus,
|
||||||
|
deleted_at: deleted_at
|
||||||
|
},
|
||||||
|
refetchQueries: [Object.keys(query)[0]]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!disableJobRefetch) {
|
||||||
|
toggledTaskObject.refetchQueries.push("GET_JOB_BY_PK");
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggledTask = await toggleTaskDeleted(toggledTaskObject);
|
||||||
|
|
||||||
|
if (!toggledTask.errors) {
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: toggledTask.data.update_tasks_by_pk.jobid,
|
||||||
|
operation: toggledTask?.data?.update_tasks_by_pk?.deleted
|
||||||
|
? AuditTrailMapping.tasksDeleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email)
|
||||||
|
: AuditTrailMapping.tasksUndeleted(toggledTask.data.update_tasks_by_pk.title, currentUser.email),
|
||||||
|
type: toggledTask?.data?.update_tasks_by_pk?.deleted ? "tasksDeleted" : "tasksUndeleted"
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
notification["success"]({
|
||||||
|
message: t("tasks.successes.deleted")
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("tasks.failures.deleted")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TaskListComponent
|
||||||
|
loading={loading}
|
||||||
|
tasks={data ? data.tasks : null}
|
||||||
|
total={data ? data.tasks_aggregate.aggregate.count : 0}
|
||||||
|
titleTranslation={t(titleTranslation || "tasks.title")}
|
||||||
|
refetch={refetch}
|
||||||
|
toggleCompletedStatus={toggleCompletedStatus}
|
||||||
|
toggleDeletedStatus={toggleDeletedStatus}
|
||||||
|
relationshipType={relationshipType}
|
||||||
|
relationshipId={relationshipId}
|
||||||
|
onlyMine={onlyMine}
|
||||||
|
showRo={showRo}
|
||||||
|
parentJobId={parentJobId}
|
||||||
|
bodyshop={bodyshop}
|
||||||
|
query={query}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
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 { FormDateTimePickerEnhanced } from "../form-date-time-picker-enhanced/form-date-time-picker-enhanced.component.jsx";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = () => ({});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskUpsertModalComponent);
|
||||||
|
|
||||||
|
export function TaskUpsertModalComponent({
|
||||||
|
form,
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
selectedJobId,
|
||||||
|
setSelectedJobId,
|
||||||
|
selectedJobDetails,
|
||||||
|
existingTask,
|
||||||
|
loading,
|
||||||
|
error
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const datePickerPresets = [
|
||||||
|
{ label: t("tasks.date_presets.today"), value: dayjs().add(1, "hour") },
|
||||||
|
{ label: t("tasks.date_presets.tomorrow"), value: dayjs().add(1, "day").startOf("day") },
|
||||||
|
{ label: t("tasks.date_presets.next_week"), value: dayjs().add(1, "week").startOf("day") },
|
||||||
|
{ label: t("tasks.date_presets.two_weeks"), value: dayjs().add(2, "weeks").startOf("day") },
|
||||||
|
{ label: t("tasks.date_presets.three_weeks"), value: dayjs().add(3, "weeks").startOf("day") },
|
||||||
|
{ label: t("tasks.date_presets.one_month"), value: dayjs().add(1, "month").startOf("day") },
|
||||||
|
{ label: t("tasks.date_presets.three_months"), value: dayjs().add(3, "month").startOf("day") }
|
||||||
|
];
|
||||||
|
|
||||||
|
const generatePresets = (job) => {
|
||||||
|
if (!job || !selectedJobDetails) return datePickerPresets; // return default presets if no job selected
|
||||||
|
const relativePresets = [];
|
||||||
|
|
||||||
|
if (selectedJobDetails?.scheduled_completion) {
|
||||||
|
const scheduledCompletion = dayjs(selectedJobDetails.scheduled_completion);
|
||||||
|
|
||||||
|
if (scheduledCompletion.isAfter(dayjs())) {
|
||||||
|
relativePresets.push(
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.completion")} -1 ${t("tasks.date_presets.day")}`,
|
||||||
|
value: scheduledCompletion.subtract(1, "day").startOf("day")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.completion")} -2 ${t("tasks.date_presets.days")}`,
|
||||||
|
value: scheduledCompletion.subtract(2, "day").startOf("day")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.completion")} -3 ${t("tasks.date_presets.days")}`,
|
||||||
|
value: scheduledCompletion.subtract(3, "day").startOf("day")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedJobDetails?.scheduled_delivery) {
|
||||||
|
const scheduledDelivery = dayjs(selectedJobDetails.scheduled_delivery);
|
||||||
|
if (scheduledDelivery.isAfter(dayjs())) {
|
||||||
|
relativePresets.push(
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.delivery")} -1 ${t("tasks.date_presets.day")}`,
|
||||||
|
value: scheduledDelivery.subtract(1, "day").startOf("day")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.delivery")} -2 ${t("tasks.date_presets.days")}`,
|
||||||
|
value: scheduledDelivery.subtract(2, "day").startOf("day")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `${t("tasks.date_presets.delivery")} -3 ${t("tasks.date_presets.days")}`,
|
||||||
|
value: scheduledDelivery.subtract(3, "day").startOf("day")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...relativePresets, ...datePickerPresets];
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearRelations = () => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
billid: null,
|
||||||
|
partsorderid: null,
|
||||||
|
joblineid: null
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the selected job id
|
||||||
|
* @param jobId
|
||||||
|
*/
|
||||||
|
const changeJobId = (jobId) => {
|
||||||
|
setSelectedJobId(jobId || null);
|
||||||
|
// Reset the form fields when selectedJobId changes
|
||||||
|
clearRelations();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading || error) return <LoadingSkeleton active />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("tasks.fields.title")}
|
||||||
|
name="title"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("tasks.fields.title")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Form.Item label={t("tasks.fields.priority")} name="priority" initialValue={2}>
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{ value: 3, label: t("tasks.fields.priorities.low") },
|
||||||
|
{ value: 2, label: t("tasks.fields.priorities.medium") },
|
||||||
|
{ value: 1, label: t("tasks.fields.priorities.high") }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("tasks.fields.completed")}
|
||||||
|
name="completed"
|
||||||
|
valuePropName="checked"
|
||||||
|
initialValue={false}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Form.Item
|
||||||
|
name="jobid"
|
||||||
|
initialValue={selectedJobId}
|
||||||
|
label={t("tasks.fields.jobid")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<JobSearchSelectComponent
|
||||||
|
placeholder={t("tasks.placeholders.jobid")}
|
||||||
|
onSelect={changeJobId}
|
||||||
|
onClear={changeJobId}
|
||||||
|
autoFocus={false}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label={t("tasks.fields.joblineid")} name="joblineid">
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.joblineid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
|
options={selectedJobDetails?.joblines?.map((jobline) => ({
|
||||||
|
key: jobline.id,
|
||||||
|
value: jobline.id,
|
||||||
|
label: jobline.line_desc
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label={t("tasks.fields.partsorderid")} name="partsorderid">
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.partsorderid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
|
options={selectedJobDetails?.parts_orders?.map((partsOrder) => ({
|
||||||
|
key: partsOrder.id,
|
||||||
|
value: partsOrder.id,
|
||||||
|
label: `${partsOrder.order_number} - ${partsOrder.vendor.name}`
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label={t("tasks.fields.billid")} name="billid">
|
||||||
|
<Select
|
||||||
|
allowClear
|
||||||
|
placeholder={t("tasks.placeholders.billid")}
|
||||||
|
disabled={!selectedJobDetails || !selectedJobId}
|
||||||
|
options={selectedJobDetails?.bills?.map((bill) => ({
|
||||||
|
key: bill.id,
|
||||||
|
value: bill.id,
|
||||||
|
label: `${bill.invoice_number} - ${bill.vendor.name}`
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("tasks.fields.assigned_to")}
|
||||||
|
name="assigned_to"
|
||||||
|
initialValue={
|
||||||
|
bodyshop.employees.find((employee) => employee?.user_email === currentUser.email && employee.active)?.id
|
||||||
|
}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
placeholder={t("tasks.placeholders.assigned_to")}
|
||||||
|
options={bodyshop.employees
|
||||||
|
.filter((x) => x.active && x.user_email)
|
||||||
|
.map((employee) => ({
|
||||||
|
key: employee.id,
|
||||||
|
value: employee.id,
|
||||||
|
label: `${employee.first_name} ${employee.last_name}`
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label={t("tasks.fields.due_date")} name="due_date">
|
||||||
|
<FormDatePicker
|
||||||
|
onlyFuture
|
||||||
|
format="MM/DD/YYYY"
|
||||||
|
presets={generatePresets(selectedJobDetails)}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!value || existingTask?.due_date === value || dayjs(value).isAfter(dayjs())) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(t("tasks.validation.due_at_error_message")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("tasks.fields.remind_at")}
|
||||||
|
name="remind_at"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!value || existingTask?.remind_at === value || dayjs(value).isAfter(dayjs().add(15, "minute"))) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(t("tasks.validation.remind_at_error_message")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormDateTimePickerEnhanced
|
||||||
|
onlyFuture
|
||||||
|
showTime
|
||||||
|
minuteStep={15}
|
||||||
|
presets={generatePresets(selectedJobDetails)}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Form.Item label={t("tasks.fields.description")} name="description">
|
||||||
|
<Input.TextArea rows={8} placeholder={t("tasks.fields.description")} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
|
import { Form, Modal, notification } from "antd";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { MUTATION_INSERT_NEW_TASK, MUTATION_UPDATE_TASK, QUERY_GET_TASK_BY_ID } from "../../graphql/tasks.queries";
|
||||||
|
import { GET_JOB_BY_PK, QUERY_GET_TASKS_JOB_DETAILS_BY_ID } from "../../graphql/jobs.queries.js";
|
||||||
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectTaskUpsert } from "../../redux/modals/modals.selectors";
|
||||||
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import TaskUpsertModalComponent from "./task-upsert-modal.component";
|
||||||
|
import { replaceUndefinedWithNull } from "../../utils/undefinedtonull.js";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions.js";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
||||||
|
import { isEqual } from "lodash";
|
||||||
|
import refetchRouteMappings from "./task-upsert-modal.route.mappings";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
taskUpsert: selectTaskUpsert
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
|
||||||
|
insertAuditTrail: ({ jobid, billid, operation, type }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, toggleModalVisible, insertAuditTrail }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const history = useNavigate();
|
||||||
|
const [insertTask] = useMutation(MUTATION_INSERT_NEW_TASK);
|
||||||
|
const [updateTask] = useMutation(MUTATION_UPDATE_TASK);
|
||||||
|
const { open, context } = taskUpsert;
|
||||||
|
const { jobid, joblineid, billid, partsorderid, taskId, existingTask, query } = context;
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [selectedJobId, setSelectedJobId] = useState(null);
|
||||||
|
const [selectedJobDetails, setSelectedJobDetails] = useState(null);
|
||||||
|
const [jobIdState, setJobIdState] = useState(null);
|
||||||
|
const [isTouched, setIsTouched] = useState(false);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const { loading, error, data } = useQuery(QUERY_GET_TASKS_JOB_DETAILS_BY_ID, {
|
||||||
|
variables: { id: jobIdState },
|
||||||
|
skip: !jobIdState
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading: taskLoading,
|
||||||
|
error: taskError,
|
||||||
|
data: taskData
|
||||||
|
} = useQuery(QUERY_GET_TASK_BY_ID, {
|
||||||
|
variables: { id: taskId },
|
||||||
|
skip: !taskId
|
||||||
|
});
|
||||||
|
// Use Effect to hydrate existing task if only a taskid is provided
|
||||||
|
useEffect(() => {
|
||||||
|
if (!taskLoading && !taskError && taskData && taskData?.tasks_by_pk) {
|
||||||
|
form.setFieldsValue(taskData.tasks_by_pk);
|
||||||
|
setSelectedJobId(taskData.tasks_by_pk.jobid);
|
||||||
|
}
|
||||||
|
}, [taskLoading, taskError, taskData, form]);
|
||||||
|
|
||||||
|
// Use Effect to hydrate selected job details
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && !error && data) {
|
||||||
|
setSelectedJobDetails(data.jobs_by_pk);
|
||||||
|
}
|
||||||
|
}, [loading, error, data]);
|
||||||
|
|
||||||
|
// Use Effect to toggle to set jobid state
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedJobId) {
|
||||||
|
setJobIdState(selectedJobId);
|
||||||
|
}
|
||||||
|
}, [selectedJobId]);
|
||||||
|
|
||||||
|
// Use Effect to hydrate form fields
|
||||||
|
useEffect(() => {
|
||||||
|
if (jobid || existingTask?.id) {
|
||||||
|
setSelectedJobId(jobid || existingTask.jobid);
|
||||||
|
}
|
||||||
|
if (existingTask && open) {
|
||||||
|
form.setFieldsValue(existingTask);
|
||||||
|
} else if (!existingTask && open) {
|
||||||
|
form.resetFields();
|
||||||
|
if (joblineid) form.setFieldsValue({ joblineid });
|
||||||
|
if (billid) form.setFieldsValue({ billid });
|
||||||
|
if (partsorderid) form.setFieldsValue({ partsorderid });
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
setSelectedJobId(null);
|
||||||
|
setIsTouched(false);
|
||||||
|
};
|
||||||
|
}, [jobid, existingTask, form, open, joblineid, billid, partsorderid]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the taskid from the URL
|
||||||
|
*/
|
||||||
|
const removeTaskIdFromUrl = () => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
if (!urlParams.has("taskid")) return;
|
||||||
|
urlParams.delete("taskid");
|
||||||
|
history(`${window.location.pathname}?${urlParams}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate refetch queries
|
||||||
|
* @param jobId
|
||||||
|
* @returns {*[]}
|
||||||
|
*/
|
||||||
|
const generateRefetchQueries = (jobId) => {
|
||||||
|
const refetchQueries = [];
|
||||||
|
|
||||||
|
if (location.pathname.includes("/manage/jobs") && jobId) {
|
||||||
|
refetchQueries.push({
|
||||||
|
query: GET_JOB_BY_PK,
|
||||||
|
variables: {
|
||||||
|
id: jobId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a specified query
|
||||||
|
if (query && Object.keys(query).length) {
|
||||||
|
refetchQueries.push(Object.keys(query)[0]);
|
||||||
|
}
|
||||||
|
// We don't have a specified query, check the page
|
||||||
|
else {
|
||||||
|
refetchRouteMappings.forEach((mapping) => {
|
||||||
|
if (location.pathname.includes(mapping.route)) {
|
||||||
|
refetchQueries.push(mapping.query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return refetchQueries;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle existing task
|
||||||
|
* @param id
|
||||||
|
* @param jobId
|
||||||
|
* @param values
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const handleExistingTask = async (id, jobId, values) => {
|
||||||
|
const task = replaceUndefinedWithNull(values);
|
||||||
|
|
||||||
|
// Remind at is dirty so lets clear remind_at_sent
|
||||||
|
if (task?.remind_at) {
|
||||||
|
task.remind_at_sent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskObject = {
|
||||||
|
variables: {
|
||||||
|
taskId: id,
|
||||||
|
task
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
taskObject.refetchQueries = generateRefetchQueries(jobId);
|
||||||
|
|
||||||
|
const taskData = await updateTask(taskObject);
|
||||||
|
|
||||||
|
if (!taskData.errors) {
|
||||||
|
const oldTask = taskData?.data?.update_tasks?.returning[0];
|
||||||
|
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: oldTask.jobid,
|
||||||
|
operation: AuditTrailMapping.tasksUpdated(oldTask.title, currentUser.email),
|
||||||
|
type: "tasksUpdated"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
notification["success"]({
|
||||||
|
message: t("tasks.successes.updated")
|
||||||
|
});
|
||||||
|
|
||||||
|
toggleModalVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle new task
|
||||||
|
* @param values
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const handleNewTask = async (values) => {
|
||||||
|
const taskObject = {
|
||||||
|
variables: {
|
||||||
|
taskInput: [
|
||||||
|
{
|
||||||
|
...values,
|
||||||
|
created_by: currentUser.email,
|
||||||
|
bodyshopid: bodyshop.id
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
taskObject.refetchQueries = generateRefetchQueries(values.jobid);
|
||||||
|
|
||||||
|
const newTaskData = await insertTask(taskObject);
|
||||||
|
const newTask = newTaskData?.data?.insert_tasks?.returning[0];
|
||||||
|
|
||||||
|
if (!newTaskData.errors) {
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: newTask.jobid,
|
||||||
|
operation: AuditTrailMapping.tasksCreated(newTask.title, currentUser.email),
|
||||||
|
type: "tasksCreated"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
form.resetFields();
|
||||||
|
toggleModalVisible();
|
||||||
|
|
||||||
|
notification["success"]({
|
||||||
|
message: t("tasks.successes.created")
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the form submit
|
||||||
|
* @param formValues
|
||||||
|
* @returns {Promise<[{jobid, bodyshopid, created_by},...*]>}
|
||||||
|
*/
|
||||||
|
const handleFinish = async (formValues) => {
|
||||||
|
if (existingTask || taskData?.tasks_by_pk) {
|
||||||
|
const taskSource = existingTask || taskData?.tasks_by_pk;
|
||||||
|
const dirtyValues = Object.keys(formValues).reduce((acc, key) => {
|
||||||
|
if (!isEqual(formValues[key], taskSource[key])) {
|
||||||
|
acc[key] = formValues[key];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
try {
|
||||||
|
await handleExistingTask(taskSource.id, taskSource.jobid, dirtyValues);
|
||||||
|
} catch (e) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("tasks.failures.updated")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await handleNewTask(formValues);
|
||||||
|
} catch (e) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("tasks.failures.created")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={existingTask ? t("tasks.actions.edit") : t("tasks.actions.new")}
|
||||||
|
open={open}
|
||||||
|
okText={t("general.actions.save")}
|
||||||
|
width="50%"
|
||||||
|
onOk={() => {
|
||||||
|
removeTaskIdFromUrl();
|
||||||
|
form.submit();
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
removeTaskIdFromUrl();
|
||||||
|
toggleModalVisible();
|
||||||
|
}}
|
||||||
|
okButtonProps={{ disabled: !isTouched }}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={handleFinish}
|
||||||
|
layout="vertical"
|
||||||
|
onFieldsChange={() => {
|
||||||
|
setIsTouched(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TaskUpsertModalComponent
|
||||||
|
form={form}
|
||||||
|
loading={loading || (taskId && taskLoading)}
|
||||||
|
error={error}
|
||||||
|
data={data}
|
||||||
|
existingTask={existingTask || taskData?.tasks_by_pk}
|
||||||
|
selectedJobId={selectedJobId}
|
||||||
|
setSelectedJobId={setSelectedJobId}
|
||||||
|
selectedJobDetails={selectedJobDetails}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TaskUpsertModalContainer);
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import {
|
||||||
|
QUERY_ALL_TASKS_PAGINATED,
|
||||||
|
QUERY_JOB_TASKS_PAGINATED,
|
||||||
|
QUERY_MY_TASKS_PAGINATED
|
||||||
|
} from "../../graphql/tasks.queries.js";
|
||||||
|
|
||||||
|
const getQueryName = (query) => Object.keys(query)[0];
|
||||||
|
|
||||||
|
const refetchRouteMappings = [
|
||||||
|
{query: getQueryName({QUERY_MY_TASKS_PAGINATED}), route: "/manage/tasks/mytasks"},
|
||||||
|
{query: getQueryName({QUERY_ALL_TASKS_PAGINATED}), route: "/manage/tasks/alltasks"},
|
||||||
|
{query: getQueryName({QUERY_JOB_TASKS_PAGINATED}), route: "/manage/jobs"}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default refetchRouteMappings;
|
||||||
@@ -6,7 +6,8 @@ import { Link } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||||
|
|
||||||
@@ -67,9 +68,20 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
|||||||
text: status,
|
text: status,
|
||||||
value: status
|
value: status
|
||||||
})),
|
})),
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.actual_completion"),
|
||||||
|
dataIndex: "actual_completion",
|
||||||
|
key: "actual_completion",
|
||||||
|
render: (text, record) => (
|
||||||
|
<DateTimeFormatter>{record.actual_completion}</DateTimeFormatter>
|
||||||
|
),
|
||||||
|
sorter: (a, b) => dateSort(a.actual_completion, b.actual_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actual_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_total"),
|
title: t("jobs.fields.clm_total"),
|
||||||
dataIndex: "clm_total",
|
dataIndex: "clm_total",
|
||||||
|
|||||||
@@ -59,9 +59,14 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
|||||||
name
|
name
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
|
tasks {
|
||||||
|
id
|
||||||
|
due_date
|
||||||
|
}
|
||||||
order_date
|
order_date
|
||||||
deliver_by
|
deliver_by
|
||||||
return
|
return
|
||||||
|
returnfrombill
|
||||||
orderedby
|
orderedby
|
||||||
parts_order_lines {
|
parts_order_lines {
|
||||||
id
|
id
|
||||||
|
|||||||
@@ -67,6 +67,24 @@ export const QUERY_ACTIVE_EMPLOYEES = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_ACTIVE_EMPLOYEES_WITH_EMAIL = gql`
|
||||||
|
query QUERY_ACTIVE_EMPLOYEES_WITH_EMAIL {
|
||||||
|
employees(where: {active: {_eq: true}, user_email: {_is_null: false}}) {
|
||||||
|
last_name
|
||||||
|
id
|
||||||
|
first_name
|
||||||
|
employee_number
|
||||||
|
active
|
||||||
|
termination_date
|
||||||
|
hire_date
|
||||||
|
flat_rate
|
||||||
|
rates
|
||||||
|
pin
|
||||||
|
user_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const INSERT_EMPLOYEES = gql`
|
export const INSERT_EMPLOYEES = gql`
|
||||||
mutation INSERT_EMPLOYEES($employees: [employees_insert_input!]!) {
|
mutation INSERT_EMPLOYEES($employees: [employees_insert_input!]!) {
|
||||||
insert_employees(objects: $employees) {
|
insert_employees(objects: $employees) {
|
||||||
|
|||||||
@@ -219,6 +219,12 @@ export const UPDATE_JOB_LINE = gql`
|
|||||||
id
|
id
|
||||||
notes
|
notes
|
||||||
mod_lbr_ty
|
mod_lbr_ty
|
||||||
|
mod_lb_hrs
|
||||||
|
part_type
|
||||||
|
op_code_desc
|
||||||
|
prt_dsmk_m
|
||||||
|
prt_dsmk_p
|
||||||
|
tax_part
|
||||||
part_qty
|
part_qty
|
||||||
db_price
|
db_price
|
||||||
act_price
|
act_price
|
||||||
|
|||||||
@@ -499,6 +499,11 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
|||||||
export const GET_JOB_BY_PK = gql`
|
export const GET_JOB_BY_PK = gql`
|
||||||
query GET_JOB_BY_PK($id: uuid!) {
|
query GET_JOB_BY_PK($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
|
tasks_aggregate(where: { completed: { _eq: false }, deleted: { _eq: false } }) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
actual_completion
|
actual_completion
|
||||||
actual_delivery
|
actual_delivery
|
||||||
actual_in
|
actual_in
|
||||||
@@ -1969,19 +1974,19 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
|||||||
kmout
|
kmout
|
||||||
qb_multiple_payers
|
qb_multiple_payers
|
||||||
lbr_adjustments
|
lbr_adjustments
|
||||||
payments {
|
payments {
|
||||||
amount
|
amount
|
||||||
created_at
|
created_at
|
||||||
date
|
date
|
||||||
exportedat
|
exportedat
|
||||||
id
|
id
|
||||||
jobid
|
jobid
|
||||||
memo
|
memo
|
||||||
payer
|
payer
|
||||||
paymentnum
|
paymentnum
|
||||||
transactionid
|
transactionid
|
||||||
type
|
type
|
||||||
}
|
}
|
||||||
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
|
||||||
id
|
id
|
||||||
removed
|
removed
|
||||||
@@ -1994,7 +1999,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
|||||||
db_price
|
db_price
|
||||||
act_price
|
act_price
|
||||||
part_qty
|
part_qty
|
||||||
notes
|
notes
|
||||||
mod_lbr_ty
|
mod_lbr_ty
|
||||||
db_hrs
|
db_hrs
|
||||||
mod_lb_hrs
|
mod_lb_hrs
|
||||||
@@ -2006,9 +2011,9 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
|||||||
prt_dsmk_p
|
prt_dsmk_p
|
||||||
convertedtolbr
|
convertedtolbr
|
||||||
convertedtolbr_data
|
convertedtolbr_data
|
||||||
act_price_before_ppc
|
act_price_before_ppc
|
||||||
sublet_ignored
|
sublet_ignored
|
||||||
sublet_completed
|
sublet_completed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2056,12 +2061,12 @@ export const generate_UPDATE_JOB_KANBAN = (
|
|||||||
}`;
|
}`;
|
||||||
|
|
||||||
return gql`
|
return gql`
|
||||||
mutation UPDATE_JOB_KANBAN {
|
mutation UPDATE_JOB_KANBAN {
|
||||||
${oldChildId ? oldChildQuery : ""}
|
${oldChildId ? oldChildQuery : ""}
|
||||||
${movedId ? movedQuery : ""}
|
${movedId ? movedQuery : ""}
|
||||||
${newChildId ? newChildQuery : ""}
|
${newChildId ? newChildQuery : ""}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QUERY_JOB_LBR_ADJUSTMENTS = gql`
|
export const QUERY_JOB_LBR_ADJUSTMENTS = gql`
|
||||||
@@ -2081,6 +2086,34 @@ export const DELETE_JOB = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_GET_TASKS_JOB_DETAILS_BY_ID = gql`
|
||||||
|
query QUERY_GET_TASKS_JOB_DETAILS_BY_ID($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
id
|
||||||
|
scheduled_delivery
|
||||||
|
scheduled_completion
|
||||||
|
joblines {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
}
|
||||||
|
bills {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
invoice_number
|
||||||
|
}
|
||||||
|
parts_orders {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
order_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_JOB_FOR_CC_CONTRACT = gql`
|
export const GET_JOB_FOR_CC_CONTRACT = gql`
|
||||||
query GET_JOB_FOR_CC_CONTRACT($id: uuid!) {
|
query GET_JOB_FOR_CC_CONTRACT($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
@@ -2238,6 +2271,8 @@ export const GET_JOB_LINE_ORDERS = gql`
|
|||||||
parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) {
|
parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) {
|
||||||
id
|
id
|
||||||
act_price
|
act_price
|
||||||
|
backordered_eta
|
||||||
|
backordered_on
|
||||||
parts_order {
|
parts_order {
|
||||||
id
|
id
|
||||||
order_date
|
order_date
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const QUERY_OWNER_BY_ID = gql`
|
|||||||
tax_number
|
tax_number
|
||||||
jobs(order_by: { date_open: desc }) {
|
jobs(order_by: { date_open: desc }) {
|
||||||
id
|
id
|
||||||
|
actual_completion
|
||||||
ro_number
|
ro_number
|
||||||
clm_no
|
clm_no
|
||||||
status
|
status
|
||||||
|
|||||||
383
client/src/graphql/tasks.queries.js
Normal file
383
client/src/graphql/tasks.queries.js
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
|
export const PARTIAL_TASK_FIELDS = gql`
|
||||||
|
fragment TaskFields on tasks {
|
||||||
|
id
|
||||||
|
created_at
|
||||||
|
updated_at
|
||||||
|
title
|
||||||
|
description
|
||||||
|
deleted
|
||||||
|
deleted_at
|
||||||
|
due_date
|
||||||
|
created_by
|
||||||
|
assigned_to
|
||||||
|
assigned_to_employee {
|
||||||
|
id
|
||||||
|
user_email
|
||||||
|
}
|
||||||
|
completed
|
||||||
|
completed_at
|
||||||
|
remind_at
|
||||||
|
priority
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
joblines {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
}
|
||||||
|
bills {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
invoice_number
|
||||||
|
}
|
||||||
|
parts_orders {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
order_number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jobid
|
||||||
|
jobline {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
}
|
||||||
|
joblineid
|
||||||
|
parts_order {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
order_number
|
||||||
|
}
|
||||||
|
partsorderid
|
||||||
|
bill {
|
||||||
|
id
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
invoice_number
|
||||||
|
}
|
||||||
|
billid
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_GET_TASK_BY_ID = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_GET_TASK_BY_ID($id: uuid!) {
|
||||||
|
tasks_by_pk(id: $id) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_ALL_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_ALL_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$assigned_to: uuid
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Query for joblineid
|
||||||
|
export const QUERY_JOBLINE_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_JOBLINE_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$joblineid: uuid!
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$assigned_to: uuid
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
joblineid: { _eq: $joblineid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
joblineid: { _eq: $joblineid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Query for partsorderid
|
||||||
|
export const QUERY_PARTSORDER_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_PARTSORDER_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$partsorderid: uuid!
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$assigned_to: uuid
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
partsorderid: { _eq: $partsorderid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
partsorderid: { _eq: $partsorderid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Query for billid
|
||||||
|
export const QUERY_BILL_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_BILL_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$billid: uuid!
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$assigned_to: uuid
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
billid: { _eq: $billid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
billid: { _eq: $billid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Use the fragment in your queries
|
||||||
|
export const QUERY_JOB_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_JOB_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$jobid: uuid!
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$assigned_to: uuid
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
jobid: { _eq: $jobid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
jobid: { _eq: $jobid }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_MY_TASKS_PAGINATED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
query QUERY_MY_TASKS_PAGINATED(
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$assigned_to: uuid!
|
||||||
|
$bodyshop: uuid!
|
||||||
|
$deleted: Boolean
|
||||||
|
$completed: Boolean
|
||||||
|
$order: [tasks_order_by!]!
|
||||||
|
) {
|
||||||
|
tasks(
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: {
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
tasks_aggregate(
|
||||||
|
where: {
|
||||||
|
assigned_to: { _eq: $assigned_to }
|
||||||
|
bodyshopid: { _eq: $bodyshop }
|
||||||
|
deleted: { _eq: $deleted }
|
||||||
|
completed: { _eq: $completed }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task completed mutation
|
||||||
|
* @type {DocumentNode}
|
||||||
|
*/
|
||||||
|
export const MUTATION_TOGGLE_TASK_COMPLETED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) {
|
||||||
|
update_tasks_by_pk(pk_columns: { id: $id }, _set: { completed: $completed, completed_at: $completed_at }) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle task deleted mutation
|
||||||
|
* @type {DocumentNode}
|
||||||
|
*/
|
||||||
|
export const MUTATION_TOGGLE_TASK_DELETED = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
mutation MUTATION_TOGGLE_TASK_DELETED($id: uuid!, $deleted: Boolean!, $deleted_at: timestamptz) {
|
||||||
|
update_tasks_by_pk(pk_columns: { id: $id }, _set: { deleted: $deleted, deleted_at: $deleted_at }) {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert new task mutation
|
||||||
|
* @type {DocumentNode}
|
||||||
|
*/
|
||||||
|
export const MUTATION_INSERT_NEW_TASK = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
mutation MUTATION_INSERT_NEW_TASK($taskInput: [tasks_insert_input!]!) {
|
||||||
|
insert_tasks(objects: $taskInput) {
|
||||||
|
returning {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update task mutation
|
||||||
|
* @type {DocumentNode}
|
||||||
|
*/
|
||||||
|
export const MUTATION_UPDATE_TASK = gql`
|
||||||
|
${PARTIAL_TASK_FIELDS}
|
||||||
|
mutation MUTATION_UPDATE_TASK($taskId: uuid!, $task: tasks_set_input!) {
|
||||||
|
update_tasks(where: { id: { _eq: $taskId } }, _set: $task) {
|
||||||
|
returning {
|
||||||
|
...TaskFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -30,6 +30,7 @@ export const QUERY_VEHICLE_BY_ID = gql`
|
|||||||
notes
|
notes
|
||||||
jobs(order_by: { date_open: desc }) {
|
jobs(order_by: { date_open: desc }) {
|
||||||
id
|
id
|
||||||
|
actual_completion
|
||||||
ro_number
|
ro_number
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
ownr_fn
|
ownr_fn
|
||||||
|
|||||||
@@ -51,6 +51,17 @@ export function JobsAvailablePageContainer({ partnerVersion, setBreadcrumbs, set
|
|||||||
{!partnerVersion && (
|
{!partnerVersion && (
|
||||||
<AlertComponent
|
<AlertComponent
|
||||||
type="warning"
|
type="warning"
|
||||||
|
action={
|
||||||
|
<a
|
||||||
|
href={InstanceRenderManager({
|
||||||
|
imex: "https://partner.imex.online/Setup.exe",
|
||||||
|
rome: "https://partner.romeonline.io/Setup.exe",
|
||||||
|
promanager: "https://dzaenazwrgg60.cloudfront.net/Setup.exe"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Button size="small">{t("general.actions.download")}</Button>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
message={t("general.messages.partnernotrunning", {
|
message={t("general.messages.partnernotrunning", {
|
||||||
app: InstanceRenderManager({
|
app: InstanceRenderManager({
|
||||||
imex: "$t(titles.imexonline)",
|
imex: "$t(titles.imexonline)",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import { insertAuditTrail } from "../../redux/application/application.actions";
|
|||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import JobCloseRoGuardContainer from '../../components/job-close-ro-guard/job-close-ro-guard.container';
|
import JobCloseRoGuardContainer from "../../components/job-close-ro-guard/job-close-ro-guard.container";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -120,6 +120,13 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
operation: AuditTrailMapping.jobinvoiced(),
|
operation: AuditTrailMapping.jobinvoiced(),
|
||||||
type: "jobinvoiced"
|
type: "jobinvoiced"
|
||||||
});
|
});
|
||||||
|
if (values.masterbypass) {
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: job.id,
|
||||||
|
operation: AuditTrailMapping.jobclosedwithbypass(),
|
||||||
|
type: "jobclosedwithbypass"
|
||||||
|
});
|
||||||
|
}
|
||||||
// history(`/manage/jobs/${job.id}`);
|
// history(`/manage/jobs/${job.id}`);
|
||||||
} else {
|
} else {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -184,7 +191,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail }) {
|
|||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<JobCloseRoGuardContainer form={form} job={job} />
|
<JobCloseRoGuardContainer form={form} job={job} />
|
||||||
<Space wrap direction="vertical" style={{ width: "100%" }}>
|
<Space wrap direction="vertical" style={{ width: "100%" }}>
|
||||||
<FormsFieldChanged form={form} />
|
<FormsFieldChanged form={form} />
|
||||||
{!job.actual_in && job.scheduled_in && <Alert type="warning" message={t("jobs.labels.actual_in_inferred")} />}
|
{!job.actual_in && job.scheduled_in && <Alert type="warning" message={t("jobs.labels.actual_in_inferred")} />}
|
||||||
|
|||||||
@@ -20,18 +20,21 @@ export default function JobsCreateComponent({ form }) {
|
|||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.vehicleinfo"),
|
title: t("jobs.labels.create.vehicleinfo"),
|
||||||
|
id: "step-job-vehicleinfo",
|
||||||
content: <JobsCreateVehicleInfoContainer form={form} />,
|
content: <JobsCreateVehicleInfoContainer form={form} />,
|
||||||
validation: !!state.vehicle.new || !!state.vehicle.selectedid || !!state.vehicle.none,
|
validation: !!state.vehicle.new || !!state.vehicle.selectedid || !!state.vehicle.none,
|
||||||
error: t("vehicles.errors.selectexistingornew")
|
error: t("vehicles.errors.selectexistingornew")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.ownerinfo"),
|
title: t("jobs.labels.create.ownerinfo"),
|
||||||
|
id: "step-job-ownerinfo",
|
||||||
content: <JobsCreateOwnerInfoContainer />,
|
content: <JobsCreateOwnerInfoContainer />,
|
||||||
validation: !!state.owner.new || !!state.owner.selectedid,
|
validation: !!state.owner.new || !!state.owner.selectedid,
|
||||||
error: t("owners.errors.selectexistingornew")
|
error: t("owners.errors.selectexistingornew")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.jobinfo"),
|
title: t("jobs.labels.create.jobinfo"),
|
||||||
|
id: "step-job-jobinfo",
|
||||||
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2} />
|
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2} />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import Icon, {
|
|||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
ToolFilled
|
ToolFilled
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Button, Divider, Form, notification, Space, Tabs } from "antd";
|
import { Badge, Button, Divider, Form, notification, Space, Tabs } from "antd";
|
||||||
import { PageHeader } from "@ant-design/pro-layout";
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
@@ -16,7 +16,7 @@ import dayjs from "../../utils/day";
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { FaHardHat, FaRegStickyNote, FaShieldAlt } from "react-icons/fa";
|
import { FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
@@ -53,14 +53,29 @@ import JobProfileDataWarning from "../../components/job-profile-data-warning/job
|
|||||||
import { DateTimeFormat } from "../../utils/DateFormatter";
|
import { DateTimeFormat } from "../../utils/DateFormatter";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
|
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
|
||||||
|
import { QUERY_JOB_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
jobRO: selectJobReadOnly
|
jobRO: selectJobReadOnly
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
setPrintCenterContext: (context) => dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
setPrintCenterContext: (context) =>
|
||||||
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "printCenter"
|
||||||
|
})
|
||||||
|
),
|
||||||
|
insertAuditTrail: ({ jobid, operation, type }) =>
|
||||||
|
dispatch(
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid,
|
||||||
|
operation,
|
||||||
|
type
|
||||||
|
})
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
export function JobsDetailPage({
|
export function JobsDetailPage({
|
||||||
@@ -69,7 +84,6 @@ export function JobsDetailPage({
|
|||||||
jobRO,
|
jobRO,
|
||||||
job,
|
job,
|
||||||
mutationUpdateJob,
|
mutationUpdateJob,
|
||||||
handleSubmit,
|
|
||||||
insertAuditTrail,
|
insertAuditTrail,
|
||||||
refetch
|
refetch
|
||||||
}) {
|
}) {
|
||||||
@@ -277,6 +291,7 @@ export function JobsDetailPage({
|
|||||||
{
|
{
|
||||||
key: "general",
|
key: "general",
|
||||||
icon: <Icon component={FaShieldAlt} />,
|
icon: <Icon component={FaShieldAlt} />,
|
||||||
|
id: "job-details-general",
|
||||||
label: t("menus.jobsdetail.general"),
|
label: t("menus.jobsdetail.general"),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <JobsDetailGeneral job={job} form={form} />
|
children: <JobsDetailGeneral job={job} form={form} />
|
||||||
@@ -284,6 +299,7 @@ export function JobsDetailPage({
|
|||||||
{
|
{
|
||||||
key: "repairdata",
|
key: "repairdata",
|
||||||
icon: <BarsOutlined />,
|
icon: <BarsOutlined />,
|
||||||
|
id: "job-details-repairdata",
|
||||||
label: t("menus.jobsdetail.repairdata"),
|
label: t("menus.jobsdetail.repairdata"),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <JobsLinesContainer job={job} joblines={job.joblines} refetch={refetch} form={form} />
|
children: <JobsLinesContainer job={job} joblines={job.joblines} refetch={refetch} form={form} />
|
||||||
@@ -291,18 +307,21 @@ export function JobsDetailPage({
|
|||||||
{
|
{
|
||||||
key: "rates",
|
key: "rates",
|
||||||
icon: <DollarCircleOutlined />,
|
icon: <DollarCircleOutlined />,
|
||||||
|
id: "job-details-rates",
|
||||||
label: t("menus.jobsdetail.rates"),
|
label: t("menus.jobsdetail.rates"),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
children: <JobsDetailRates job={job} form={form} />
|
children: <JobsDetailRates job={job} form={form} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "totals",
|
key: "totals",
|
||||||
|
id: "job-details-totals",
|
||||||
icon: <DollarCircleOutlined />,
|
icon: <DollarCircleOutlined />,
|
||||||
label: t("menus.jobsdetail.totals"),
|
label: t("menus.jobsdetail.totals"),
|
||||||
children: <JobsDetailTotals job={job} refetch={refetch} />
|
children: <JobsDetailTotals job={job} refetch={refetch} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "partssublet",
|
key: "partssublet",
|
||||||
|
id: "job-details-partssublet",
|
||||||
icon: <ToolFilled />,
|
icon: <ToolFilled />,
|
||||||
label: HasFeatureAccess({ featureName: "bills", bodyshop })
|
label: HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||||
? t("menus.jobsdetail.partssublet")
|
? t("menus.jobsdetail.partssublet")
|
||||||
@@ -317,6 +336,7 @@ export function JobsDetailPage({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "labor",
|
key: "labor",
|
||||||
|
id: "job-details-labor",
|
||||||
icon: <Icon component={FaHardHat} />,
|
icon: <Icon component={FaHardHat} />,
|
||||||
label: t("menus.jobsdetail.labor"),
|
label: t("menus.jobsdetail.labor"),
|
||||||
children: <JobsDetailLaborContainer job={job} jobId={job.id} />
|
children: <JobsDetailLaborContainer job={job} jobId={job.id} />
|
||||||
@@ -326,11 +346,13 @@ export function JobsDetailPage({
|
|||||||
{
|
{
|
||||||
key: "lifecycle",
|
key: "lifecycle",
|
||||||
icon: <BarsOutlined />,
|
icon: <BarsOutlined />,
|
||||||
|
id: "job-details-lifecycle",
|
||||||
label: t("menus.jobsdetail.lifecycle"),
|
label: t("menus.jobsdetail.lifecycle"),
|
||||||
children: <JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses} />
|
children: <JobLifecycleComponent job={job} statuses={bodyshop.md_ro_statuses} />
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "dates",
|
key: "dates",
|
||||||
|
id: "job-details-dates",
|
||||||
icon: <CalendarFilled />,
|
icon: <CalendarFilled />,
|
||||||
label: t("menus.jobsdetail.dates"),
|
label: t("menus.jobsdetail.dates"),
|
||||||
forceRender: true,
|
forceRender: true,
|
||||||
@@ -344,6 +366,7 @@ export function JobsDetailPage({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "documents",
|
key: "documents",
|
||||||
|
id: "job-details-documents",
|
||||||
icon: <FileImageFilled />,
|
icon: <FileImageFilled />,
|
||||||
label: t("jobs.labels.documents"),
|
label: t("jobs.labels.documents"),
|
||||||
children: bodyshop.uselocalmediaserver ? (
|
children: bodyshop.uselocalmediaserver ? (
|
||||||
@@ -356,6 +379,7 @@ export function JobsDetailPage({
|
|||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: "notes",
|
key: "notes",
|
||||||
|
id: "job-details-notes",
|
||||||
icon: <Icon component={FaRegStickyNote} />,
|
icon: <Icon component={FaRegStickyNote} />,
|
||||||
label: t("jobs.labels.notes"),
|
label: t("jobs.labels.notes"),
|
||||||
children: <JobNotesContainer jobId={job.id} />
|
children: <JobNotesContainer jobId={job.id} />
|
||||||
@@ -363,8 +387,29 @@ export function JobsDetailPage({
|
|||||||
{
|
{
|
||||||
key: "audit",
|
key: "audit",
|
||||||
icon: <HistoryOutlined />,
|
icon: <HistoryOutlined />,
|
||||||
|
id: "job-details-audit",
|
||||||
label: t("jobs.labels.audit"),
|
label: t("jobs.labels.audit"),
|
||||||
children: <JobAuditTrail jobId={job.id} />
|
children: <JobAuditTrail jobId={job.id} />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "tasks",
|
||||||
|
icon: <FaTasks />,
|
||||||
|
id: "job-details-tasks",
|
||||||
|
label: (
|
||||||
|
<Space direction="horizontal">
|
||||||
|
{t("jobs.labels.tasks")}
|
||||||
|
{job.tasks_aggregate.aggregate.count > 0 && <Badge count={job.tasks_aggregate.aggregate.count} />}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
children: (
|
||||||
|
<TaskListContainer
|
||||||
|
relationshipType={"jobid"}
|
||||||
|
relationshipId={job.id}
|
||||||
|
query={{ QUERY_JOB_TASKS_PAGINATED }}
|
||||||
|
titleTranslation="tasks.titles.job_tasks"
|
||||||
|
showRo={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FloatButton, Layout, Spin, Collapse, Button, Space, Tag } from "antd";
|
import { FloatButton, Layout, Spin } from "antd";
|
||||||
// import preval from "preval.macro";
|
// import preval from "preval.macro";
|
||||||
import React, { lazy, Suspense, useEffect, useState } from "react";
|
import React, { lazy, Suspense, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -30,8 +30,8 @@ import "./manage.page.styles.scss";
|
|||||||
|
|
||||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||||
|
|
||||||
const CardPaymentModalContainer = lazy(() =>
|
const CardPaymentModalContainer = lazy(
|
||||||
import("../../components/card-payment-modal/card-payment-modal.container.")
|
() => import("../../components/card-payment-modal/card-payment-modal.container.")
|
||||||
);
|
);
|
||||||
|
|
||||||
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container"));
|
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container"));
|
||||||
@@ -59,8 +59,8 @@ const JobCostingModal = lazy(() => import("../../components/job-costing-modal/jo
|
|||||||
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container"));
|
const ReportCenterModal = lazy(() => import("../../components/report-center-modal/report-center-modal.container"));
|
||||||
const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-modal.container"));
|
const BillEnterModalContainer = lazy(() => import("../../components/bill-enter-modal/bill-enter-modal.container"));
|
||||||
const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
const TimeTicketModalContainer = lazy(() => import("../../components/time-ticket-modal/time-ticket-modal.container"));
|
||||||
const TimeTicketModalTask = lazy(() =>
|
const TimeTicketModalTask = lazy(
|
||||||
import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
() => import("../../components/time-ticket-task-modal/time-ticket-task-modal.container")
|
||||||
);
|
);
|
||||||
const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container"));
|
const PaymentModalContainer = lazy(() => import("../../components/payment-modal/payment-modal.container"));
|
||||||
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
const ProductionListPage = lazy(() => import("../production-list/production-list.container"));
|
||||||
@@ -97,7 +97,10 @@ const Dms = lazy(() => import("../dms/dms.container"));
|
|||||||
const DmsPayables = lazy(() => import("../dms-payables/dms-payables.container"));
|
const DmsPayables = lazy(() => import("../dms-payables/dms-payables.container"));
|
||||||
const ManageRootPage = lazy(() => import("../manage-root/manage-root.page.container"));
|
const ManageRootPage = lazy(() => import("../manage-root/manage-root.page.container"));
|
||||||
const TtApprovals = lazy(() => import("../tt-approvals/tt-approvals.page.container"));
|
const TtApprovals = lazy(() => import("../tt-approvals/tt-approvals.page.container"));
|
||||||
|
const MyTasksPage = lazy(() => import("../tasks/myTasksPageContainer.jsx"));
|
||||||
|
const AllTasksPage = lazy(() => import("../tasks/allTasksPageContainer.jsx"));
|
||||||
|
|
||||||
|
const TaskUpsertModalContainer = lazy(() => import("../../components/task-upsert-modal/task-upsert-modal.container"));
|
||||||
const { Content, Footer } = Layout;
|
const { Content, Footer } = Layout;
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -114,7 +117,6 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) {
|
export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [chatVisible] = useState(false);
|
const [chatVisible] = useState(false);
|
||||||
const [tours, setTours] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = InstanceRenderManager({
|
const widgetId = InstanceRenderManager({
|
||||||
@@ -147,12 +149,10 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
This
|
|
||||||
>
|
>
|
||||||
<PaymentModalContainer />
|
<PaymentModalContainer />
|
||||||
|
|
||||||
<CardPaymentModalContainer />
|
<CardPaymentModalContainer />
|
||||||
|
<TaskUpsertModalContainer />
|
||||||
<BreadCrumbs />
|
<BreadCrumbs />
|
||||||
<BillEnterModalContainer />
|
<BillEnterModalContainer />
|
||||||
<JobCostingModal />
|
<JobCostingModal />
|
||||||
@@ -252,6 +252,22 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/tasks/mytasks"
|
||||||
|
element={
|
||||||
|
<Suspense fallback={<Spin />}>
|
||||||
|
<MyTasksPage />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/tasks/alltasks"
|
||||||
|
element={
|
||||||
|
<Suspense fallback={<Spin />}>
|
||||||
|
<AllTasksPage />
|
||||||
|
</Suspense>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/inventory/"
|
path="/inventory/"
|
||||||
element={
|
element={
|
||||||
@@ -613,28 +629,6 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
|||||||
Disclaimer & Notices
|
Disclaimer & Notices
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{InstanceRenderManager({
|
|
||||||
promanager: (
|
|
||||||
<Collapse>
|
|
||||||
<Collapse.Panel header="DEVELOPMENT ONLY - ProductFruits Tours">
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
setTours(await window.productFruits.api.tours.getTours());
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Get Tours
|
|
||||||
</Button>
|
|
||||||
{tours.map((tour) => (
|
|
||||||
<Tag key={tour.id} onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
|
||||||
{tour.name}
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
</Collapse.Panel>
|
|
||||||
</Collapse>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Footer>
|
</Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -72,10 +72,17 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
InstanceRenderManager({
|
||||||
key: "licensing",
|
executeFunction: true,
|
||||||
label: t("bodyshop.labels.licensing"),
|
args: [],
|
||||||
children: <ShopInfoUsersComponent />
|
imex: () => {
|
||||||
|
items.push({
|
||||||
|
key: "licensing",
|
||||||
|
label: t("bodyshop.labels.licensing"),
|
||||||
|
children: <ShopInfoUsersComponent />
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rome: "USE_IMEX"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
||||||
|
|||||||
60
client/src/pages/tasks/allTasksPageContainer.jsx
Normal file
60
client/src/pages/tasks/allTasksPageContainer.jsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import TasksPageComponent from "./tasks.page.component";
|
||||||
|
import queryString from "query-string";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
|
import TaskPageTypes from "./taskPageTypes.jsx";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions.js";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
|
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function AllTasksPageContainer({ setBreadcrumbs, setSelectedHeader, setTaskUpsertContext }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.all_tasks", {
|
||||||
|
app: InstanceRenderManager({
|
||||||
|
imex: "$t(titles.imexonline)",
|
||||||
|
rome: "$t(titles.romeonline)",
|
||||||
|
promanager: "$t(titles.promanager)"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
setSelectedHeader("all_tasks");
|
||||||
|
setBreadcrumbs([
|
||||||
|
{
|
||||||
|
link: "/manage/tasks/alltasks",
|
||||||
|
label: t("titles.bc.all_tasks")
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
|
// This takes care of the ability to deep link a task from the URL (Dispatches the modal)
|
||||||
|
useEffect(() => {
|
||||||
|
// Check for a query string in the URL
|
||||||
|
const urlParams = new URLSearchParams(searchParams);
|
||||||
|
const taskId = urlParams.get("taskid");
|
||||||
|
if (taskId) {
|
||||||
|
setTaskUpsertContext({
|
||||||
|
context: {
|
||||||
|
taskId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
urlParams.delete("taskid");
|
||||||
|
}
|
||||||
|
}, [setTaskUpsertContext, searchParams]);
|
||||||
|
|
||||||
|
return <TasksPageComponent type={TaskPageTypes.ALL_TASKS} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(AllTasksPageContainer);
|
||||||
40
client/src/pages/tasks/myTasksPageContainer.jsx
Normal file
40
client/src/pages/tasks/myTasksPageContainer.jsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import TasksPageComponent from "./tasks.page.component";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr.js";
|
||||||
|
import TaskPageTypes from "./taskPageTypes.jsx";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function MyTasksPageContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.my_tasks", {
|
||||||
|
app: InstanceRenderManager({
|
||||||
|
imex: "$t(titles.imexonline)",
|
||||||
|
rome: "$t(titles.romeonline)",
|
||||||
|
promanager: "$t(titles.promanager)"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
setSelectedHeader("my_tasks");
|
||||||
|
setBreadcrumbs([
|
||||||
|
{
|
||||||
|
link: "/manage/tasks/mytasks",
|
||||||
|
label: t("titles.bc.my_tasks")
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
|
return <TasksPageComponent type={TaskPageTypes.MY_TASKS} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(MyTasksPageContainer);
|
||||||
6
client/src/pages/tasks/taskPageTypes.jsx
Normal file
6
client/src/pages/tasks/taskPageTypes.jsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const TaskPageTypes = {
|
||||||
|
MY_TASKS: "myTasks",
|
||||||
|
ALL_TASKS: "allTasks"
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TaskPageTypes;
|
||||||
41
client/src/pages/tasks/tasks.page.component.jsx
Normal file
41
client/src/pages/tasks/tasks.page.component.jsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React from "react";
|
||||||
|
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
|
||||||
|
import { QUERY_ALL_TASKS_PAGINATED, QUERY_MY_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
||||||
|
import taskPageTypes from "./taskPageTypes.jsx";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors.js";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
bodyshop: selectBodyshop
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(TasksPageComponent);
|
||||||
|
|
||||||
|
export function TasksPageComponent({ bodyshop, currentUser, type }) {
|
||||||
|
switch (type) {
|
||||||
|
case taskPageTypes.MY_TASKS:
|
||||||
|
return (
|
||||||
|
<TaskListContainer
|
||||||
|
onlyMine={true}
|
||||||
|
relationshipId={bodyshop?.employees?.find((e) => e.user_email === currentUser.email)?.id}
|
||||||
|
relationshipType={"assigned_to"}
|
||||||
|
query={{ QUERY_MY_TASKS_PAGINATED }}
|
||||||
|
titleTranslation={"tasks.titles.my_tasks"}
|
||||||
|
disableJobRefetch={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case taskPageTypes.ALL_TASKS:
|
||||||
|
return (
|
||||||
|
<TaskListContainer
|
||||||
|
query={{ QUERY_ALL_TASKS_PAGINATED }}
|
||||||
|
titleTranslation={"tasks.titles.all_tasks"}
|
||||||
|
disableJobRefetch={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,12 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
(load[itemDate].allHoursIn || 0) +
|
(load[itemDate].allHoursIn || 0) +
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].allHoursInBody =
|
||||||
|
(load[itemDate].allHoursInBody || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].allHoursInRefinish =
|
||||||
|
(load[itemDate].allHoursInRefinish || 0) +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
//If the job hasn't already arrived, add it to the jobs in list.
|
//If the job hasn't already arrived, add it to the jobs in list.
|
||||||
// Make sure it also hasn't already been completed, or isn't an in and out job.
|
// Make sure it also hasn't already been completed, or isn't an in and out job.
|
||||||
//This prevents the duplicate counting.
|
//This prevents the duplicate counting.
|
||||||
@@ -119,7 +124,15 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
if (AddJobForSchedulingCalc) {
|
if (AddJobForSchedulingCalc) {
|
||||||
load[itemDate].jobsIn.push(item);
|
load[itemDate].jobsIn.push(item);
|
||||||
load[itemDate].hoursIn =
|
load[itemDate].hoursIn =
|
||||||
(load[itemDate].hoursIn || 0) + item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs;
|
(load[itemDate].hoursIn || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].hoursInBody =
|
||||||
|
(load[itemDate].hoursInBody || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].hoursInRefinish =
|
||||||
|
(load[itemDate].hoursInRefinish || 0) +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
load[itemDate] = {
|
load[itemDate] = {
|
||||||
@@ -127,10 +140,21 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
jobsIn: AddJobForSchedulingCalc ? [item] : [], //Same as above, only add it if it isn't already in production.
|
jobsIn: AddJobForSchedulingCalc ? [item] : [], //Same as above, only add it if it isn't already in production.
|
||||||
jobsOut: [],
|
jobsOut: [],
|
||||||
allJobsOut: [],
|
allJobsOut: [],
|
||||||
allHoursIn: item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs,
|
allHoursIn:
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
|
allHoursInBody: item.labhrs.aggregate.sum.mod_lb_hrs,
|
||||||
|
allHoursInRefinish: item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
hoursIn: AddJobForSchedulingCalc
|
hoursIn: AddJobForSchedulingCalc
|
||||||
? item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs
|
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
: 0
|
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||||
|
: 0,
|
||||||
|
hoursInBody: AddJobForSchedulingCalc
|
||||||
|
? item.labhrs.aggregate.sum.mod_lb_hrs
|
||||||
|
: 0,
|
||||||
|
hoursInRefinish: AddJobForSchedulingCalc
|
||||||
|
? item.larhrs.aggregate.sum.mod_lb_hrs
|
||||||
|
: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -151,6 +175,12 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
(load[itemDate].allHoursOut || 0) +
|
(load[itemDate].allHoursOut || 0) +
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].allHoursOutBody =
|
||||||
|
(load[itemDate].allHoursOutBody || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].allHoursOutRefinish =
|
||||||
|
(load[itemDate].allHoursOutRefinish || 0) +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
//Add only the jobs that are still in production to get rid of.
|
//Add only the jobs that are still in production to get rid of.
|
||||||
//If it's not in production, we'd subtract unnecessarily.
|
//If it's not in production, we'd subtract unnecessarily.
|
||||||
load[itemDate].allJobsOut.push(item);
|
load[itemDate].allJobsOut.push(item);
|
||||||
@@ -161,6 +191,12 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
(load[itemDate].hoursOut || 0) +
|
(load[itemDate].hoursOut || 0) +
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].hoursOutBody =
|
||||||
|
(load[itemDate].hoursOutBody || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
load[itemDate].hoursOutRefinish =
|
||||||
|
(load[itemDate].hoursOutRefinish || 0) +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
load[itemDate] = {
|
load[itemDate] = {
|
||||||
@@ -169,7 +205,11 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
hoursOut: AddJobForSchedulingCalc
|
hoursOut: AddJobForSchedulingCalc
|
||||||
? item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs
|
? item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs
|
||||||
: 0,
|
: 0,
|
||||||
allHoursOut: item.labhrs.aggregate.sum.mod_lb_hrs + item.larhrs.aggregate.sum.mod_lb_hrs
|
allHoursOut:
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
|
allHoursOutBody: item.labhrs.aggregate.sum.mod_lb_hrs,
|
||||||
|
allHoursOutRefinish: item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ const INITIAL_STATE = {
|
|||||||
name: "ShopName",
|
name: "ShopName",
|
||||||
address: InstanceRenderManager({
|
address: InstanceRenderManager({
|
||||||
imex: "noreply@iemx.online",
|
imex: "noreply@iemx.online",
|
||||||
rome: "noreply@romeonline.io"
|
rome: "noreply@romeonline.io",
|
||||||
|
promanager: "noreply@promanager.web-est.com"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
to: null,
|
to: null,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const INITIAL_STATE = {
|
|||||||
billEnter: { ...baseModal },
|
billEnter: { ...baseModal },
|
||||||
courtesyCarReturn: { ...baseModal },
|
courtesyCarReturn: { ...baseModal },
|
||||||
noteUpsert: { ...baseModal },
|
noteUpsert: { ...baseModal },
|
||||||
|
taskUpsert: { ...baseModal },
|
||||||
schedule: { ...baseModal },
|
schedule: { ...baseModal },
|
||||||
partsOrder: { ...baseModal },
|
partsOrder: { ...baseModal },
|
||||||
timeTicket: { ...baseModal },
|
timeTicket: { ...baseModal },
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export const selectCourtesyCarReturn = createSelector([selectModals], (modals) =
|
|||||||
|
|
||||||
export const selectNoteUpsert = createSelector([selectModals], (modals) => modals.noteUpsert);
|
export const selectNoteUpsert = createSelector([selectModals], (modals) => modals.noteUpsert);
|
||||||
|
|
||||||
|
export const selectTaskUpsert = createSelector([selectModals], (modals) => modals.taskUpsert);
|
||||||
|
|
||||||
export const selectSchedule = createSelector([selectModals], (modals) => modals.schedule);
|
export const selectSchedule = createSelector([selectModals], (modals) => modals.schedule);
|
||||||
|
|
||||||
export const selectPartsOrder = createSelector([selectModals], (modals) => modals.partsOrder);
|
export const selectPartsOrder = createSelector([selectModals], (modals) => modals.partsOrder);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { getToken } from "firebase/messaging";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
||||||
|
import { Userpilot } from "userpilot";
|
||||||
import { factory } from "../../App/App.container";
|
import { factory } from "../../App/App.container";
|
||||||
import {
|
import {
|
||||||
analytics,
|
analytics,
|
||||||
@@ -25,6 +26,10 @@ import {
|
|||||||
messaging,
|
messaging,
|
||||||
updateCurrentUser
|
updateCurrentUser
|
||||||
} from "../../firebase/firebase.utils";
|
} from "../../firebase/firebase.utils";
|
||||||
|
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
|
||||||
|
import client from "../../utils/GraphQLClient";
|
||||||
|
import day from "../../utils/day";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import {
|
import {
|
||||||
checkInstanceId,
|
checkInstanceId,
|
||||||
sendPasswordResetFailure,
|
sendPasswordResetFailure,
|
||||||
@@ -43,11 +48,6 @@ import {
|
|||||||
validatePasswordResetSuccess
|
validatePasswordResetSuccess
|
||||||
} from "./user.actions";
|
} from "./user.actions";
|
||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
import client from "../../utils/GraphQLClient";
|
|
||||||
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
|
|
||||||
import day from "../../utils/day";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import { Userpilot } from "userpilot";
|
|
||||||
|
|
||||||
const fpPromise = FingerprintJS.load();
|
const fpPromise = FingerprintJS.load();
|
||||||
|
|
||||||
@@ -310,10 +310,41 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
|||||||
updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false })
|
updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const user = yield select((state) => state.user.currentUser);
|
||||||
if (payload.features.singleDeviceOnly) {
|
if (payload.features.singleDeviceOnly) {
|
||||||
const user = yield select((state) => state.user.currentUser);
|
if (
|
||||||
|
!(
|
||||||
|
user.email.includes("@imex.") ||
|
||||||
|
user.email.includes("@rome.") ||
|
||||||
|
user.email.includes("@rometech.") ||
|
||||||
|
user.email.includes("@promanager.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
yield put(setInstanceId(user.uid));
|
||||||
|
}
|
||||||
|
|
||||||
if (!(user.email.includes("@imex.") || user.email.includes("@rome."))) yield put(setInstanceId(user.uid));
|
//For Rome, check to make sure it's not a PM shop.
|
||||||
|
try {
|
||||||
|
InstanceRenderManager({
|
||||||
|
executeFunction: true,
|
||||||
|
args: [],
|
||||||
|
rome: () => {
|
||||||
|
if (
|
||||||
|
payload.imexshopid.toLowerCase().startsWith("pm_") &&
|
||||||
|
!(
|
||||||
|
user.email.includes("@imex.") ||
|
||||||
|
user.email.includes("@rome.") ||
|
||||||
|
user.email.includes("@rometech.") ||
|
||||||
|
user.email.includes("@promanager.")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error("You are not authorized to use this application.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
promanager: () => {}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
yield put(setInstanceConflict());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
|
||||||
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
"jobassignmentremoved": "Employee assignment removed for {{operation}}",
|
||||||
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
|
||||||
|
"jobclosedwithbypass": "Job was invoiced using the master bypass password. ",
|
||||||
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
"jobconverted": "Job converted and assigned number {{ro_number}}.",
|
||||||
"jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.",
|
"jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.",
|
||||||
"jobexported": "",
|
"jobexported": "",
|
||||||
@@ -134,7 +135,13 @@
|
|||||||
"jobstatuschange": "Job status changed to {{status}}.",
|
"jobstatuschange": "Job status changed to {{status}}.",
|
||||||
"jobsupplement": "Job supplement imported.",
|
"jobsupplement": "Job supplement imported.",
|
||||||
"jobsuspend": "Suspend Toggle set to {{status}}",
|
"jobsuspend": "Suspend Toggle set to {{status}}",
|
||||||
"jobvoid": "Job has been voided."
|
"jobvoid": "Job has been voided.",
|
||||||
|
"tasks_completed": "Task '{{title}}' completed by {{completedBy}}",
|
||||||
|
"tasks_created": "Task '{{title}}' created by {{createdBy}}",
|
||||||
|
"tasks_deleted": "Task '{{title}}' deleted by {{deletedBy}}",
|
||||||
|
"tasks_uncompleted": "Task '{{title}}' uncompleted by {{uncompletedBy}}",
|
||||||
|
"tasks_undeleted": "Task '{{title}}' undeleted by {{undeletedBy}}",
|
||||||
|
"tasks_updated": "Task '{{title}}' updated by {{updatedBy}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -223,11 +230,12 @@
|
|||||||
"markexported": "Mark Exported",
|
"markexported": "Mark Exported",
|
||||||
"markforreexport": "Mark for Re-export",
|
"markforreexport": "Mark for Re-export",
|
||||||
"new": "New Bill",
|
"new": "New Bill",
|
||||||
"nobilllines": "This part has not yet been recieved.",
|
"nobilllines": "",
|
||||||
"noneselected": "No bill selected.",
|
"noneselected": "No bill selected.",
|
||||||
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
|
||||||
"printlabels": "Print Labels",
|
"printlabels": "Print Labels",
|
||||||
"retailtotal": "Bills Retail Total",
|
"retailtotal": "Bills Retail Total",
|
||||||
|
"returnfrombill": "Return From Bill",
|
||||||
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
|
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
|
||||||
"state_tax": "State Tax",
|
"state_tax": "State Tax",
|
||||||
"subtotal": "Subtotal",
|
"subtotal": "Subtotal",
|
||||||
@@ -858,10 +866,11 @@
|
|||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"in": "Available",
|
"in": "Available",
|
||||||
"inservice": "In Service",
|
"inservice": "Service/Maintenance",
|
||||||
"leasereturn": "Lease Returned",
|
"leasereturn": "Lease Returned",
|
||||||
"out": "Rented",
|
"out": "Rented",
|
||||||
"sold": "Sold"
|
"sold": "Sold",
|
||||||
|
"unavailable": "Unavailable"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"saved": "Courtesy Car saved successfully."
|
"saved": "Courtesy Car saved successfully."
|
||||||
@@ -931,7 +940,8 @@
|
|||||||
"scheduledindate": "Sheduled In Today: {{date}}",
|
"scheduledindate": "Sheduled In Today: {{date}}",
|
||||||
"scheduledintoday": "Sheduled In Today",
|
"scheduledintoday": "Sheduled In Today",
|
||||||
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
"scheduledoutdate": "Sheduled Out Today: {{date}}",
|
||||||
"scheduledouttoday": "Sheduled Out Today"
|
"scheduledouttoday": "Sheduled Out Today",
|
||||||
|
"tasks": "Tasks"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -1126,6 +1136,7 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"deleteall": "Delete All",
|
"deleteall": "Delete All",
|
||||||
"deselectall": "Deselect All",
|
"deselectall": "Deselect All",
|
||||||
|
"download": "Download",
|
||||||
"edit": "Edit",
|
"edit": "Edit",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
"print": "Print",
|
"print": "Print",
|
||||||
@@ -1351,6 +1362,7 @@
|
|||||||
"amount": "Amount",
|
"amount": "Amount",
|
||||||
"dateOfPayment": "Date of Payment",
|
"dateOfPayment": "Date of Payment",
|
||||||
"descriptions": "Payment Details",
|
"descriptions": "Payment Details",
|
||||||
|
"hint": "Hint",
|
||||||
"payer": "Payer",
|
"payer": "Payer",
|
||||||
"payername": "Payer Name",
|
"payername": "Payer Name",
|
||||||
"paymentid": "Payment Reference ID",
|
"paymentid": "Payment Reference ID",
|
||||||
@@ -1541,6 +1553,7 @@
|
|||||||
"voiding": "Error voiding Job. {{error}}"
|
"voiding": "Error voiding Job. {{error}}"
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"active_tasks": "Active Tasks",
|
||||||
"actual_completion": "Actual Completion",
|
"actual_completion": "Actual Completion",
|
||||||
"actual_delivery": "Actual Delivery",
|
"actual_delivery": "Actual Delivery",
|
||||||
"actual_in": "Actual In",
|
"actual_in": "Actual In",
|
||||||
@@ -1975,7 +1988,7 @@
|
|||||||
"outstanding_ar": "A balance is outstanding on this RO. Payments can still be entered when the job is closed. ",
|
"outstanding_ar": "A balance is outstanding on this RO. Payments can still be entered when the job is closed. ",
|
||||||
"outstanding_credit_memos": "Outstanding credit memos have not been entered against this job. Credit Memos may still be posted once the job is closed.",
|
"outstanding_credit_memos": "Outstanding credit memos have not been entered against this job. Credit Memos may still be posted once the job is closed.",
|
||||||
"outstanding_ppd": "There are outstanding PPDs that may not have been synced back to the estimate.",
|
"outstanding_ppd": "There are outstanding PPDs that may not have been synced back to the estimate.",
|
||||||
"outstanding_reconciliation_discrep": "At least one discrepancy is not 0. This may indicate that this job is not properly reconciled and should not be closed.",
|
"outstanding_reconciliation_discrep": "At least one discrepancy is not $0. This may indicate that this job is not properly reconciled and should not be closed.",
|
||||||
"outstanding_sublets": "There are sublet lines on the job which have not been marked as completed. ",
|
"outstanding_sublets": "There are sublet lines on the job which have not been marked as completed. ",
|
||||||
"outstandinghours": "There are outstanding hours on the job that have not been paid or have been overpaid.",
|
"outstandinghours": "There are outstanding hours on the job that have not been paid or have been overpaid.",
|
||||||
"override_header": "Override estimate header on import?",
|
"override_header": "Override estimate header on import?",
|
||||||
@@ -2004,6 +2017,7 @@
|
|||||||
"ppc": "This line contains a part price change.",
|
"ppc": "This line contains a part price change.",
|
||||||
"ppdnotexported": "PPDs not Exported",
|
"ppdnotexported": "PPDs not Exported",
|
||||||
"profileadjustments": "Profile Disc./Mkup",
|
"profileadjustments": "Profile Disc./Mkup",
|
||||||
|
"profitbypassrequired": "Minimum gross profit requirements have not been met.",
|
||||||
"profits": "Job Profits",
|
"profits": "Job Profits",
|
||||||
"prt_dsmk_total": "Line Item Adjustment",
|
"prt_dsmk_total": "Line Item Adjustment",
|
||||||
"rates": "Rates",
|
"rates": "Rates",
|
||||||
@@ -2025,12 +2039,12 @@
|
|||||||
"remove_from_ar": "Remove from AR",
|
"remove_from_ar": "Remove from AR",
|
||||||
"returntotals": "Return Totals",
|
"returntotals": "Return Totals",
|
||||||
"ro_guard": {
|
"ro_guard": {
|
||||||
"eforce_profit": "Profit margins enforced.",
|
|
||||||
"enforce_ar": "AR collection enforced.",
|
"enforce_ar": "AR collection enforced.",
|
||||||
"enforce_bills": "Bill discrepancy enforced.",
|
"enforce_bills": "Bill discrepancy enforced.",
|
||||||
"enforce_cm": "Credit memo entry enforced.",
|
"enforce_cm": "Credit memo entry enforced.",
|
||||||
"enforce_labor": "Labor allocations enforced.",
|
"enforce_labor": "Labor allocations enforced.",
|
||||||
"enforce_ppd": "PPD sync enforced.",
|
"enforce_ppd": "PPD sync enforced.",
|
||||||
|
"enforce_profit": "Profit marginsenforced.",
|
||||||
"enforce_sublet": "Sublet completion enforced.",
|
"enforce_sublet": "Sublet completion enforced.",
|
||||||
"enforce_validation": "Master Bypass Required: {{message}}",
|
"enforce_validation": "Master Bypass Required: {{message}}",
|
||||||
"enforced": "This check has been enforced by your shop manager. Enter the master bypass password to close the Job."
|
"enforced": "This check has been enforced by your shop manager. Enter the master bypass password to close the Job."
|
||||||
@@ -2053,6 +2067,7 @@
|
|||||||
"supplementnote": "The Job had a supplement imported.",
|
"supplementnote": "The Job had a supplement imported.",
|
||||||
"suspended": "SUSPENDED",
|
"suspended": "SUSPENDED",
|
||||||
"suspense": "Suspense",
|
"suspense": "Suspense",
|
||||||
|
"tasks": "Tasks",
|
||||||
"threshhold": "Max Threshold: ${{amount}}",
|
"threshhold": "Max Threshold: ${{amount}}",
|
||||||
"total_cost": "Total Cost",
|
"total_cost": "Total Cost",
|
||||||
"total_cust_payable": "Total Customer Amount Payable",
|
"total_cust_payable": "Total Customer Amount Payable",
|
||||||
@@ -2150,6 +2165,7 @@
|
|||||||
"accounting-payments": "Payments",
|
"accounting-payments": "Payments",
|
||||||
"accounting-receivables": "Receivables",
|
"accounting-receivables": "Receivables",
|
||||||
"activejobs": "Active Jobs",
|
"activejobs": "Active Jobs",
|
||||||
|
"all_tasks": "All Tasks",
|
||||||
"alljobs": "All Jobs",
|
"alljobs": "All Jobs",
|
||||||
"allpayments": "All Payments",
|
"allpayments": "All Payments",
|
||||||
"availablejobs": "Available Jobs",
|
"availablejobs": "Available Jobs",
|
||||||
@@ -2158,6 +2174,7 @@
|
|||||||
"courtesycars-all": "All Courtesy Cars",
|
"courtesycars-all": "All Courtesy Cars",
|
||||||
"courtesycars-contracts": "Contracts",
|
"courtesycars-contracts": "Contracts",
|
||||||
"courtesycars-newcontract": "New Contract",
|
"courtesycars-newcontract": "New Contract",
|
||||||
|
"create_task": "Create Task",
|
||||||
"customers": "Customers",
|
"customers": "Customers",
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"enterbills": "Enter Bills",
|
"enterbills": "Enter Bills",
|
||||||
@@ -2170,6 +2187,7 @@
|
|||||||
"home": "Home",
|
"home": "Home",
|
||||||
"inventory": "Inventory",
|
"inventory": "Inventory",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
|
"my_tasks": "My Tasks",
|
||||||
"newjob": "Create New Job",
|
"newjob": "Create New Job",
|
||||||
"owners": "Owners",
|
"owners": "Owners",
|
||||||
"parts-queue": "Parts Queue",
|
"parts-queue": "Parts Queue",
|
||||||
@@ -2196,6 +2214,7 @@
|
|||||||
"shop_csi": "CSI",
|
"shop_csi": "CSI",
|
||||||
"shop_templates": "Templates",
|
"shop_templates": "Templates",
|
||||||
"shop_vendors": "Vendors",
|
"shop_vendors": "Vendors",
|
||||||
|
"tasks": "Tasks",
|
||||||
"temporarydocs": "Temporary Documents",
|
"temporarydocs": "Temporary Documents",
|
||||||
"timetickets": "Time Tickets",
|
"timetickets": "Time Tickets",
|
||||||
"ttapprovals": "Time Ticket Approvals",
|
"ttapprovals": "Time Ticket Approvals",
|
||||||
@@ -2389,6 +2408,7 @@
|
|||||||
"percent_accepted": "% Accepted"
|
"percent_accepted": "% Accepted"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"notyetdispatched": "This part has not been dispatched.",
|
||||||
"parts_dispatch": "Parts Dispatch"
|
"parts_dispatch": "Parts Dispatch"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2505,6 +2525,7 @@
|
|||||||
"markexported": "Payment(s) marked exported.",
|
"markexported": "Payment(s) marked exported.",
|
||||||
"markreexported": "Payment marked for re-export successfully",
|
"markreexported": "Payment marked for re-export successfully",
|
||||||
"payment": "Payment created successfully. ",
|
"payment": "Payment created successfully. ",
|
||||||
|
"paymentupdate": "Payment updated successfully. ",
|
||||||
"stripe": "Credit card transaction charged successfully."
|
"stripe": "Credit card transaction charged successfully."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2606,6 +2627,7 @@
|
|||||||
"job_costing_ro": "Job Costing",
|
"job_costing_ro": "Job Costing",
|
||||||
"job_lifecycle_ro": "Job Lifecycle",
|
"job_lifecycle_ro": "Job Lifecycle",
|
||||||
"job_notes": "Job Notes",
|
"job_notes": "Job Notes",
|
||||||
|
"job_tasks": "Job Tasks",
|
||||||
"key_tag": "Key Tag",
|
"key_tag": "Key Tag",
|
||||||
"labels": {
|
"labels": {
|
||||||
"count": "Count",
|
"count": "Count",
|
||||||
@@ -2806,6 +2828,7 @@
|
|||||||
"parts_orders": "Parts Orders",
|
"parts_orders": "Parts Orders",
|
||||||
"payments": "Payments",
|
"payments": "Payments",
|
||||||
"scoreboard": "Scoreboard",
|
"scoreboard": "Scoreboard",
|
||||||
|
"tasks": "Tasks",
|
||||||
"timetickets": "Timetickets"
|
"timetickets": "Timetickets"
|
||||||
},
|
},
|
||||||
"vendor": "Vendor"
|
"vendor": "Vendor"
|
||||||
@@ -2921,6 +2944,8 @@
|
|||||||
"scoreboard_detail": "Scoreboard Detail",
|
"scoreboard_detail": "Scoreboard Detail",
|
||||||
"scoreboard_summary": "Scoreboard Summary",
|
"scoreboard_summary": "Scoreboard Summary",
|
||||||
"supplement_ratio_ins_co": "Supplement Ratio by Source",
|
"supplement_ratio_ins_co": "Supplement Ratio by Source",
|
||||||
|
"tasks_date": "Tasks by Date",
|
||||||
|
"tasks_date_employee": "Employee Tasks by Date",
|
||||||
"thank_you_date": "Thank You Letters",
|
"thank_you_date": "Thank You Letters",
|
||||||
"timetickets": "Time Tickets",
|
"timetickets": "Time Tickets",
|
||||||
"timetickets_employee": "Employee Time Tickets",
|
"timetickets_employee": "Employee Time Tickets",
|
||||||
@@ -3000,6 +3025,92 @@
|
|||||||
"updated": "Scoreboard updated."
|
"updated": "Scoreboard updated."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tasks": {
|
||||||
|
"actions": {
|
||||||
|
"edit": "Edit Task",
|
||||||
|
"new": "New Task"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"allTasks": "All",
|
||||||
|
"complete": "Toggle Complete",
|
||||||
|
"create": "Create Task",
|
||||||
|
"delete": "Toggle Delete",
|
||||||
|
"edit": "Edit",
|
||||||
|
"myTasks": "Mine",
|
||||||
|
"refresh": "Refresh"
|
||||||
|
},
|
||||||
|
"date_presets": {
|
||||||
|
"completion": "Completion",
|
||||||
|
"day": "Day",
|
||||||
|
"days": "Days",
|
||||||
|
"delivery": "Delivery",
|
||||||
|
"next_week": "Next Week",
|
||||||
|
"one_month": "One Month",
|
||||||
|
"three_months": "Three Months",
|
||||||
|
"three_weeks": "Three Weeks",
|
||||||
|
"today": "Today",
|
||||||
|
"tomorrow": "Tomorrow",
|
||||||
|
"two_weeks": "Two Weeks"
|
||||||
|
},
|
||||||
|
"failures": {
|
||||||
|
"completed": "Failed to toggle Task completion.",
|
||||||
|
"created": "Failed to create Task.",
|
||||||
|
"deleted": "Failed to toggle Task deletion.",
|
||||||
|
"updated": "Failed to update Task."
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"actions": "Actions",
|
||||||
|
"assigned_to": "Assigned To",
|
||||||
|
"bill": "Bill",
|
||||||
|
"billid": "Bill",
|
||||||
|
"completed": "Completed",
|
||||||
|
"created_at": "Created At",
|
||||||
|
"description": "Description",
|
||||||
|
"due_date": "Due Date",
|
||||||
|
"job": {
|
||||||
|
"ro_number": "RO #"
|
||||||
|
},
|
||||||
|
"jobid": "Job",
|
||||||
|
"jobline": "Job Line",
|
||||||
|
"joblineid": "Job Line",
|
||||||
|
"parts_order": "Parts Order",
|
||||||
|
"partsorderid": "Parts Order",
|
||||||
|
"priorities": {
|
||||||
|
"high": "High",
|
||||||
|
"low": "Low",
|
||||||
|
"medium": "Medium"
|
||||||
|
},
|
||||||
|
"priority": "Priority",
|
||||||
|
"remind_at": "Remind At",
|
||||||
|
"title": "Title"
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"assigned_to": "Select an Employee",
|
||||||
|
"billid": "Select a Bill",
|
||||||
|
"description": "Enter a description",
|
||||||
|
"jobid": "Select a Job",
|
||||||
|
"joblineid": "Select a Job Line",
|
||||||
|
"partsorderid": "Select a Parts Order"
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"completed": "Toggled Task completion successfully.",
|
||||||
|
"created": "Task created successfully.",
|
||||||
|
"deleted": "Toggled Task deletion successfully.",
|
||||||
|
"updated": "Task updated successfully."
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"all_tasks": "All Tasks",
|
||||||
|
"completed": "Completed Tasks",
|
||||||
|
"deleted": "Deleted Tasks",
|
||||||
|
"job_tasks": "Job Tasks",
|
||||||
|
"mine": "My Tasks",
|
||||||
|
"my_tasks": "My Tasks"
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"due_at_error_message": "The due date must be in the future!",
|
||||||
|
"remind_at_error_message": "The reminder date and time must be at least 30 minutes in the future!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "Employee ID",
|
"employeeid": "Employee ID",
|
||||||
@@ -3104,11 +3215,13 @@
|
|||||||
"accounting-payables": "Payables | {{app}}",
|
"accounting-payables": "Payables | {{app}}",
|
||||||
"accounting-payments": "Payments | {{app}}",
|
"accounting-payments": "Payments | {{app}}",
|
||||||
"accounting-receivables": "Receivables | {{app}}",
|
"accounting-receivables": "Receivables | {{app}}",
|
||||||
|
"all_tasks": "All Tasks",
|
||||||
"app": "",
|
"app": "",
|
||||||
"bc": {
|
"bc": {
|
||||||
"accounting-payables": "Payables",
|
"accounting-payables": "Payables",
|
||||||
"accounting-payments": "Payments",
|
"accounting-payments": "Payments",
|
||||||
"accounting-receivables": "Receivables",
|
"accounting-receivables": "Receivables",
|
||||||
|
"all_tasks": "All Tasks",
|
||||||
"availablejobs": "Available Jobs",
|
"availablejobs": "Available Jobs",
|
||||||
"bills-list": "Bills",
|
"bills-list": "Bills",
|
||||||
"contracts": "Contracts",
|
"contracts": "Contracts",
|
||||||
@@ -3132,6 +3245,7 @@
|
|||||||
"jobs-intake": "Intake",
|
"jobs-intake": "Intake",
|
||||||
"jobs-new": "Create a New Job",
|
"jobs-new": "Create a New Job",
|
||||||
"jobs-ready": "Ready Jobs",
|
"jobs-ready": "Ready Jobs",
|
||||||
|
"my_tasks": "My Tasks",
|
||||||
"owner-detail": "{{name}}",
|
"owner-detail": "{{name}}",
|
||||||
"owners": "Owners",
|
"owners": "Owners",
|
||||||
"parts-queue": "Parts Queue",
|
"parts-queue": "Parts Queue",
|
||||||
@@ -3146,6 +3260,7 @@
|
|||||||
"shop-csi": "CSI Responses",
|
"shop-csi": "CSI Responses",
|
||||||
"shop-templates": "Shop Templates",
|
"shop-templates": "Shop Templates",
|
||||||
"shop-vendors": "Vendors",
|
"shop-vendors": "Vendors",
|
||||||
|
"tasks": "Tasks",
|
||||||
"temporarydocs": "Temporary Documents",
|
"temporarydocs": "Temporary Documents",
|
||||||
"timetickets": "Time Tickets",
|
"timetickets": "Time Tickets",
|
||||||
"ttapprovals": "Time Ticket Approvals",
|
"ttapprovals": "Time Ticket Approvals",
|
||||||
@@ -3176,6 +3291,7 @@
|
|||||||
"jobsdetail": "Job {{ro_number}} | {{app}}",
|
"jobsdetail": "Job {{ro_number}} | {{app}}",
|
||||||
"jobsdocuments": "Job Documents {{ro_number}} | {{app}}",
|
"jobsdocuments": "Job Documents {{ro_number}} | {{app}}",
|
||||||
"manageroot": "Home | {{app}}",
|
"manageroot": "Home | {{app}}",
|
||||||
|
"my_tasks": "My Tasks",
|
||||||
"owners": "All Owners | {{app}}",
|
"owners": "All Owners | {{app}}",
|
||||||
"owners-detail": "{{name}} | {{app}}",
|
"owners-detail": "{{name}} | {{app}}",
|
||||||
"parts-queue": "Parts Queue | {{app}}",
|
"parts-queue": "Parts Queue | {{app}}",
|
||||||
@@ -3195,6 +3311,7 @@
|
|||||||
"shop-csi": "CSI Responses | {{app}}",
|
"shop-csi": "CSI Responses | {{app}}",
|
||||||
"shop-templates": "Shop Templates | {{app}}",
|
"shop-templates": "Shop Templates | {{app}}",
|
||||||
"shop_vendors": "Vendors | {{app}}",
|
"shop_vendors": "Vendors | {{app}}",
|
||||||
|
"tasks": "Tasks",
|
||||||
"techconsole": "Technician Console | {{app}}",
|
"techconsole": "Technician Console | {{app}}",
|
||||||
"techjobclock": "Technician Job Clock | {{app}}",
|
"techjobclock": "Technician Job Clock | {{app}}",
|
||||||
"techjoblookup": "Technician Job Lookup | {{app}}",
|
"techjoblookup": "Technician Job Lookup | {{app}}",
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
"jobassignmentchange": "",
|
"jobassignmentchange": "",
|
||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
|
"jobclosedwithbypass": "",
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
"jobdelivery": "",
|
"jobdelivery": "",
|
||||||
"jobexported": "",
|
"jobexported": "",
|
||||||
@@ -134,7 +135,13 @@
|
|||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": "",
|
"jobsupplement": "",
|
||||||
"jobsuspend": "",
|
"jobsuspend": "",
|
||||||
"jobvoid": ""
|
"jobvoid": "",
|
||||||
|
"tasks_completed": "",
|
||||||
|
"tasks_created": "",
|
||||||
|
"tasks_deleted": "",
|
||||||
|
"tasks_uncompleted": "",
|
||||||
|
"tasks_undeleted": "",
|
||||||
|
"tasks_updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -223,10 +230,12 @@
|
|||||||
"markexported": "",
|
"markexported": "",
|
||||||
"markforreexport": "",
|
"markforreexport": "",
|
||||||
"new": "",
|
"new": "",
|
||||||
|
"nobilllines": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"onlycmforinvoiced": "",
|
"onlycmforinvoiced": "",
|
||||||
"printlabels": "",
|
"printlabels": "",
|
||||||
"retailtotal": "",
|
"retailtotal": "",
|
||||||
|
"returnfrombill": "",
|
||||||
"savewithdiscrepancy": "",
|
"savewithdiscrepancy": "",
|
||||||
"state_tax": "",
|
"state_tax": "",
|
||||||
"subtotal": "",
|
"subtotal": "",
|
||||||
@@ -860,7 +869,8 @@
|
|||||||
"inservice": "",
|
"inservice": "",
|
||||||
"leasereturn": "",
|
"leasereturn": "",
|
||||||
"out": "",
|
"out": "",
|
||||||
"sold": ""
|
"sold": "",
|
||||||
|
"unavailable": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"saved": ""
|
"saved": ""
|
||||||
@@ -930,7 +940,8 @@
|
|||||||
"scheduledindate": "",
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
"scheduledoutdate": "",
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": "",
|
||||||
|
"tasks": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -1125,6 +1136,7 @@
|
|||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
"deleteall": "",
|
"deleteall": "",
|
||||||
"deselectall": "",
|
"deselectall": "",
|
||||||
|
"download": "",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"login": "",
|
"login": "",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -1350,6 +1362,7 @@
|
|||||||
"amount": "",
|
"amount": "",
|
||||||
"dateOfPayment": "",
|
"dateOfPayment": "",
|
||||||
"descriptions": "",
|
"descriptions": "",
|
||||||
|
"hint": "",
|
||||||
"payer": "",
|
"payer": "",
|
||||||
"payername": "",
|
"payername": "",
|
||||||
"paymentid": "",
|
"paymentid": "",
|
||||||
@@ -1540,6 +1553,7 @@
|
|||||||
"voiding": ""
|
"voiding": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"active_tasks": "",
|
||||||
"actual_completion": "Realización real",
|
"actual_completion": "Realización real",
|
||||||
"actual_delivery": "Entrega real",
|
"actual_delivery": "Entrega real",
|
||||||
"actual_in": "Real en",
|
"actual_in": "Real en",
|
||||||
@@ -2003,6 +2017,7 @@
|
|||||||
"ppc": "",
|
"ppc": "",
|
||||||
"ppdnotexported": "",
|
"ppdnotexported": "",
|
||||||
"profileadjustments": "",
|
"profileadjustments": "",
|
||||||
|
"profitbypassrequired": "",
|
||||||
"profits": "",
|
"profits": "",
|
||||||
"prt_dsmk_total": "",
|
"prt_dsmk_total": "",
|
||||||
"rates": "Tarifas",
|
"rates": "Tarifas",
|
||||||
@@ -2024,12 +2039,12 @@
|
|||||||
"remove_from_ar": "",
|
"remove_from_ar": "",
|
||||||
"returntotals": "",
|
"returntotals": "",
|
||||||
"ro_guard": {
|
"ro_guard": {
|
||||||
"eforce_profit": "",
|
|
||||||
"enforce_ar": "",
|
"enforce_ar": "",
|
||||||
"enforce_bills": "",
|
"enforce_bills": "",
|
||||||
"enforce_cm": "",
|
"enforce_cm": "",
|
||||||
"enforce_labor": "",
|
"enforce_labor": "",
|
||||||
"enforce_ppd": "",
|
"enforce_ppd": "",
|
||||||
|
"enforce_profit": "",
|
||||||
"enforce_sublet": "",
|
"enforce_sublet": "",
|
||||||
"enforce_validation": "",
|
"enforce_validation": "",
|
||||||
"enforced": ""
|
"enforced": ""
|
||||||
@@ -2052,6 +2067,7 @@
|
|||||||
"supplementnote": "",
|
"supplementnote": "",
|
||||||
"suspended": "",
|
"suspended": "",
|
||||||
"suspense": "",
|
"suspense": "",
|
||||||
|
"tasks": "",
|
||||||
"threshhold": "",
|
"threshhold": "",
|
||||||
"total_cost": "",
|
"total_cost": "",
|
||||||
"total_cust_payable": "",
|
"total_cust_payable": "",
|
||||||
@@ -2149,6 +2165,7 @@
|
|||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
"activejobs": "Empleos activos",
|
"activejobs": "Empleos activos",
|
||||||
|
"all_tasks": "",
|
||||||
"alljobs": "",
|
"alljobs": "",
|
||||||
"allpayments": "",
|
"allpayments": "",
|
||||||
"availablejobs": "Trabajos disponibles",
|
"availablejobs": "Trabajos disponibles",
|
||||||
@@ -2157,6 +2174,7 @@
|
|||||||
"courtesycars-all": "",
|
"courtesycars-all": "",
|
||||||
"courtesycars-contracts": "",
|
"courtesycars-contracts": "",
|
||||||
"courtesycars-newcontract": "",
|
"courtesycars-newcontract": "",
|
||||||
|
"create_task": "",
|
||||||
"customers": "Clientes",
|
"customers": "Clientes",
|
||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"enterbills": "",
|
"enterbills": "",
|
||||||
@@ -2169,6 +2187,7 @@
|
|||||||
"home": "Casa",
|
"home": "Casa",
|
||||||
"inventory": "",
|
"inventory": "",
|
||||||
"jobs": "Trabajos",
|
"jobs": "Trabajos",
|
||||||
|
"my_tasks": "",
|
||||||
"newjob": "",
|
"newjob": "",
|
||||||
"owners": "propietarios",
|
"owners": "propietarios",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -2195,6 +2214,7 @@
|
|||||||
"shop_csi": "",
|
"shop_csi": "",
|
||||||
"shop_templates": "",
|
"shop_templates": "",
|
||||||
"shop_vendors": "Vendedores",
|
"shop_vendors": "Vendedores",
|
||||||
|
"tasks": "",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
@@ -2388,6 +2408,7 @@
|
|||||||
"percent_accepted": ""
|
"percent_accepted": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"notyetdispatched": "",
|
||||||
"parts_dispatch": ""
|
"parts_dispatch": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2504,6 +2525,7 @@
|
|||||||
"markexported": "",
|
"markexported": "",
|
||||||
"markreexported": "",
|
"markreexported": "",
|
||||||
"payment": "",
|
"payment": "",
|
||||||
|
"paymentupdate": "",
|
||||||
"stripe": ""
|
"stripe": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2605,6 +2627,7 @@
|
|||||||
"job_costing_ro": "",
|
"job_costing_ro": "",
|
||||||
"job_lifecycle_ro": "",
|
"job_lifecycle_ro": "",
|
||||||
"job_notes": "",
|
"job_notes": "",
|
||||||
|
"job_tasks": "",
|
||||||
"key_tag": "",
|
"key_tag": "",
|
||||||
"labels": {
|
"labels": {
|
||||||
"count": "",
|
"count": "",
|
||||||
@@ -2805,6 +2828,7 @@
|
|||||||
"parts_orders": "",
|
"parts_orders": "",
|
||||||
"payments": "",
|
"payments": "",
|
||||||
"scoreboard": "",
|
"scoreboard": "",
|
||||||
|
"tasks": "",
|
||||||
"timetickets": ""
|
"timetickets": ""
|
||||||
},
|
},
|
||||||
"vendor": ""
|
"vendor": ""
|
||||||
@@ -2920,6 +2944,8 @@
|
|||||||
"scoreboard_detail": "",
|
"scoreboard_detail": "",
|
||||||
"scoreboard_summary": "",
|
"scoreboard_summary": "",
|
||||||
"supplement_ratio_ins_co": "",
|
"supplement_ratio_ins_co": "",
|
||||||
|
"tasks_date": "",
|
||||||
|
"tasks_date_employee": "",
|
||||||
"thank_you_date": "",
|
"thank_you_date": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timetickets_employee": "",
|
"timetickets_employee": "",
|
||||||
@@ -2999,6 +3025,92 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tasks": {
|
||||||
|
"actions": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"allTasks": "",
|
||||||
|
"complete": "",
|
||||||
|
"create": "",
|
||||||
|
"delete": "",
|
||||||
|
"edit": "",
|
||||||
|
"myTasks": "",
|
||||||
|
"refresh": ""
|
||||||
|
},
|
||||||
|
"date_presets": {
|
||||||
|
"completion": "",
|
||||||
|
"day": "",
|
||||||
|
"days": "",
|
||||||
|
"delivery": "",
|
||||||
|
"next_week": "",
|
||||||
|
"one_month": "",
|
||||||
|
"three_months": "",
|
||||||
|
"three_weeks": "",
|
||||||
|
"today": "",
|
||||||
|
"tomorrow": "",
|
||||||
|
"two_weeks": ""
|
||||||
|
},
|
||||||
|
"failures": {
|
||||||
|
"completed": "",
|
||||||
|
"created": "",
|
||||||
|
"deleted": "",
|
||||||
|
"updated": ""
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"actions": "",
|
||||||
|
"assigned_to": "",
|
||||||
|
"bill": "",
|
||||||
|
"billid": "",
|
||||||
|
"completed": "",
|
||||||
|
"created_at": "",
|
||||||
|
"description": "",
|
||||||
|
"due_date": "",
|
||||||
|
"job": {
|
||||||
|
"ro_number": ""
|
||||||
|
},
|
||||||
|
"jobid": "",
|
||||||
|
"jobline": "",
|
||||||
|
"joblineid": "",
|
||||||
|
"parts_order": "",
|
||||||
|
"partsorderid": "",
|
||||||
|
"priorities": {
|
||||||
|
"high": "",
|
||||||
|
"low": "",
|
||||||
|
"medium": ""
|
||||||
|
},
|
||||||
|
"priority": "",
|
||||||
|
"remind_at": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"assigned_to": "",
|
||||||
|
"billid": "",
|
||||||
|
"description": "",
|
||||||
|
"jobid": "",
|
||||||
|
"joblineid": "",
|
||||||
|
"partsorderid": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"completed": "",
|
||||||
|
"created": "",
|
||||||
|
"deleted": "",
|
||||||
|
"updated": ""
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"all_tasks": "",
|
||||||
|
"completed": "",
|
||||||
|
"deleted": "",
|
||||||
|
"job_tasks": "",
|
||||||
|
"mine": "",
|
||||||
|
"my_tasks": ""
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"due_at_error_message": "",
|
||||||
|
"remind_at_error_message": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
@@ -3103,11 +3215,13 @@
|
|||||||
"accounting-payables": "",
|
"accounting-payables": "",
|
||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
|
"all_tasks": "",
|
||||||
"app": "",
|
"app": "",
|
||||||
"bc": {
|
"bc": {
|
||||||
"accounting-payables": "",
|
"accounting-payables": "",
|
||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
|
"all_tasks": "",
|
||||||
"availablejobs": "",
|
"availablejobs": "",
|
||||||
"bills-list": "",
|
"bills-list": "",
|
||||||
"contracts": "",
|
"contracts": "",
|
||||||
@@ -3131,6 +3245,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobs-new": "",
|
"jobs-new": "",
|
||||||
"jobs-ready": "",
|
"jobs-ready": "",
|
||||||
|
"my_tasks": "",
|
||||||
"owner-detail": "",
|
"owner-detail": "",
|
||||||
"owners": "",
|
"owners": "",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -3145,6 +3260,7 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop-vendors": "",
|
"shop-vendors": "",
|
||||||
|
"tasks": "",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
@@ -3175,6 +3291,7 @@
|
|||||||
"jobsdetail": "Trabajo {{ro_number}} | {{app}}",
|
"jobsdetail": "Trabajo {{ro_number}} | {{app}}",
|
||||||
"jobsdocuments": "Documentos de trabajo {{ro_number}} | {{app}}",
|
"jobsdocuments": "Documentos de trabajo {{ro_number}} | {{app}}",
|
||||||
"manageroot": "Casa | {{app}}",
|
"manageroot": "Casa | {{app}}",
|
||||||
|
"my_tasks": "",
|
||||||
"owners": "Todos los propietarios | {{app}}",
|
"owners": "Todos los propietarios | {{app}}",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -3194,6 +3311,7 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendedores | {{app}}",
|
"shop_vendors": "Vendedores | {{app}}",
|
||||||
|
"tasks": "",
|
||||||
"techconsole": "{{app}}",
|
"techconsole": "{{app}}",
|
||||||
"techjobclock": "{{app}}",
|
"techjobclock": "{{app}}",
|
||||||
"techjoblookup": "{{app}}",
|
"techjoblookup": "{{app}}",
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
"jobassignmentchange": "",
|
"jobassignmentchange": "",
|
||||||
"jobassignmentremoved": "",
|
"jobassignmentremoved": "",
|
||||||
"jobchecklist": "",
|
"jobchecklist": "",
|
||||||
|
"jobclosedwithbypass": "",
|
||||||
"jobconverted": "",
|
"jobconverted": "",
|
||||||
"jobdelivery": "",
|
"jobdelivery": "",
|
||||||
"jobexported": "",
|
"jobexported": "",
|
||||||
@@ -134,7 +135,13 @@
|
|||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": "",
|
"jobsupplement": "",
|
||||||
"jobsuspend": "",
|
"jobsuspend": "",
|
||||||
"jobvoid": ""
|
"jobvoid": "",
|
||||||
|
"tasks_completed": "",
|
||||||
|
"tasks_created": "",
|
||||||
|
"tasks_deleted": "",
|
||||||
|
"tasks_uncompleted": "",
|
||||||
|
"tasks_undeleted": "",
|
||||||
|
"tasks_updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
@@ -223,10 +230,12 @@
|
|||||||
"markexported": "",
|
"markexported": "",
|
||||||
"markforreexport": "",
|
"markforreexport": "",
|
||||||
"new": "",
|
"new": "",
|
||||||
|
"nobilllines": "",
|
||||||
"noneselected": "",
|
"noneselected": "",
|
||||||
"onlycmforinvoiced": "",
|
"onlycmforinvoiced": "",
|
||||||
"printlabels": "",
|
"printlabels": "",
|
||||||
"retailtotal": "",
|
"retailtotal": "",
|
||||||
|
"returnfrombill": "",
|
||||||
"savewithdiscrepancy": "",
|
"savewithdiscrepancy": "",
|
||||||
"state_tax": "",
|
"state_tax": "",
|
||||||
"subtotal": "",
|
"subtotal": "",
|
||||||
@@ -860,7 +869,8 @@
|
|||||||
"inservice": "",
|
"inservice": "",
|
||||||
"leasereturn": "",
|
"leasereturn": "",
|
||||||
"out": "",
|
"out": "",
|
||||||
"sold": ""
|
"sold": "",
|
||||||
|
"unavailable": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"saved": ""
|
"saved": ""
|
||||||
@@ -930,7 +940,8 @@
|
|||||||
"scheduledindate": "",
|
"scheduledindate": "",
|
||||||
"scheduledintoday": "",
|
"scheduledintoday": "",
|
||||||
"scheduledoutdate": "",
|
"scheduledoutdate": "",
|
||||||
"scheduledouttoday": ""
|
"scheduledouttoday": "",
|
||||||
|
"tasks": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
@@ -1125,6 +1136,7 @@
|
|||||||
"delete": "Effacer",
|
"delete": "Effacer",
|
||||||
"deleteall": "",
|
"deleteall": "",
|
||||||
"deselectall": "",
|
"deselectall": "",
|
||||||
|
"download": "",
|
||||||
"edit": "modifier",
|
"edit": "modifier",
|
||||||
"login": "",
|
"login": "",
|
||||||
"print": "",
|
"print": "",
|
||||||
@@ -1350,6 +1362,7 @@
|
|||||||
"amount": "",
|
"amount": "",
|
||||||
"dateOfPayment": "",
|
"dateOfPayment": "",
|
||||||
"descriptions": "",
|
"descriptions": "",
|
||||||
|
"hint": "",
|
||||||
"payer": "",
|
"payer": "",
|
||||||
"payername": "",
|
"payername": "",
|
||||||
"paymentid": "",
|
"paymentid": "",
|
||||||
@@ -1540,6 +1553,7 @@
|
|||||||
"voiding": ""
|
"voiding": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"active_tasks": "",
|
||||||
"actual_completion": "Achèvement réel",
|
"actual_completion": "Achèvement réel",
|
||||||
"actual_delivery": "Livraison réelle",
|
"actual_delivery": "Livraison réelle",
|
||||||
"actual_in": "En réel",
|
"actual_in": "En réel",
|
||||||
@@ -2003,6 +2017,7 @@
|
|||||||
"ppc": "",
|
"ppc": "",
|
||||||
"ppdnotexported": "",
|
"ppdnotexported": "",
|
||||||
"profileadjustments": "",
|
"profileadjustments": "",
|
||||||
|
"profitbypassrequired": "",
|
||||||
"profits": "",
|
"profits": "",
|
||||||
"prt_dsmk_total": "",
|
"prt_dsmk_total": "",
|
||||||
"rates": "Les taux",
|
"rates": "Les taux",
|
||||||
@@ -2024,12 +2039,12 @@
|
|||||||
"remove_from_ar": "",
|
"remove_from_ar": "",
|
||||||
"returntotals": "",
|
"returntotals": "",
|
||||||
"ro_guard": {
|
"ro_guard": {
|
||||||
"eforce_profit": "",
|
|
||||||
"enforce_ar": "",
|
"enforce_ar": "",
|
||||||
"enforce_bills": "",
|
"enforce_bills": "",
|
||||||
"enforce_cm": "",
|
"enforce_cm": "",
|
||||||
"enforce_labor": "",
|
"enforce_labor": "",
|
||||||
"enforce_ppd": "",
|
"enforce_ppd": "",
|
||||||
|
"enforce_profit": "",
|
||||||
"enforce_sublet": "",
|
"enforce_sublet": "",
|
||||||
"enforce_validation": "",
|
"enforce_validation": "",
|
||||||
"enforced": ""
|
"enforced": ""
|
||||||
@@ -2052,6 +2067,7 @@
|
|||||||
"supplementnote": "",
|
"supplementnote": "",
|
||||||
"suspended": "",
|
"suspended": "",
|
||||||
"suspense": "",
|
"suspense": "",
|
||||||
|
"tasks": "",
|
||||||
"threshhold": "",
|
"threshhold": "",
|
||||||
"total_cost": "",
|
"total_cost": "",
|
||||||
"total_cust_payable": "",
|
"total_cust_payable": "",
|
||||||
@@ -2149,6 +2165,7 @@
|
|||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
"activejobs": "Emplois actifs",
|
"activejobs": "Emplois actifs",
|
||||||
|
"all_tasks": "",
|
||||||
"alljobs": "",
|
"alljobs": "",
|
||||||
"allpayments": "",
|
"allpayments": "",
|
||||||
"availablejobs": "Emplois disponibles",
|
"availablejobs": "Emplois disponibles",
|
||||||
@@ -2157,6 +2174,7 @@
|
|||||||
"courtesycars-all": "",
|
"courtesycars-all": "",
|
||||||
"courtesycars-contracts": "",
|
"courtesycars-contracts": "",
|
||||||
"courtesycars-newcontract": "",
|
"courtesycars-newcontract": "",
|
||||||
|
"create_task": "",
|
||||||
"customers": "Les clients",
|
"customers": "Les clients",
|
||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"enterbills": "",
|
"enterbills": "",
|
||||||
@@ -2169,6 +2187,7 @@
|
|||||||
"home": "Accueil",
|
"home": "Accueil",
|
||||||
"inventory": "",
|
"inventory": "",
|
||||||
"jobs": "Emplois",
|
"jobs": "Emplois",
|
||||||
|
"my_tasks": "",
|
||||||
"newjob": "",
|
"newjob": "",
|
||||||
"owners": "Propriétaires",
|
"owners": "Propriétaires",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -2195,6 +2214,7 @@
|
|||||||
"shop_csi": "",
|
"shop_csi": "",
|
||||||
"shop_templates": "",
|
"shop_templates": "",
|
||||||
"shop_vendors": "Vendeurs",
|
"shop_vendors": "Vendeurs",
|
||||||
|
"tasks": "",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
@@ -2388,6 +2408,7 @@
|
|||||||
"percent_accepted": ""
|
"percent_accepted": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"notyetdispatched": "",
|
||||||
"parts_dispatch": ""
|
"parts_dispatch": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2504,6 +2525,7 @@
|
|||||||
"markexported": "",
|
"markexported": "",
|
||||||
"markreexported": "",
|
"markreexported": "",
|
||||||
"payment": "",
|
"payment": "",
|
||||||
|
"paymentupdate": "",
|
||||||
"stripe": ""
|
"stripe": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2605,6 +2627,7 @@
|
|||||||
"job_costing_ro": "",
|
"job_costing_ro": "",
|
||||||
"job_lifecycle_ro": "",
|
"job_lifecycle_ro": "",
|
||||||
"job_notes": "",
|
"job_notes": "",
|
||||||
|
"job_tasks": "",
|
||||||
"key_tag": "",
|
"key_tag": "",
|
||||||
"labels": {
|
"labels": {
|
||||||
"count": "",
|
"count": "",
|
||||||
@@ -2805,6 +2828,7 @@
|
|||||||
"parts_orders": "",
|
"parts_orders": "",
|
||||||
"payments": "",
|
"payments": "",
|
||||||
"scoreboard": "",
|
"scoreboard": "",
|
||||||
|
"tasks": "",
|
||||||
"timetickets": ""
|
"timetickets": ""
|
||||||
},
|
},
|
||||||
"vendor": ""
|
"vendor": ""
|
||||||
@@ -2920,6 +2944,8 @@
|
|||||||
"scoreboard_detail": "",
|
"scoreboard_detail": "",
|
||||||
"scoreboard_summary": "",
|
"scoreboard_summary": "",
|
||||||
"supplement_ratio_ins_co": "",
|
"supplement_ratio_ins_co": "",
|
||||||
|
"tasks_date": "",
|
||||||
|
"tasks_date_employee": "",
|
||||||
"thank_you_date": "",
|
"thank_you_date": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"timetickets_employee": "",
|
"timetickets_employee": "",
|
||||||
@@ -2999,6 +3025,92 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tasks": {
|
||||||
|
"actions": {
|
||||||
|
"edit": "",
|
||||||
|
"new": ""
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"allTasks": "",
|
||||||
|
"complete": "",
|
||||||
|
"create": "",
|
||||||
|
"delete": "",
|
||||||
|
"edit": "",
|
||||||
|
"myTasks": "",
|
||||||
|
"refresh": ""
|
||||||
|
},
|
||||||
|
"date_presets": {
|
||||||
|
"completion": "",
|
||||||
|
"day": "",
|
||||||
|
"days": "",
|
||||||
|
"delivery": "",
|
||||||
|
"next_week": "",
|
||||||
|
"one_month": "",
|
||||||
|
"three_months": "",
|
||||||
|
"three_weeks": "",
|
||||||
|
"today": "",
|
||||||
|
"tomorrow": "",
|
||||||
|
"two_weeks": ""
|
||||||
|
},
|
||||||
|
"failures": {
|
||||||
|
"completed": "",
|
||||||
|
"created": "",
|
||||||
|
"deleted": "",
|
||||||
|
"updated": ""
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"actions": "",
|
||||||
|
"assigned_to": "",
|
||||||
|
"bill": "",
|
||||||
|
"billid": "",
|
||||||
|
"completed": "",
|
||||||
|
"created_at": "",
|
||||||
|
"description": "",
|
||||||
|
"due_date": "",
|
||||||
|
"job": {
|
||||||
|
"ro_number": ""
|
||||||
|
},
|
||||||
|
"jobid": "",
|
||||||
|
"jobline": "",
|
||||||
|
"joblineid": "",
|
||||||
|
"parts_order": "",
|
||||||
|
"partsorderid": "",
|
||||||
|
"priorities": {
|
||||||
|
"high": "",
|
||||||
|
"low": "",
|
||||||
|
"medium": ""
|
||||||
|
},
|
||||||
|
"priority": "",
|
||||||
|
"remind_at": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
|
"placeholders": {
|
||||||
|
"assigned_to": "",
|
||||||
|
"billid": "",
|
||||||
|
"description": "",
|
||||||
|
"jobid": "",
|
||||||
|
"joblineid": "",
|
||||||
|
"partsorderid": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"completed": "",
|
||||||
|
"created": "",
|
||||||
|
"deleted": "",
|
||||||
|
"updated": ""
|
||||||
|
},
|
||||||
|
"titles": {
|
||||||
|
"all_tasks": "",
|
||||||
|
"completed": "",
|
||||||
|
"deleted": "",
|
||||||
|
"job_tasks": "",
|
||||||
|
"mine": "",
|
||||||
|
"my_tasks": ""
|
||||||
|
},
|
||||||
|
"validation": {
|
||||||
|
"due_at_error_message": "",
|
||||||
|
"remind_at_error_message": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
@@ -3103,11 +3215,13 @@
|
|||||||
"accounting-payables": "",
|
"accounting-payables": "",
|
||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
|
"all_tasks": "",
|
||||||
"app": "",
|
"app": "",
|
||||||
"bc": {
|
"bc": {
|
||||||
"accounting-payables": "",
|
"accounting-payables": "",
|
||||||
"accounting-payments": "",
|
"accounting-payments": "",
|
||||||
"accounting-receivables": "",
|
"accounting-receivables": "",
|
||||||
|
"all_tasks": "",
|
||||||
"availablejobs": "",
|
"availablejobs": "",
|
||||||
"bills-list": "",
|
"bills-list": "",
|
||||||
"contracts": "",
|
"contracts": "",
|
||||||
@@ -3131,6 +3245,7 @@
|
|||||||
"jobs-intake": "",
|
"jobs-intake": "",
|
||||||
"jobs-new": "",
|
"jobs-new": "",
|
||||||
"jobs-ready": "",
|
"jobs-ready": "",
|
||||||
|
"my_tasks": "",
|
||||||
"owner-detail": "",
|
"owner-detail": "",
|
||||||
"owners": "",
|
"owners": "",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -3145,6 +3260,7 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop-vendors": "",
|
"shop-vendors": "",
|
||||||
|
"tasks": "",
|
||||||
"temporarydocs": "",
|
"temporarydocs": "",
|
||||||
"timetickets": "",
|
"timetickets": "",
|
||||||
"ttapprovals": "",
|
"ttapprovals": "",
|
||||||
@@ -3175,6 +3291,7 @@
|
|||||||
"jobsdetail": "Travail {{ro_number}} | {{app}}",
|
"jobsdetail": "Travail {{ro_number}} | {{app}}",
|
||||||
"jobsdocuments": "Documents de travail {{ro_number}} | {{app}}",
|
"jobsdocuments": "Documents de travail {{ro_number}} | {{app}}",
|
||||||
"manageroot": "Accueil | {{app}}",
|
"manageroot": "Accueil | {{app}}",
|
||||||
|
"my_tasks": "",
|
||||||
"owners": "Tous les propriétaires | {{app}}",
|
"owners": "Tous les propriétaires | {{app}}",
|
||||||
"owners-detail": "",
|
"owners-detail": "",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
@@ -3194,6 +3311,7 @@
|
|||||||
"shop-csi": "",
|
"shop-csi": "",
|
||||||
"shop-templates": "",
|
"shop-templates": "",
|
||||||
"shop_vendors": "Vendeurs | {{app}}",
|
"shop_vendors": "Vendeurs | {{app}}",
|
||||||
|
"tasks": "",
|
||||||
"techconsole": "{{app}}",
|
"techconsole": "{{app}}",
|
||||||
"techjobclock": "{{app}}",
|
"techjobclock": "{{app}}",
|
||||||
"techjoblookup": "{{app}}",
|
"techjoblookup": "{{app}}",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const AuditTrailMapping = {
|
|||||||
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
|
||||||
jobinproductionchange: (inproduction) => i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
jobinproductionchange: (inproduction) => i18n.t("audit_trail.messages.jobinproductionchange", { inproduction }),
|
||||||
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
jobinvoiced: () => i18n.t("audit_trail.messages.jobinvoiced"),
|
||||||
|
jobclosedwithbypass: () => i18n.t("audit_trail.messages.jobclosedwithbypass"),
|
||||||
jobmodifylbradj: ({ mod_lbr_ty, hours }) => i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
jobmodifylbradj: ({ mod_lbr_ty, hours }) => i18n.t("audit_trail.messages.jobmodifylbradj", { mod_lbr_ty, hours }),
|
||||||
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
|
||||||
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
|
||||||
@@ -39,7 +40,38 @@ const AuditTrailMapping = {
|
|||||||
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||||
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
||||||
jobvoid: () => i18n.t("audit_trail.messages.jobvoid")
|
jobvoid: () => i18n.t("audit_trail.messages.jobvoid"),
|
||||||
|
// Tasks Entries
|
||||||
|
tasksCreated: (title, createdBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_created", {
|
||||||
|
title,
|
||||||
|
createdBy
|
||||||
|
}),
|
||||||
|
tasksUpdated: (title, updatedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_updated", {
|
||||||
|
title,
|
||||||
|
updatedBy
|
||||||
|
}),
|
||||||
|
tasksDeleted: (title, deletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_deleted", {
|
||||||
|
title,
|
||||||
|
deletedBy
|
||||||
|
}),
|
||||||
|
tasksUndeleted: (title, undeletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_undeleted", {
|
||||||
|
title,
|
||||||
|
undeletedBy
|
||||||
|
}),
|
||||||
|
tasksCompleted: (title, completedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_completed", {
|
||||||
|
title,
|
||||||
|
completedBy
|
||||||
|
}),
|
||||||
|
tasksUncompleted: (title, uncompletedBy) =>
|
||||||
|
i18n.t("audit_trail.messages.tasks_uncompleted", {
|
||||||
|
title,
|
||||||
|
uncompletedBy
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AuditTrailMapping;
|
export default AuditTrailMapping;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { auth } from "../firebase/firebase.utils";
|
import { auth } from "../firebase/firebase.utils";
|
||||||
|
import InstanceRenderManager from "./instanceRenderMgr";
|
||||||
|
|
||||||
axios.defaults.baseURL =
|
axios.defaults.baseURL =
|
||||||
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
||||||
@@ -12,6 +13,15 @@ export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
|||||||
if (token) {
|
if (token) {
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
InstanceRenderManager({
|
||||||
|
executeFunction: true,
|
||||||
|
args: [],
|
||||||
|
promanager: () => {
|
||||||
|
if (!config.url.startsWith("http://localhost:1337")) {
|
||||||
|
config.headers["Convenient-Company"] = "promanager";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import client from "../utils/GraphQLClient";
|
|||||||
import cleanAxios from "./CleanAxios";
|
import cleanAxios from "./CleanAxios";
|
||||||
import { TemplateList } from "./TemplateConstants";
|
import { TemplateList } from "./TemplateConstants";
|
||||||
import { generateTemplate } from "./graphQLmodifier";
|
import { generateTemplate } from "./graphQLmodifier";
|
||||||
|
import InstanceRenderManager from "./instanceRenderMgr";
|
||||||
const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL;
|
const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL;
|
||||||
|
|
||||||
jsreport.serverUrl = server;
|
jsreport.serverUrl = server;
|
||||||
@@ -71,8 +72,8 @@ export default async function RenderTemplate(
|
|||||||
...contextData,
|
...contextData,
|
||||||
...templateObject.variables,
|
...templateObject.variables,
|
||||||
...templateObject.context,
|
...templateObject.context,
|
||||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
headerpath: `/${InstanceRenderManager({ imex: bodyshop.imexshopid, rome: bodyshop.imexshopid, promanager: "GENERIC" })}/header.html`,
|
||||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
footerpath: `/${InstanceRenderManager({ imex: bodyshop.imexshopid, rome: bodyshop.imexshopid, promanager: "GENERIC" })}/footer.html`,
|
||||||
bodyshop: bodyshop,
|
bodyshop: bodyshop,
|
||||||
filters: templateObject?.filters,
|
filters: templateObject?.filters,
|
||||||
sorters: templateObject?.sorters,
|
sorters: templateObject?.sorters,
|
||||||
|
|||||||
@@ -587,6 +587,14 @@ export const TemplateList = (type, context) => {
|
|||||||
key: "job_lifecycle_ro",
|
key: "job_lifecycle_ro",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
group: "post"
|
group: "post"
|
||||||
|
},
|
||||||
|
job_tasks: {
|
||||||
|
title: i18n.t("printcenter.jobs.job_tasks"),
|
||||||
|
description: "",
|
||||||
|
subject: i18n.t("printcenter.jobs.job_tasks"),
|
||||||
|
key: "job_tasks",
|
||||||
|
disabled: false,
|
||||||
|
group: "ro"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
@@ -2089,6 +2097,30 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("jobs.fields.date_invoiced")
|
field: i18n.t("jobs.fields.date_invoiced")
|
||||||
},
|
},
|
||||||
group: "jobs"
|
group: "jobs"
|
||||||
|
},
|
||||||
|
tasks_date: {
|
||||||
|
title: i18n.t("reportcenter.templates.tasks_date"),
|
||||||
|
subject: i18n.t("reportcenter.templates.tasks_date"),
|
||||||
|
key: "tasks_date",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.tasks"),
|
||||||
|
field: i18n.t("tasks.fields.created_at")
|
||||||
|
},
|
||||||
|
group: "jobs"
|
||||||
|
},
|
||||||
|
tasks_date_employee: {
|
||||||
|
title: i18n.t("reportcenter.templates.tasks_date_employee"),
|
||||||
|
subject: i18n.t("reportcenter.templates.tasks_date_employee"),
|
||||||
|
key: "tasks_date_employee",
|
||||||
|
idtype: "employee",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.tasks"),
|
||||||
|
field: i18n.t("tasks.fields.created_at")
|
||||||
|
},
|
||||||
|
group: "jobs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default async function FcmHandler({ client, payload }) {
|
export default async function FcmHandler({ client, payload }) {
|
||||||
|
console.log("FCM", payload);
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case "messaging-inbound":
|
case "messaging-inbound":
|
||||||
client.cache.modify({
|
client.cache.modify({
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Replaces undefined values with null in an object.
|
||||||
|
* Optionally, you can specify keys to replace.
|
||||||
|
* If keys are specified, only those keys will be replaced.
|
||||||
|
* If no keys are specified, all undefined values will be replaced.
|
||||||
|
* @param obj
|
||||||
|
* @param keys
|
||||||
|
* @returns {*}
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
export default function UndefinedToNull(obj, keys) {
|
export default function UndefinedToNull(obj, keys) {
|
||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
if (keys && keys.indexOf(key) >= 0) {
|
if (keys && keys.indexOf(key) >= 0) {
|
||||||
@@ -8,3 +18,21 @@ export default function UndefinedToNull(obj, keys) {
|
|||||||
});
|
});
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces undefined values with null in an object. Optionally, you can specify keys to replace. If keys are specified, only those keys will be replaced. If no keys are specified, all undefined values will be replaced.
|
||||||
|
* @param obj
|
||||||
|
* @param keys
|
||||||
|
* @returns {{[p: string]: unknown}}
|
||||||
|
*/
|
||||||
|
export function replaceUndefinedWithNull(obj, keys) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(obj).map(([key, value]) => {
|
||||||
|
if (keys) {
|
||||||
|
return [key, keys.includes(key) && value === undefined ? null : value];
|
||||||
|
} else {
|
||||||
|
return [key, value === undefined ? null : value];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import * as path from "path";
|
|||||||
import * as url from "url";
|
import * as url from "url";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { ViteEjsPlugin } from "vite-plugin-ejs";
|
import { ViteEjsPlugin } from "vite-plugin-ejs";
|
||||||
|
import eslint from 'vite-plugin-eslint';
|
||||||
|
|
||||||
//import CompressionPlugin from 'vite-plugin-compression';
|
//import CompressionPlugin from 'vite-plugin-compression';
|
||||||
import { VitePWA } from "vite-plugin-pwa";
|
import { VitePWA } from "vite-plugin-pwa";
|
||||||
import InstanceRenderManager from "./src/utils/instanceRenderMgr";
|
import InstanceRenderManager from "./src/utils/instanceRenderMgr";
|
||||||
@@ -100,7 +102,8 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
reactVirtualized(),
|
reactVirtualized(),
|
||||||
react()
|
react(),
|
||||||
|
eslint(),
|
||||||
// CompressionPlugin(), //Cloudfront already compresses assets, so not needed.
|
// CompressionPlugin(), //Cloudfront already compresses assets, so not needed.
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
actions: [ ]
|
actions: []
|
||||||
custom_types:
|
custom_types:
|
||||||
enums: [ ]
|
enums: []
|
||||||
input_objects: [ ]
|
input_objects: []
|
||||||
objects: [ ]
|
objects: []
|
||||||
scalars: [ ]
|
scalars: []
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[ ]
|
[]
|
||||||
|
|||||||
@@ -1 +1,8 @@
|
|||||||
[ ]
|
- name: Task Reminders
|
||||||
|
webhook: '{{HASURA_API_URL}}/tasks-remind-handler'
|
||||||
|
schedule: '*/15 * * * *'
|
||||||
|
include_in_metadata: true
|
||||||
|
payload: {}
|
||||||
|
headers:
|
||||||
|
- name: event-secret
|
||||||
|
value_from_env: EVENT_SECRET
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{ }
|
{}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[ ]
|
[]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[ ]
|
[]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user