Compare commits
97 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
646754732d | ||
|
|
efc1157653 | ||
|
|
a5d3f2caf1 | ||
|
|
4ad87a522c | ||
|
|
145cf7cc93 | ||
|
|
cdb2d4d2d6 | ||
|
|
29f0031c1e | ||
|
|
e3059b41ae | ||
|
|
2a33f462a3 | ||
|
|
cbc164dbeb | ||
|
|
6382fdf19c | ||
|
|
9287e6608d | ||
|
|
d221763064 | ||
|
|
b39a5b755e | ||
|
|
449330441a | ||
|
|
fcab5e6ef2 | ||
|
|
0212b837ea | ||
|
|
e7438a099e | ||
|
|
b3303e3c38 | ||
|
|
c69c86d193 | ||
|
|
73ec8b8a70 | ||
|
|
af09796df8 | ||
|
|
954504de8d | ||
|
|
0aba040338 | ||
|
|
c3bfe87674 | ||
|
|
9aa1279144 | ||
|
|
4e6c45b195 | ||
|
|
4fdb939bd2 | ||
|
|
062a1dcc72 | ||
|
|
7b420b1855 | ||
|
|
40f61bbc8f | ||
|
|
f5d821c394 | ||
|
|
3958ec9189 | ||
|
|
1e4f52e541 | ||
|
|
5cc5cb444e | ||
|
|
4acf0c59ca | ||
|
|
2858a5e871 | ||
|
|
24496d3ee1 | ||
|
|
0a5df69b12 | ||
|
|
80efea02c6 | ||
|
|
9f5c282b41 | ||
|
|
b2602c3385 | ||
|
|
0e584af424 | ||
|
|
cdc3de2a33 | ||
|
|
3bfa556b02 | ||
|
|
44cb7577e2 | ||
|
|
46d2b08477 | ||
|
|
0193ff9e65 | ||
|
|
fd9a51209f | ||
|
|
d0a7b87e04 | ||
|
|
799b24c90e | ||
|
|
3e1a8c87d1 | ||
|
|
c886d874de | ||
|
|
4dfb020089 | ||
|
|
bc6f05acbc | ||
|
|
2701bbd501 | ||
|
|
1f2040d97c | ||
|
|
43963a3e91 | ||
|
|
4287311adb | ||
|
|
d0e8589a76 | ||
|
|
c4bab72947 | ||
|
|
aa4b4998fa | ||
|
|
ed4566e00f | ||
|
|
5c2cdfe16c | ||
|
|
12c75357b5 | ||
|
|
d40f3ee45a | ||
|
|
96a0def846 | ||
|
|
1fd595d0de | ||
|
|
4d9be1d232 | ||
|
|
fb2bc20b4f | ||
|
|
744593e96a | ||
|
|
1e9308be9b | ||
|
|
411605e121 | ||
|
|
1da8d6abb3 | ||
|
|
cdcef798df | ||
|
|
f7207a9f3f | ||
|
|
7a54b55bd4 | ||
|
|
991dfc2ad5 | ||
|
|
718c8291a8 | ||
|
|
f1e84c348b | ||
|
|
2a2d399a98 | ||
|
|
5f513a8bef | ||
|
|
4b96d5a707 | ||
|
|
220f3d4410 | ||
|
|
841f62bd84 | ||
|
|
f3f16b78d5 | ||
|
|
91e2e7931b | ||
|
|
1e855799f8 | ||
|
|
3c6faf8473 | ||
|
|
9deb2964a5 | ||
|
|
9cf9f8b844 | ||
|
|
ad46ea74c0 | ||
|
|
2a28855e4b | ||
|
|
8d25f60097 | ||
|
|
982a51f16e | ||
|
|
68d02648d7 | ||
|
|
89d5b1cfe4 |
@@ -5,6 +5,7 @@ orbs:
|
|||||||
aws-s3: circleci/aws-s3@4.0.0
|
aws-s3: circleci/aws-s3@4.0.0
|
||||||
aws-cli: circleci/aws-cli@4.0
|
aws-cli: circleci/aws-cli@4.0
|
||||||
eb: circleci/aws-elastic-beanstalk@2.0.1
|
eb: circleci/aws-elastic-beanstalk@2.0.1
|
||||||
|
jira: circleci/jira@2.1.0
|
||||||
jobs:
|
jobs:
|
||||||
imex-api-deploy:
|
imex-api-deploy:
|
||||||
docker:
|
docker:
|
||||||
@@ -18,6 +19,12 @@ jobs:
|
|||||||
eb status --verbose
|
eb status --verbose
|
||||||
eb deploy
|
eb deploy
|
||||||
eb status
|
eb status
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (ImEX) - API
|
||||||
|
environment_type: production
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
imex-hasura-migrate:
|
imex-hasura-migrate:
|
||||||
docker:
|
docker:
|
||||||
@@ -33,11 +40,16 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Execute migration
|
name: Execute migration
|
||||||
command: |
|
command: |
|
||||||
npm install hasura-cli -g
|
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
|
||||||
hasura migrate apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
hasura migrate apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
||||||
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
||||||
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (ImEX) - Hasura
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
imex-app-build:
|
imex-app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.18.2
|
- image: cimg/node:18.18.2
|
||||||
@@ -62,6 +74,7 @@ jobs:
|
|||||||
to: "s3://imex-online-production/"
|
to: "s3://imex-online-production/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
|
||||||
|
|
||||||
imex-app-beta-build:
|
imex-app-beta-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.18.2
|
- image: cimg/node:18.18.2
|
||||||
@@ -86,6 +99,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://imex-online-beta/"
|
to: "s3://imex-online-beta/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (ImEX) - Front End
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
rome-api-deploy:
|
rome-api-deploy:
|
||||||
docker:
|
docker:
|
||||||
@@ -99,7 +118,12 @@ jobs:
|
|||||||
eb status --verbose
|
eb status --verbose
|
||||||
eb deploy
|
eb deploy
|
||||||
eb status
|
eb status
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (Rome) - API
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
rome-hasura-migrate:
|
rome-hasura-migrate:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.18.2
|
- image: cimg/node:18.18.2
|
||||||
@@ -114,11 +138,16 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Execute migration
|
name: Execute migration
|
||||||
command: |
|
command: |
|
||||||
npm install hasura-cli -g
|
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
|
||||||
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (Rome) - Hasura
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
rome-app-build:
|
rome-app-build:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/node:18.18.2
|
- image: cimg/node:18.18.2
|
||||||
@@ -143,6 +172,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://rome-online-production/"
|
to: "s3://rome-online-production/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (Rome) - Front End
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
promanager-app-build:
|
promanager-app-build:
|
||||||
docker:
|
docker:
|
||||||
@@ -168,6 +203,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://promanager-production/"
|
to: "s3://promanager-production/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Production (ProManager) - Front End
|
||||||
|
environment_type: production
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
test-rome-hasura-migrate:
|
test-rome-hasura-migrate:
|
||||||
docker:
|
docker:
|
||||||
@@ -183,10 +224,18 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Execute migration
|
name: Execute migration
|
||||||
command: |
|
command: |
|
||||||
npm install hasura-cli -g
|
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
|
||||||
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
sleep 5
|
||||||
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
sleep 10
|
||||||
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
|
||||||
|
- jira/notify:
|
||||||
|
environment: Test (Rome) - Hasura
|
||||||
|
environment_type: testing
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
test-rome-app-build:
|
test-rome-app-build:
|
||||||
docker:
|
docker:
|
||||||
@@ -212,6 +261,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://rome-online-test/"
|
to: "s3://rome-online-test/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Test (Rome) - Front End
|
||||||
|
environment_type: testing
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
test-promanager-app-build:
|
test-promanager-app-build:
|
||||||
docker:
|
docker:
|
||||||
@@ -237,6 +292,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://promanager-testing/"
|
to: "s3://promanager-testing/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Test (ProManager) - Front End
|
||||||
|
environment_type: testing
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
test-hasura-migrate:
|
test-hasura-migrate:
|
||||||
docker:
|
docker:
|
||||||
@@ -252,10 +313,18 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Execute migration
|
name: Execute migration
|
||||||
command: |
|
command: |
|
||||||
npm install hasura-cli -g
|
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
|
||||||
hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||||
|
sleep 15
|
||||||
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||||
|
sleep 30
|
||||||
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
|
||||||
|
- jira/notify:
|
||||||
|
environment: Test (ImEX) - Hasura
|
||||||
|
environment_type: testing
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
imex-test-app-build:
|
imex-test-app-build:
|
||||||
docker:
|
docker:
|
||||||
@@ -302,7 +371,12 @@ jobs:
|
|||||||
from: dist
|
from: dist
|
||||||
to: "s3://imex-online-test-beta/"
|
to: "s3://imex-online-test-beta/"
|
||||||
arguments: "--exclude '*.map'"
|
arguments: "--exclude '*.map'"
|
||||||
|
- jira/notify:
|
||||||
|
environment: Test (ImEX) - Front End
|
||||||
|
environment_type: testing
|
||||||
|
pipeline_id: << pipeline.id >>
|
||||||
|
job_type: deployment
|
||||||
|
pipeline_number: << pipeline.number >>
|
||||||
|
|
||||||
admin-app-build:
|
admin-app-build:
|
||||||
docker:
|
docker:
|
||||||
@@ -353,7 +427,7 @@ workflows:
|
|||||||
secret: ${HASURA_PROD_SECRET}
|
secret: ${HASURA_PROD_SECRET}
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master
|
only: master-AIO
|
||||||
- rome-api-deploy:
|
- rome-api-deploy:
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
@@ -363,7 +437,7 @@ workflows:
|
|||||||
branches:
|
branches:
|
||||||
only: master-AIO
|
only: master-AIO
|
||||||
- rome-hasura-migrate:
|
- rome-hasura-migrate:
|
||||||
secret: ${HASURA_PROD_SECRET}
|
secret: ${HASURA_ROME_PROD_SECRET}
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only: master-AIO
|
only: master-AIO
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
|
||||||
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
|
||||||
VITE_APP_GA_CODE=231099835
|
VITE_APP_GA_CODE=231099835
|
||||||
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_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
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
|
||||||
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
|
||||||
VITE_APP_GA_CODE=231099835
|
VITE_APP_GA_CODE=231099835
|
||||||
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_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
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
|
||||||
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
|
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/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
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="Pragma" content="no-cache">
|
||||||
|
<meta http-equiv="Expires" content="0">
|
||||||
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
|
<% if (env.VITE_APP_INSTANCE === 'IMEX') { %>
|
||||||
<link rel="icon" href="/favicon.png"/>
|
<link rel="icon" href="/favicon.png"/>
|
||||||
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
|
<% } %> <% if (env.VITE_APP_INSTANCE === 'ROME') { %>
|
||||||
|
|||||||
4
client/package-lock.json
generated
4
client/package-lock.json
generated
@@ -109,7 +109,8 @@
|
|||||||
"vite-plugin-legacy": "^2.1.0",
|
"vite-plugin-legacy": "^2.1.0",
|
||||||
"vite-plugin-node-polyfills": "^0.22.0",
|
"vite-plugin-node-polyfills": "^0.22.0",
|
||||||
"vite-plugin-pwa": "^0.20.1",
|
"vite-plugin-pwa": "^0.20.1",
|
||||||
"vite-plugin-style-import": "^2.0.0"
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
|
"workbox-window": "^7.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18.2"
|
"node": ">=18.18.2"
|
||||||
@@ -18429,6 +18430,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.1.0.tgz",
|
||||||
"integrity": "sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==",
|
"integrity": "sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/trusted-types": "^2.0.2",
|
"@types/trusted-types": "^2.0.2",
|
||||||
"workbox-core": "7.1.0"
|
"workbox-core": "7.1.0"
|
||||||
|
|||||||
@@ -153,6 +153,7 @@
|
|||||||
"vite-plugin-legacy": "^2.1.0",
|
"vite-plugin-legacy": "^2.1.0",
|
||||||
"vite-plugin-node-polyfills": "^0.22.0",
|
"vite-plugin-node-polyfills": "^0.22.0",
|
||||||
"vite-plugin-pwa": "^0.20.1",
|
"vite-plugin-pwa": "^0.20.1",
|
||||||
"vite-plugin-style-import": "^2.0.0"
|
"vite-plugin-style-import": "^2.0.0",
|
||||||
|
"workbox-window": "^7.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { checkUserSession } from "../redux/user/user.actions";
|
|||||||
import { selectBodyshop, selectCurrentEula, selectCurrentUser } from "../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentEula, selectCurrentUser } from "../redux/user/user.selectors";
|
||||||
import PrivateRoute from "../components/PrivateRoute";
|
import PrivateRoute from "../components/PrivateRoute";
|
||||||
import "./App.styles.scss";
|
import "./App.styles.scss";
|
||||||
import handleBeta from "../utils/handleBeta";
|
|
||||||
import Eula from "../components/eula/eula.component";
|
import Eula from "../components/eula/eula.component";
|
||||||
import InstanceRenderMgr from "../utils/instanceRenderMgr";
|
import InstanceRenderMgr from "../utils/instanceRenderMgr";
|
||||||
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
|
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
|
||||||
@@ -108,8 +107,6 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
|
|||||||
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBeta();
|
|
||||||
|
|
||||||
if (!online) {
|
if (!online) {
|
||||||
return (
|
return (
|
||||||
<Result
|
<Result
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
|
|||||||
});
|
});
|
||||||
|
|
||||||
billlines.forEach((billline) => {
|
billlines.forEach((billline) => {
|
||||||
const { deductedfromlbr, inventories, jobline, ...il } = billline;
|
const { deductedfromlbr, inventories, jobline, original_actual_price, create_ppc, ...il } = billline;
|
||||||
delete il.__typename;
|
delete il.__typename;
|
||||||
|
|
||||||
if (il.id) {
|
if (il.id) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled, CopyFilled } from "@ant-design/icons";
|
||||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, notification } from "antd";
|
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, message, notification } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -14,10 +14,12 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||||
|
import { getCurrentUser } from "../../firebase/firebase.utils";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
cardPaymentModal: selectCardPayment,
|
cardPaymentModal: selectCardPayment,
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: getCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -25,11 +27,17 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
|
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
|
||||||
});
|
});
|
||||||
|
|
||||||
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
const CardPaymentModalComponent = ({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
cardPaymentModal,
|
||||||
|
toggleModalVisible,
|
||||||
|
insertAuditTrail
|
||||||
|
}) => {
|
||||||
const { context, actions } = cardPaymentModal;
|
const { context, actions } = cardPaymentModal;
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [paymentLink, setPaymentLink] = useState();
|
||||||
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);
|
||||||
@@ -51,8 +59,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
||||||
//Add a slight delay to allow the refetch to properly get the data.
|
//Add a slight delay to allow the refetch to properly get the data.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (actions && actions.refetch && typeof actions.refetch === "function")
|
if (actions && actions.refetch && typeof actions.refetch === "function") actions.refetch();
|
||||||
actions.refetch();
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
}, 750);
|
}, 750);
|
||||||
@@ -86,7 +93,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleIntelliPayCharge = async () => {
|
const handleIntelliPayCharge = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
//Validate
|
//Validate
|
||||||
@@ -101,7 +107,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
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(),
|
paymentSplitMeta: form.getFieldsValue()
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.intellipay) {
|
if (window.intellipay) {
|
||||||
@@ -126,6 +132,42 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleIntelliPayChargeShortLink = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
//Validate
|
||||||
|
try {
|
||||||
|
await form.validateFields();
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { payments } = form.getFieldsValue();
|
||||||
|
const response = await axios.post("/intellipay/generate_payment_url", {
|
||||||
|
bodyshop,
|
||||||
|
amount: payments?.reduce((acc, val) => {
|
||||||
|
return acc + (val?.amount || 0);
|
||||||
|
}, 0),
|
||||||
|
account: payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null,
|
||||||
|
comment: btoa(JSON.stringify({ payments, userEmail: currentUser.email })),
|
||||||
|
paymentSplitMeta: form.getFieldsValue()
|
||||||
|
});
|
||||||
|
if (response.data) {
|
||||||
|
setPaymentLink(response.data?.shorUrl);
|
||||||
|
navigator.clipboard.writeText(response.data?.shorUrl);
|
||||||
|
message.success(t("general.actions.copied"));
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("job_payments.notifications.error.openingip")
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Card Payment">
|
<Card title="Card Payment">
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
@@ -208,10 +250,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
{() => {
|
{() => {
|
||||||
//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 (
|
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
|
||||||
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) });
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -246,7 +285,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
||||||
return acc + (val?.amount || 0);
|
return acc + (val?.amount || 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space style={{ float: "right" }}>
|
<Space style={{ float: "right" }}>
|
||||||
<Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
|
<Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
|
||||||
@@ -273,11 +311,36 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
>
|
>
|
||||||
{t("job_payments.buttons.proceedtopayment")}
|
{t("job_payments.buttons.proceedtopayment")}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Space direction="vertical" align="center">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
// data-ipayname="submit"
|
||||||
|
className="ipayfield"
|
||||||
|
loading={queryLoading || loading}
|
||||||
|
disabled={!(totalAmountToCharge > 0)}
|
||||||
|
onClick={handleIntelliPayChargeShortLink}
|
||||||
|
>
|
||||||
|
{t("job_payments.buttons.create_short_link")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
{paymentLink && (
|
||||||
|
<Space
|
||||||
|
style={{ cursor: "pointer", float: "right" }}
|
||||||
|
align="end"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(paymentLink);
|
||||||
|
message.success(t("general.actions.copied"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{paymentLink}</div>
|
||||||
|
<CopyFilled />
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
</Spin>
|
</Spin>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import Icon, {
|
|||||||
FileFilled,
|
FileFilled,
|
||||||
HomeFilled,
|
HomeFilled,
|
||||||
ImportOutlined,
|
ImportOutlined,
|
||||||
InfoCircleOutlined,
|
|
||||||
LineChartOutlined,
|
LineChartOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
PhoneOutlined,
|
PhoneOutlined,
|
||||||
@@ -27,8 +26,8 @@ import Icon, {
|
|||||||
UserOutlined
|
UserOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Layout, Menu, Switch, Tooltip } from "antd";
|
import { Layout, Menu } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React 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, FaTasks } from "react-icons/fa";
|
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||||
@@ -43,7 +42,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 { checkBeta, handleBeta, setBeta } from "../../utils/handleBeta";
|
|
||||||
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";
|
||||||
|
|
||||||
@@ -115,20 +113,22 @@ function Header({
|
|||||||
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
names: ["ImEXPay", "DmsAp", "Simple_Inventory"],
|
||||||
splitKey: bodyshop && bodyshop.imexshopid
|
splitKey: bodyshop && bodyshop.imexshopid
|
||||||
});
|
});
|
||||||
const [betaSwitch, setBetaSwitch] = useState(false);
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
const deleteBetaCookie = () => {
|
||||||
const isBeta = checkBeta();
|
const cookieExists = document.cookie.split("; ").some((row) => row.startsWith(`betaSwitchImex=`));
|
||||||
setBetaSwitch(isBeta);
|
if (cookieExists) {
|
||||||
}, []);
|
const domain = window.location.hostname.split(".").slice(-2).join(".");
|
||||||
|
document.cookie = `betaSwitchImex=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.${domain}`;
|
||||||
const betaSwitchChange = (checked) => {
|
console.log(`betaSwitchImex cookie deleted`);
|
||||||
setBeta(checked);
|
} else {
|
||||||
setBetaSwitch(checked);
|
console.log(`betaSwitchImex cookie does not exist`);
|
||||||
handleBeta();
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
deleteBetaCookie();
|
||||||
|
|
||||||
const accountingChildren = [];
|
const accountingChildren = [];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -695,31 +695,6 @@ function Header({
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
InstanceRenderManager({
|
|
||||||
executeFunction: true,
|
|
||||||
args: [],
|
|
||||||
imex: () => {
|
|
||||||
menuItems.push({
|
|
||||||
key: "beta-switch",
|
|
||||||
id: "header-beta-switch",
|
|
||||||
style: { marginLeft: "auto" },
|
|
||||||
label: (
|
|
||||||
<Tooltip
|
|
||||||
title={`A more modern ${InstanceRenderManager({
|
|
||||||
imex: t("titles.imexonline"),
|
|
||||||
rome: t("titles.romeonline"),
|
|
||||||
promanager: t("titles.promanager")
|
|
||||||
})} is ready for you to try! You can switch back at any time.`}
|
|
||||||
>
|
|
||||||
<InfoCircleOutlined />
|
|
||||||
<span style={{ marginRight: 8 }}>Try the new app</span>
|
|
||||||
<Switch checked={betaSwitch} onChange={betaSwitchChange} />
|
|
||||||
</Tooltip>
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Header>
|
<Layout.Header>
|
||||||
<Menu
|
<Menu
|
||||||
|
|||||||
@@ -141,10 +141,14 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
|||||||
key: t("jobs.fields.ded_amt"),
|
key: t("jobs.fields.ded_amt"),
|
||||||
total: job.job_totals.totals.custPayable.deductible
|
total: job.job_totals.totals.custPayable.deductible
|
||||||
},
|
},
|
||||||
// {
|
...(InstanceRenderManager({
|
||||||
// key: t("jobs.fields.federal_tax_payable"),
|
imex: [{
|
||||||
// total: job.job_totals.totals.custPayable.federal_tax,
|
key: t("jobs.fields.federal_tax_payable"),
|
||||||
// },
|
total: job.job_totals.totals.custPayable.federal_tax
|
||||||
|
}],
|
||||||
|
rome: [],
|
||||||
|
promanager: "USE_ROME"
|
||||||
|
})),
|
||||||
{
|
{
|
||||||
key: t("jobs.fields.other_amount_payable"),
|
key: t("jobs.fields.other_amount_payable"),
|
||||||
total: job.job_totals.totals.custPayable.other_customer_amount
|
total: job.job_totals.totals.custPayable.other_customer_amount
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export default function PartsOrderModalPriceChange({ form, field }) {
|
|||||||
key: "25",
|
key: "25",
|
||||||
label: t("parts_orders.labels.discount", { percent: "25%" })
|
label: t("parts_orders.labels.discount", { percent: "25%" })
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "40",
|
||||||
|
label: t("parts_orders.labels.discount", { percent: "40%" })
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "custom",
|
key: "custom",
|
||||||
label: (
|
label: (
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||||
@@ -20,7 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(PaymentsGenerateLink);
|
export default connect(mapStateToProps, mapDispatchToProps)(PaymentsGenerateLink);
|
||||||
|
|
||||||
export function PaymentsGenerateLink({ bodyshop, callback, job, openChatByPhone, setMessage }) {
|
export function PaymentsGenerateLink({ bodyshop, currentUser, callback, job, openChatByPhone, setMessage }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
@@ -30,29 +31,35 @@ export function PaymentsGenerateLink({ bodyshop, callback, job, openChatByPhone,
|
|||||||
|
|
||||||
const handleFinish = async ({ amount }) => {
|
const handleFinish = async ({ amount }) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
let p;
|
||||||
const p = parsePhoneNumber(job.ownr_ph1, "CA");
|
try {
|
||||||
|
p = parsePhoneNumber(job.ownr_ph1 || "", "CA");
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Unable to parse phone number");
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await axios.post("/intellipay/generate_payment_url", {
|
const response = await axios.post("/intellipay/generate_payment_url", {
|
||||||
bodyshop,
|
bodyshop,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
account: job.ro_number,
|
account: job.ro_number,
|
||||||
invoice: job.id
|
comment: btoa(JSON.stringify({ payments: [{ jobid: job.id, amount }], userEmail: currentUser.email }))
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setPaymentLink(response.data.shorUrl);
|
setPaymentLink(response.data.shorUrl);
|
||||||
|
|
||||||
openChatByPhone({
|
if (p) {
|
||||||
phone_num: p.formatInternational(),
|
openChatByPhone({
|
||||||
jobid: job.id
|
phone_num: p.formatInternational(),
|
||||||
});
|
jobid: job.id
|
||||||
setMessage(
|
});
|
||||||
t("payments.labels.smspaymentreminder", {
|
setMessage(
|
||||||
shopname: bodyshop.shopname,
|
t("payments.labels.smspaymentreminder", {
|
||||||
amount: amount,
|
shopname: bodyshop.shopname,
|
||||||
payment_link: response.data.shorUrl
|
amount: amount,
|
||||||
})
|
payment_link: response.data.shorUrl
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//Add in confirmation & errors.
|
//Add in confirmation & errors.
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import {
|
|||||||
PauseCircleOutlined
|
PauseCircleOutlined
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space, Tooltip } from "antd";
|
import { Card, Col, Row, Space, Tooltip } from "antd";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import Dinero from "dinero.js";
|
|
||||||
|
|
||||||
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
|
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
|
||||||
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
|
||||||
@@ -18,8 +18,8 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ
|
|||||||
|
|
||||||
import dayjs from "../../utils/day";
|
import dayjs from "../../utils/day";
|
||||||
|
|
||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
|
||||||
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||||
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
|
|
||||||
const cardColor = (ssbuckets, totalHrs) => {
|
const cardColor = (ssbuckets, totalHrs) => {
|
||||||
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
|
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
|
||||||
@@ -213,21 +213,13 @@ const EstimatorToolTip = ({ metadata, cardSettings }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
|
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
|
||||||
const amount = metadata?.job_totals?.totals?.subtotal?.amount;
|
const dineroAmount = Dinero(metadata?.job_totals?.totals?.subtotal ?? Dinero()).toFormat();
|
||||||
const dineroAmount = amount ? Dinero({ amount: parseInt(amount * 100) }).toFormat("0,0.00") : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
cardSettings?.subtotal && (
|
cardSettings?.subtotal && (
|
||||||
<Col span={cardSettings.compact ? 24 : 12}>
|
<Col span={cardSettings.compact ? 24 : 12}>
|
||||||
<EllipsesToolTip
|
<EllipsesToolTip title={`${dineroAmount}`} kiosk={cardSettings.kiosk}>
|
||||||
title={!!amount ? `${t("production.statistics.currency_symbol")}${dineroAmount}` : null}
|
{dineroAmount}
|
||||||
kiosk={cardSettings.kiosk}
|
|
||||||
>
|
|
||||||
{!!amount ? (
|
|
||||||
<span>{`${t("production.statistics.currency_symbol")}${dineroAmount}`}</span>
|
|
||||||
) : (
|
|
||||||
<span> </span>
|
|
||||||
)}
|
|
||||||
</EllipsesToolTip>
|
</EllipsesToolTip>
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Card, Statistic } from "antd";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { defaultKanbanSettings, statisticsItems } from "./settings/defaultKanbanSettings.js";
|
import { defaultKanbanSettings, statisticsItems } from "./settings/defaultKanbanSettings.js";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
|
|
||||||
export const StatisticType = {
|
export const StatisticType = {
|
||||||
HOURS: "hours",
|
HOURS: "hours",
|
||||||
@@ -32,7 +33,21 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const calculateTotalAmount = (items, key) => {
|
const calculateTotalAmount = (items, key) => {
|
||||||
return items.reduce((acc, item) => acc + (item[key]?.totals?.subtotal?.amount || 0), 0);
|
return items.reduce((acc, item) => acc.add(Dinero(item[key]?.totals?.subtotal ?? Dinero())), Dinero({ amount: 0 }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculateReducerTotalAmount = (lanes, key) => {
|
||||||
|
return lanes.reduce(
|
||||||
|
(acc, lane) => {
|
||||||
|
return acc.add(
|
||||||
|
lane.cards.reduce(
|
||||||
|
(laneAcc, card) => laneAcc.add(Dinero(card.metadata[key]?.totals?.subtotal ?? Dinero())),
|
||||||
|
Dinero({ amount: 0 })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
Dinero({ amount: 0 })
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateReducerTotal = (lanes, key, subKey) => {
|
const calculateReducerTotal = (lanes, key, subKey) => {
|
||||||
@@ -43,14 +58,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateReducerTotalAmount = (lanes, key) => {
|
|
||||||
return lanes.reduce((acc, lane) => {
|
|
||||||
return (
|
|
||||||
acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata[key]?.totals?.subtotal?.amount || 0), 0)
|
|
||||||
);
|
|
||||||
}, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatValue = (value, type) => {
|
const formatValue = (value, type) => {
|
||||||
if (type === StatisticType.JOBS) {
|
if (type === StatisticType.JOBS) {
|
||||||
return value.toFixed(0);
|
return value.toFixed(0);
|
||||||
@@ -87,9 +94,15 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
|||||||
const totalAmountInProduction = useMemo(() => {
|
const totalAmountInProduction = useMemo(() => {
|
||||||
if (!cardSettings.totalAmountInProduction) return null;
|
if (!cardSettings.totalAmountInProduction) return null;
|
||||||
const total = calculateTotalAmount(data, "job_totals");
|
const total = calculateTotalAmount(data, "job_totals");
|
||||||
return parseFloat(total.toFixed(2));
|
return total.toFormat("$0,0.00");
|
||||||
}, [data, cardSettings.totalAmountInProduction]);
|
}, [data, cardSettings.totalAmountInProduction]);
|
||||||
|
|
||||||
|
const totalAmountOnBoard = useMemo(() => {
|
||||||
|
if (!reducerData || !cardSettings.totalAmountOnBoard) return null;
|
||||||
|
const total = calculateReducerTotalAmount(reducerData.lanes, "job_totals");
|
||||||
|
return total.toFormat("$0,0.00");
|
||||||
|
}, [reducerData, cardSettings.totalAmountOnBoard]);
|
||||||
|
|
||||||
const totalHrsOnBoard = useMemo(() => {
|
const totalHrsOnBoard = useMemo(() => {
|
||||||
if (!reducerData || !cardSettings.totalHrsOnBoard) return null;
|
if (!reducerData || !cardSettings.totalHrsOnBoard) return null;
|
||||||
const total =
|
const total =
|
||||||
@@ -118,12 +131,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
|||||||
[reducerData, cardSettings.jobsOnBoard]
|
[reducerData, cardSettings.jobsOnBoard]
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalAmountOnBoard = useMemo(() => {
|
|
||||||
if (!reducerData || !cardSettings.totalAmountOnBoard) return null;
|
|
||||||
const total = calculateReducerTotalAmount(reducerData.lanes, "job_totals");
|
|
||||||
return parseFloat(total.toFixed(2));
|
|
||||||
}, [reducerData, cardSettings.totalAmountOnBoard]);
|
|
||||||
|
|
||||||
const tasksInProduction = useMemo(() => {
|
const tasksInProduction = useMemo(() => {
|
||||||
if (!data || !cardSettings.tasksInProduction) return null;
|
if (!data || !cardSettings.tasksInProduction) return null;
|
||||||
return data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0);
|
return data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0);
|
||||||
@@ -191,7 +198,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
|
|||||||
<Statistic
|
<Statistic
|
||||||
title={t(`production.statistics.${stat.label}`)}
|
title={t(`production.statistics.${stat.label}`)}
|
||||||
value={formatValue(stat.value, stat.type)}
|
value={formatValue(stat.value, stat.type)}
|
||||||
prefix={stat.type === StatisticType.AMOUNT ? t("production.statistics.currency_symbol") : undefined}
|
|
||||||
suffix={
|
suffix={
|
||||||
stat.type === StatisticType.HOURS
|
stat.type === StatisticType.HOURS
|
||||||
? t("production.statistics.hours")
|
? t("production.statistics.hours")
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Dropdown, Input, Space, Statistic, Table } from "antd";
|
|
||||||
import { PageHeader } from "@ant-design/pro-layout";
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import { Button, Dropdown, Input, Space, Statistic, Table } from "antd";
|
||||||
|
import _ from "lodash";
|
||||||
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import ReactDragListView from "react-drag-listview";
|
import ReactDragListView from "react-drag-listview";
|
||||||
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 { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import Prompt from "../../utils/prompt.js";
|
||||||
|
import AlertComponent from "../alert/alert.component.jsx";
|
||||||
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
import ProductionListColumnsAdd from "../production-list-columns/production-list-columns.add.component";
|
||||||
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
import ProductionListColumns from "../production-list-columns/production-list-columns.data";
|
||||||
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
import ProductionListDetail from "../production-list-detail/production-list-detail.component";
|
||||||
|
import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
|
||||||
import ProductionListPrint from "./production-list-print.component";
|
import ProductionListPrint from "./production-list-print.component";
|
||||||
import ResizeableTitle from "./production-list-table.resizeable.component";
|
import ResizeableTitle from "./production-list-table.resizeable.component";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
import { SyncOutlined } from "@ant-design/icons";
|
|
||||||
import Prompt from "../../utils/prompt.js";
|
|
||||||
import _ from "lodash";
|
|
||||||
import AlertComponent from "../alert/alert.component.jsx";
|
|
||||||
import { ProductionListConfigManager } from "./production-list-config-manager.component.jsx";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -43,7 +43,7 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici
|
|||||||
const initialStateRef = useRef(
|
const initialStateRef = useRef(
|
||||||
(bodyshop.production_config &&
|
(bodyshop.production_config &&
|
||||||
bodyshop.production_config.find((p) => p.name === defaultView)?.columns.tableState) ||
|
bodyshop.production_config.find((p) => p.name === defaultView)?.columns.tableState) ||
|
||||||
bodyshop.production_config[0]?.columns.tableState || {
|
(bodyshop.production_config && bodyshop.production_config[0]?.columns.tableState) || {
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
filteredInfo: { text: "" }
|
filteredInfo: { text: "" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,28 +34,34 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const {
|
const {
|
||||||
treatments: { Enhanced_Payroll }
|
treatments: { Enhanced_Payroll, ADPPayroll }
|
||||||
} = useSplitTreatments({
|
} = useSplitTreatments({
|
||||||
attributes: {},
|
attributes: {},
|
||||||
names: ["Enhanced_Payroll"],
|
names: ["Enhanced_Payroll", "ADPPayroll"],
|
||||||
splitKey: bodyshop.imexshopid
|
splitKey: bodyshop.imexshopid
|
||||||
});
|
});
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const Templates = TemplateList("report_center");
|
const Templates = TemplateList("report_center");
|
||||||
const ReportsList =
|
const ReportsList = Object.keys(Templates)
|
||||||
Enhanced_Payroll.treatment === "on"
|
.map((key) => Templates[key])
|
||||||
? Object.keys(Templates)
|
.filter((temp) => {
|
||||||
.map((key) => {
|
const enhancedPayrollOn = Enhanced_Payroll.treatment === "on";
|
||||||
return Templates[key];
|
const adpPayrollOn = ADPPayroll.treatment === "on";
|
||||||
})
|
|
||||||
.filter((temp) => temp.enhanced_payroll === undefined || temp.enhanced_payroll === true)
|
if (enhancedPayrollOn && adpPayrollOn) {
|
||||||
: Object.keys(Templates)
|
return temp.enhanced_payroll !== false || temp.adp_payroll !== false;
|
||||||
.map((key) => {
|
}
|
||||||
return Templates[key];
|
if (enhancedPayrollOn) {
|
||||||
})
|
return temp.enhanced_payroll !== false && temp.adp_payroll !== true;
|
||||||
.filter((temp) => temp.enhanced_payroll === undefined || temp.enhanced_payroll === false);
|
}
|
||||||
|
if (adpPayrollOn) {
|
||||||
|
return temp.adp_payroll !== false && temp.enhanced_payroll !== true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp.enhanced_payroll !== true && temp.adp_payroll !== true;
|
||||||
|
});
|
||||||
|
|
||||||
const { open } = reportCenterModal;
|
const { open } = reportCenterModal;
|
||||||
|
|
||||||
@@ -104,7 +110,7 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
to: values.to,
|
to: values.to,
|
||||||
subject: Templates[values.key]?.subject
|
subject: Templates[values.key]?.subject
|
||||||
},
|
},
|
||||||
values.sendbyexcel === "excel" ? "x" : values.sendby === "email" ? "e" : "p",
|
values.sendbytext === "text" ? values.sendbytext : values.sendbyexcel === "excel" ? "x" : values.sendby === "email" ? "e" : "p",
|
||||||
id
|
id
|
||||||
);
|
);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -291,7 +297,15 @@ export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
|
|||||||
</Radio.Group>
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
);
|
||||||
if (reporttype !== "excel")
|
if (reporttype === "text")
|
||||||
|
return (
|
||||||
|
<Form.Item label={t("general.labels.sendby")} name="sendbytext" initialValue="text">
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="text">{t("general.labels.text")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
if (reporttype !== "excel" || reporttype !== "text")
|
||||||
return (
|
return (
|
||||||
<Form.Item label={t("general.labels.sendby")} name="sendby" initialValue="print">
|
<Form.Item label={t("general.labels.sendby")} name="sendby" initialValue="print">
|
||||||
<Radio.Group>
|
<Radio.Group>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ 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";
|
||||||
|
import ShopInfoIntellipay from "./shop-intellipay-config.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -135,6 +136,17 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
],
|
],
|
||||||
rome: "USE_IMEX",
|
rome: "USE_IMEX",
|
||||||
promanager: []
|
promanager: []
|
||||||
|
}),
|
||||||
|
...InstanceRenderManager({
|
||||||
|
imex: [],
|
||||||
|
rome: [
|
||||||
|
{
|
||||||
|
key: "intellipay",
|
||||||
|
label: t("bodyshop.labels.intellipay"),
|
||||||
|
children: <ShopInfoIntellipay form={form} />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
promanager: []
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ 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 DatePickerRanges from "../../utils/DatePickerRanges";
|
import DatePickerRanges from "../../utils/DatePickerRanges";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import FeatureWrapper, { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||||
import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
|
||||||
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 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({
|
||||||
@@ -28,10 +28,10 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
treatments: { ClosingPeriod }
|
treatments: { ClosingPeriod, ADPPayroll }
|
||||||
} = useSplitTreatments({
|
} = useSplitTreatments({
|
||||||
attributes: {},
|
attributes: {},
|
||||||
names: ["ClosingPeriod"],
|
names: ["ClosingPeriod", "ADPPayroll"],
|
||||||
splitKey: bodyshop && bodyshop.imexshopid
|
splitKey: bodyshop && bodyshop.imexshopid
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,7 +98,6 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<Form.Item label={t("bodyshop.fields.email")} name="email">
|
<Form.Item label={t("bodyshop.fields.email")} name="email">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.phone")}
|
label={t("bodyshop.fields.phone")}
|
||||||
name="phone"
|
name="phone"
|
||||||
@@ -356,14 +355,22 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<Select mode="tags" />
|
<Select mode="tags" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{ClosingPeriod.treatment === "on" && (
|
{ClosingPeriod.treatment === "on" && (
|
||||||
<>
|
<Form.Item
|
||||||
<Form.Item
|
name={["accountingconfig", "ClosingPeriod"]}
|
||||||
name={["accountingconfig", "ClosingPeriod"]}
|
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
>
|
||||||
>
|
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
</Form.Item>
|
||||||
</Form.Item>
|
)}
|
||||||
</>
|
{ADPPayroll.treatment === "on" && (
|
||||||
|
<Form.Item name={["accountingconfig", "companyCode"]} label={t("bodyshop.fields.companycode")}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
{ADPPayroll.treatment === "on" && (
|
||||||
|
<Form.Item name={["accountingconfig", "batchID"]} label={t("bodyshop.fields.batchid")}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</FeatureWrapper>
|
</FeatureWrapper>
|
||||||
|
|||||||
@@ -30,219 +30,226 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
return (
|
return (
|
||||||
<RbacWrapper action="shop:rbac">
|
<RbacWrapper action="shop:rbac">
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
{...HasFeatureAccess({ featureName: "export", bodyshop }) ? [
|
{...HasFeatureAccess({ featureName: "export", bodyshop })
|
||||||
<Form.Item
|
? [
|
||||||
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>
|
||||||
{...HasFeatureAccess({ featureName: "bills", bodyshop }) ? [
|
]
|
||||||
<Form.Item
|
: []}
|
||||||
label={t("bodyshop.fields.rbac.bills.delete")}
|
{...HasFeatureAccess({ featureName: "bills", bodyshop })
|
||||||
rules={[
|
? [
|
||||||
{
|
<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"]}
|
||||||
]:[]}
|
>
|
||||||
|
<InputNumber />
|
||||||
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop }) ? [
|
</Form.Item>
|
||||||
<Form.Item
|
]
|
||||||
label={t("bodyshop.fields.rbac.contracts.create")}
|
: []}
|
||||||
rules={[
|
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop })
|
||||||
{
|
? [
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.contracts.create")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "contracts:create"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>,
|
]}
|
||||||
<Form.Item
|
name={["md_rbac", "contracts:create"]}
|
||||||
label={t("bodyshop.fields.rbac.contracts.detail")}
|
>
|
||||||
rules={[
|
<InputNumber />
|
||||||
{
|
</Form.Item>,
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.contracts.detail")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "contracts:detail"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>,
|
]}
|
||||||
<Form.Item
|
name={["md_rbac", "contracts:detail"]}
|
||||||
label={t("bodyshop.fields.rbac.contracts.list")}
|
>
|
||||||
rules={[
|
<InputNumber />
|
||||||
{
|
</Form.Item>,
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.contracts.list")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "contracts:list"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>,
|
]}
|
||||||
<Form.Item
|
name={["md_rbac", "contracts:list"]}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
>
|
||||||
rules={[
|
<InputNumber />
|
||||||
{
|
</Form.Item>,
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "courtesycar:create"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>,
|
]}
|
||||||
<Form.Item
|
name={["md_rbac", "courtesycar:create"]}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
>
|
||||||
rules={[
|
<InputNumber />
|
||||||
{
|
</Form.Item>,
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "courtesycar:detail"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>,
|
]}
|
||||||
<Form.Item
|
name={["md_rbac", "courtesycar:detail"]}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
>
|
||||||
rules={[
|
<InputNumber />
|
||||||
{
|
</Form.Item>,
|
||||||
required: true
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
||||||
}
|
rules={[
|
||||||
]}
|
{
|
||||||
name={["md_rbac", "courtesycar:list"]}
|
required: true
|
||||||
>
|
//message: t("general.validation.required"),
|
||||||
<InputNumber />
|
}
|
||||||
</Form.Item>
|
]}
|
||||||
]:[]}
|
name={["md_rbac", "courtesycar:list"]}
|
||||||
{...HasFeatureAccess({ featureName: "csi", bodyshop }) ? [
|
>
|
||||||
<Form.Item
|
<InputNumber />
|
||||||
label={t("bodyshop.fields.rbac.csi.export")}
|
</Form.Item>
|
||||||
rules={[
|
]
|
||||||
{
|
: []}
|
||||||
required: true
|
{...HasFeatureAccess({ featureName: "csi", bodyshop })
|
||||||
//message: t("general.validation.required"),
|
? [
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.csi.export")}
|
||||||
name={["md_rbac", "csi:export"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>,
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.csi.page")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "csi:export"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.csi.page")}
|
||||||
name={["md_rbac", "csi:page"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//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={[
|
||||||
@@ -255,6 +262,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.employee_teams.page")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "employee_teams:page"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.jobs.admin")}
|
label={t("bodyshop.fields.rbac.jobs.admin")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -435,31 +454,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "employees:page"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.employee_teams.page")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "employee_teams:page"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.payments.enter")}
|
label={t("bodyshop.fields.rbac.payments.enter")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -522,7 +516,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.production.list")}
|
label={t("bodyshop.fields.rbac.production.list")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -561,128 +554,118 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
{...HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? [
|
{...HasFeatureAccess({ featureName: "timetickets", bodyshop })
|
||||||
<Form.Item
|
? [
|
||||||
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "shiftclock:view"]}
|
]}
|
||||||
>
|
name={["md_rbac", "shiftclock:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.shop.config")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.shop.config")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "shop:config"]}
|
]}
|
||||||
>
|
name={["md_rbac", "shop:config"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
]}
|
||||||
>
|
name={["md_rbac", "timetickets:edit"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
]}
|
||||||
>
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "timetickets:editcommitted"]}
|
]}
|
||||||
>
|
name={["md_rbac", "timetickets:editcommitted"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "ttapprovals:view"]}
|
]}
|
||||||
>
|
name={["md_rbac", "ttapprovals:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "ttapprovals:approve"]}
|
]}
|
||||||
>
|
name={["md_rbac", "ttapprovals:approve"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "timetickets:enter"]}
|
]}
|
||||||
>
|
name={["md_rbac", "timetickets:enter"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.timetickets.list")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.timetickets.list")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "timetickets:list"]}
|
]}
|
||||||
>
|
name={["md_rbac", "timetickets:list"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>,
|
<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={[
|
||||||
@@ -757,7 +740,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.users.editaccess")}
|
label={t("bodyshop.fields.rbac.users.editaccess")}
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Alert, Form, InputNumber, Switch } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay);
|
||||||
|
|
||||||
|
export function ShopInfoIntellipay({ bodyshop, form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
|
||||||
|
{() => {
|
||||||
|
const { intellipay_config } = form.getFieldsValue();
|
||||||
|
|
||||||
|
if (intellipay_config?.enable_cash_discount)
|
||||||
|
return <Alert message={t("bodyshop.labels.intellipay_cash_discount")} />;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.intellipay_config.enable_cash_discount")}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["intellipay_config", "enable_cash_discount"]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
|
||||||
|
valuePropName="checked"
|
||||||
|
dependencies={[["intellipay_config", "enable_cash_discount"]]}
|
||||||
|
name={["intellipay_config", "cash_discount_percentage"]}
|
||||||
|
rules={[
|
||||||
|
({ getFieldsValue }) => ({ required: form.getFieldValue(["intellipay_config", "enable_cash_discount"]) })
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={100} precision={1} suffix='%'/>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Button, Card, DatePicker, Form, Popover, Radio, Space } from "antd";
|
import { Button, Card, DatePicker, Form, Popover, Radio, Space } from "antd";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
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";
|
||||||
@@ -7,10 +6,12 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import DatePIckerRanges from "../../utils/DatePickerRanges";
|
import DatePIckerRanges from "../../utils/DatePickerRanges";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectTechnician,
|
||||||
technician: selectTechnician
|
technician: selectTechnician
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -18,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(TechJobPrintTickets);
|
export default connect(mapStateToProps, mapDispatchToProps)(TechJobPrintTickets);
|
||||||
|
|
||||||
export function TechJobPrintTickets({ technician, event, attendacePrint }) {
|
export function TechJobPrintTickets({ bodyshop, technician, event, attendacePrint }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -57,7 +58,8 @@ export function TechJobPrintTickets({ technician, event, attendacePrint }) {
|
|||||||
subject:
|
subject:
|
||||||
attendacePrint === true ? Templates.attendance_employee.subject : Templates.timetickets_employee.subject
|
attendacePrint === true ? Templates.attendance_employee.subject : Templates.timetickets_employee.subject
|
||||||
},
|
},
|
||||||
values.sendby // === "email" ? "e" : "p"
|
values.sendby,
|
||||||
|
bodyshop
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ export function TimeTicketList({
|
|||||||
key: "memo",
|
key: "memo",
|
||||||
sorter: (a, b) => alphaSort(a.memo, b.memo),
|
sorter: (a, b) => alphaSort(a.memo, b.memo),
|
||||||
sortOrder: state.sortedInfo.columnKey === "memo" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "memo" && state.sortedInfo.order,
|
||||||
render: (text, record) => (record.clockon || record.clockoff ? t(record.memo) : record.memo)
|
render: (text, record) => (record.memo?.startsWith("timetickets.labels") ? t(record.memo) : record.memo)
|
||||||
},
|
},
|
||||||
...(Enhanced_Payroll.treatment === "on"
|
...(Enhanced_Payroll.treatment === "on"
|
||||||
? [
|
? [
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useLazyQuery } from "@apollo/client";
|
import { useLazyQuery } from "@apollo/client";
|
||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Form, Input, InputNumber, Select, Switch } from "antd";
|
import { Form, Input, InputNumber, Select, Switch } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -7,8 +8,10 @@ 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 { selectAuthLevel, selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectAuthLevel, selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
import EmployeeSearchSelect from "../employee-search-select/employee-search-select.component";
|
||||||
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
import {
|
||||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
default as DateTimePicker,
|
||||||
|
default as FormDateTimePicker
|
||||||
|
} from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||||
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
|
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
|
||||||
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
|
||||||
@@ -16,7 +19,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
|||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -69,13 +71,7 @@ export function TimeTicketModalComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const MemoInput = ({ value, ...props }) => {
|
const MemoInput = ({ value, ...props }) => {
|
||||||
return (
|
return <Input value={value?.startsWith("timetickets.labels") ? t(value) : value} {...props} />;
|
||||||
<Input
|
|
||||||
value={value?.startsWith("timetickets.") ? t(value) : value}
|
|
||||||
{...props}
|
|
||||||
disabled={value?.startsWith("timetickets.") || disabled}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -333,7 +329,9 @@ export function LaborAllocationContainer({ jobid, loading, lineTicketData, hideT
|
|||||||
timetickets={lineTicketData.timetickets}
|
timetickets={lineTicketData.timetickets}
|
||||||
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
|
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
|
||||||
/>
|
/>
|
||||||
{!hideTimeTickets && <TimeTicketList loading={loading} timetickets={lineTicketData.timetickets} techConsole />}
|
{!hideTimeTickets && (
|
||||||
|
<TimeTicketList loading={loading} timetickets={jobid ? lineTicketData.timetickets : []} techConsole />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function TimeTicketShiftActive({ timetickets, refetch, isTechCons
|
|||||||
renderItem={(ticket) => (
|
renderItem={(ticket) => (
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Card
|
<Card
|
||||||
title={t(ticket.memo)}
|
title={ticket.memo?.startsWith("timetickets.labels") ? t(ticket.memo) : ticket.memo}
|
||||||
actions={[
|
actions={[
|
||||||
<TechClockOffButton
|
<TechClockOffButton
|
||||||
jobId={ticket.jobid}
|
jobId={ticket.jobid}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { AlertOutlined } from "@ant-design/icons";
|
import { AlertOutlined } from "@ant-design/icons";
|
||||||
import { Alert, Button, Col, Row, Space } from "antd";
|
import { Alert, Button, Col, notification, Row, Space } from "antd";
|
||||||
import i18n from "i18next";
|
import i18n from "i18next";
|
||||||
import React, { useEffect } from "react";
|
import React, { useCallback, 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 { selectUpdateAvailable } from "../../redux/application/application.selectors";
|
import { selectUpdateAvailable } from "../../redux/application/application.selectors";
|
||||||
import { useRegisterSW } from "virtual:pwa-register/react";
|
import { useRegisterSW } from "virtual:pwa-register/react";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import useCountDown from "../../utils/countdownHook";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
updateAvailable: selectUpdateAvailable
|
updateAvailable: selectUpdateAvailable
|
||||||
@@ -19,6 +20,15 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function UpdateAlert({ updateAvailable }) {
|
export function UpdateAlert({ updateAvailable }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [timerStarted, setTimerStarted] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [
|
||||||
|
timeLeft,
|
||||||
|
{
|
||||||
|
start //pause, resume, reset
|
||||||
|
}
|
||||||
|
] = useCountDown(180000, 1000);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
offlineReady: [offlineReady],
|
offlineReady: [offlineReady],
|
||||||
needRefresh: [needRefresh],
|
needRefresh: [needRefresh],
|
||||||
@@ -31,7 +41,7 @@ export function UpdateAlert({ updateAvailable }) {
|
|||||||
() => {
|
() => {
|
||||||
r.update();
|
r.update();
|
||||||
},
|
},
|
||||||
10 * 60 * 1000
|
30 * 60 * 1000
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -40,11 +50,43 @@ export function UpdateAlert({ updateAvailable }) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ReloadNewVersion = useCallback(() => {
|
||||||
|
setLoading(true);
|
||||||
|
updateServiceWorker(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload(true);
|
||||||
|
}, 5000);
|
||||||
|
}, [updateServiceWorker]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (import.meta.env.DEV) {
|
if (needRefresh) {
|
||||||
console.log(`SW Status => Refresh? ${needRefresh} - offlineReady? ${offlineReady}`);
|
start();
|
||||||
|
setTimerStarted(true);
|
||||||
}
|
}
|
||||||
}, [needRefresh, offlineReady]);
|
}, [start, needRefresh, offlineReady]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (needRefresh && timerStarted && timeLeft < 60000) {
|
||||||
|
notification.open({
|
||||||
|
type: "warning",
|
||||||
|
closable: false,
|
||||||
|
duration: 65000,
|
||||||
|
key: "autoupdate",
|
||||||
|
message: t("general.actions.autoupdate", {
|
||||||
|
time: (timeLeft / 1000).toFixed(0),
|
||||||
|
app: InstanceRenderManager({
|
||||||
|
imex: "$t(titles.imexonline)",
|
||||||
|
rome: "$t(titles.romeonline)",
|
||||||
|
promanager: "$t(titles.promanager)"
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
placement: "bottomRight"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (needRefresh && timerStarted && timeLeft <= 0) {
|
||||||
|
ReloadNewVersion();
|
||||||
|
}
|
||||||
|
}, [timeLeft, t, needRefresh, ReloadNewVersion, timerStarted]);
|
||||||
|
|
||||||
if (!needRefresh) return null;
|
if (!needRefresh) return null;
|
||||||
|
|
||||||
@@ -75,9 +117,10 @@ export function UpdateAlert({ updateAvailable }) {
|
|||||||
<Button onClick={() => window.open("https://imex-online.noticeable.news/", "_blank")}>
|
<Button onClick={() => window.open("https://imex-online.noticeable.news/", "_blank")}>
|
||||||
{i18n.t("general.actions.viewreleasenotes")}
|
{i18n.t("general.actions.viewreleasenotes")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" onClick={() => updateServiceWorker(true)}>
|
<Button loading={loading} type="primary" onClick={() => ReloadNewVersion()}>
|
||||||
{i18n.t("general.actions.refresh")}
|
{i18n.t("general.actions.refresh")} {`(${(timeLeft / 1000).toFixed(0)} s)`}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button onClick={() => start(300000)}>{i18n.t("general.actions.delay")}</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
|||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
//To be used as a form element only.
|
// To be used as a form element only.
|
||||||
|
|
||||||
const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, preferredMake, showPhone }, ref) => {
|
const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, preferredMake, showPhone }, ref) => {
|
||||||
const [option, setOption] = useState(value);
|
const [option, setOption] = useState(value);
|
||||||
@@ -33,9 +33,25 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
|
|||||||
if (!value || !options) return label;
|
if (!value || !options) return label;
|
||||||
const discount = options?.find((o) => o.id === value)?.discount;
|
const discount = options?.find((o) => o.id === value)?.discount;
|
||||||
return (
|
return (
|
||||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
<div
|
||||||
<div style={{ flex: 1 }}>{label}</div>
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
{discount && discount !== 0 ? <Tag color="green">{`${discount * 100}%`}</Tag> : null}
|
{discount && discount !== 0 ? <Tag color="green">{`${discount * 100}%`}</Tag> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -45,36 +61,67 @@ const VendorSearchSelect = ({ value, onChange, options, onSelect, disabled, pref
|
|||||||
optionFilterProp="name"
|
optionFilterProp="name"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
disabled={disabled || false}
|
disabled={disabled || false}
|
||||||
optionLabelProp={"name"}
|
optionLabelProp="name"
|
||||||
>
|
>
|
||||||
{favorites
|
{favorites &&
|
||||||
? favorites.map((o) => (
|
favorites.map((o) => (
|
||||||
<Option key={`favorite-${o.id}`} value={o.id} name={o.name} discount={o.discount}>
|
<Option key={`favorite-${o.id}`} value={o.id} name={o.name} discount={o.discount}>
|
||||||
<div className="imex-flex-row">
|
<div
|
||||||
<div style={{ flex: 1 }}>{o.name}</div>
|
style={{
|
||||||
<Space style={{ marginLeft: "1rem" }}>
|
display: "flex",
|
||||||
<HeartOutlined style={{ color: "red" }} />
|
alignItems: "center",
|
||||||
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
flexWrap: "nowrap",
|
||||||
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
width: "100%"
|
||||||
</Space>
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{o.name}
|
||||||
</div>
|
</div>
|
||||||
</Option>
|
<Space style={{ marginLeft: "1rem" }}>
|
||||||
))
|
<HeartOutlined style={{ color: "red" }} />
|
||||||
: null}
|
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
||||||
{options
|
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
||||||
? options.map((o) => (
|
</Space>
|
||||||
<Option key={o.id} value={o.id} name={o.name} discount={o.discount}>
|
</div>
|
||||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
</Option>
|
||||||
<div style={{ flex: 1 }}>{o.name}</div>
|
))}
|
||||||
|
{options &&
|
||||||
<Space style={{ marginLeft: "1rem" }}>
|
options.map((o) => (
|
||||||
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
<Option key={o.id} value={o.id} name={o.name} discount={o.discount}>
|
||||||
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
<div
|
||||||
</Space>
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
flexWrap: "nowrap",
|
||||||
|
width: "100%"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minWidth: 0,
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
whiteSpace: "nowrap"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{o.name}
|
||||||
</div>
|
</div>
|
||||||
</Option>
|
<Space style={{ marginLeft: "1rem" }}>
|
||||||
))
|
{o.phone && showPhone && <PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>}
|
||||||
: null}
|
{o.discount && o.discount !== 0 ? <Tag color="green">{`${o.discount * 100}%`}</Tag> : null}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { getFirestore } from "firebase/firestore";
|
|||||||
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
import { getMessaging, getToken, onMessage } from "firebase/messaging";
|
||||||
import { store } from "../redux/store";
|
import { store } from "../redux/store";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { checkBeta } from "../utils/handleBeta";
|
|
||||||
|
|
||||||
const config = JSON.parse(import.meta.env.VITE_APP_FIREBASE_CONFIG);
|
const config = JSON.parse(import.meta.env.VITE_APP_FIREBASE_CONFIG);
|
||||||
initializeApp(config);
|
initializeApp(config);
|
||||||
@@ -88,7 +87,7 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
operationName: eventName,
|
operationName: eventName,
|
||||||
variables: additionalParams,
|
variables: additionalParams,
|
||||||
dbevent: false,
|
dbevent: false,
|
||||||
env: checkBeta() ? "beta" : "master"
|
env: "master"
|
||||||
});
|
});
|
||||||
// console.log(
|
// console.log(
|
||||||
// "%c[Analytics]",
|
// "%c[Analytics]",
|
||||||
|
|||||||
@@ -138,7 +138,8 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
tt_enforce_hours_for_tech_console
|
tt_enforce_hours_for_tech_console
|
||||||
md_tasks_presets
|
md_tasks_presets
|
||||||
use_paint_scale_data
|
use_paint_scale_data
|
||||||
md_ro_guard
|
intellipay_config
|
||||||
|
md_ro_guard
|
||||||
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -266,7 +267,8 @@ export const UPDATE_SHOP = gql`
|
|||||||
enforce_conversion_category
|
enforce_conversion_category
|
||||||
tt_enforce_hours_for_tech_console
|
tt_enforce_hours_for_tech_console
|
||||||
md_tasks_presets
|
md_tasks_presets
|
||||||
md_ro_guard
|
intellipay_config
|
||||||
|
md_ro_guard
|
||||||
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { Tabs } from "antd";
|
import { Tabs } from "antd";
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.component";
|
||||||
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
||||||
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
||||||
import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.component";
|
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
|
||||||
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
|
||||||
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
|
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
||||||
|
|
||||||
export default function TechLookupContainer() {
|
export default function TechLookupContainer() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -20,6 +21,7 @@ export default function TechLookupContainer() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<RbacWrapperComponent action="jobs:list-active">
|
<RbacWrapperComponent action="jobs:list-active">
|
||||||
|
<TechLookupJobsDrawer />
|
||||||
<TechLookupJobsList />
|
<TechLookupJobsList />
|
||||||
</RbacWrapperComponent>
|
</RbacWrapperComponent>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import ErrorBoundary from "../../components/error-boundary/error-boundary.compon
|
|||||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
import TechHeader from "../../components/tech-header/tech-header.component";
|
import TechHeader from "../../components/tech-header/tech-header.component";
|
||||||
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
|
|
||||||
import TechSider from "../../components/tech-sider/tech-sider.component";
|
import TechSider from "../../components/tech-sider/tech-sider.component";
|
||||||
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
import UpdateAlert from "../../components/update-alert/update-alert.component";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
@@ -68,7 +67,7 @@ export function TechPage({ technician }) {
|
|||||||
<Layout>
|
<Layout>
|
||||||
<UpdateAlert />
|
<UpdateAlert />
|
||||||
<TechHeader />
|
<TechHeader />
|
||||||
<TechLookupJobsDrawer />
|
|
||||||
<TaskUpsertModalContainer />
|
<TaskUpsertModalContainer />
|
||||||
<Content className="tech-content-container">
|
<Content className="tech-content-container">
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
signInWithEmailAndPassword,
|
signInWithEmailAndPassword,
|
||||||
signOut
|
signOut
|
||||||
} from "firebase/auth";
|
} from "firebase/auth";
|
||||||
import { doc, getDoc, setDoc } from "firebase/firestore";
|
import { arrayUnion, doc, getDoc, setDoc, updateDoc } from "firebase/firestore";
|
||||||
import { getToken } from "firebase/messaging";
|
import { getToken } from "firebase/messaging";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
@@ -48,6 +48,7 @@ import {
|
|||||||
validatePasswordResetSuccess
|
validatePasswordResetSuccess
|
||||||
} from "./user.actions";
|
} from "./user.actions";
|
||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
|
|
||||||
const fpPromise = FingerprintJS.load();
|
const fpPromise = FingerprintJS.load();
|
||||||
|
|
||||||
@@ -177,10 +178,24 @@ export function* setInstanceIdSaga({ payload: uid }) {
|
|||||||
// Get the visitor identifier when you need it.
|
// Get the visitor identifier when you need it.
|
||||||
const fp = yield fpPromise;
|
const fp = yield fpPromise;
|
||||||
const result = yield fp.get();
|
const result = yield fp.get();
|
||||||
yield setDoc(userInstanceRef, {
|
const res = yield cleanAxios.get("https://api.ipify.org/?format=json");
|
||||||
timestamp: new Date(),
|
const udoc = yield getDoc(userInstanceRef);
|
||||||
fingerprint: result.visitorId
|
|
||||||
});
|
if (!udoc.data()) {
|
||||||
|
yield setDoc(userInstanceRef, {
|
||||||
|
timestamp: new Date(),
|
||||||
|
fingerprint: result.visitorId,
|
||||||
|
//totalFingerprint: result,
|
||||||
|
ip: [res.data.ip]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
yield updateDoc(userInstanceRef, {
|
||||||
|
timestamp: new Date(),
|
||||||
|
fingerprint: result.visitorId,
|
||||||
|
//totalFingerprint: result,
|
||||||
|
ip: arrayUnion(res.data.ip)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
yield put(setLocalFingerprint(result.visitorId));
|
yield put(setLocalFingerprint(result.visitorId));
|
||||||
yield delay(5 * 60 * 1000);
|
yield delay(5 * 60 * 1000);
|
||||||
|
|||||||
@@ -230,7 +230,7 @@
|
|||||||
"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",
|
||||||
@@ -270,9 +270,9 @@
|
|||||||
"testrender": "Test Render"
|
"testrender": "Test Render"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"creatingdefaultview": "Error creating default view.",
|
||||||
"loading": "Unable to load shop details. Please call technical support.",
|
"loading": "Unable to load shop details. Please call technical support.",
|
||||||
"saving": "Error encountered while saving. {{message}}",
|
"saving": "Error encountered while saving. {{message}}"
|
||||||
"creatingdefaultview": "Error creating default view."
|
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
|
"ReceivableCustomField": "QBO Receivable Custom Field {{number}}",
|
||||||
@@ -285,19 +285,21 @@
|
|||||||
},
|
},
|
||||||
"appt_length": "Default Appointment Length",
|
"appt_length": "Default Appointment Length",
|
||||||
"attach_pdf_to_email": "Attach PDF copy to sent emails?",
|
"attach_pdf_to_email": "Attach PDF copy to sent emails?",
|
||||||
|
"batchid": "ADP Batch ID",
|
||||||
"bill_allow_post_to_closed": "Allow Bills to be posted to Closed Jobs",
|
"bill_allow_post_to_closed": "Allow Bills to be posted to Closed Jobs",
|
||||||
"bill_federal_tax_rate": "Bills - Federal Tax Rate %",
|
"bill_federal_tax_rate": "Bills - Federal Tax Rate %",
|
||||||
"bill_local_tax_rate": "Bill - Local Tax Rate %",
|
"bill_local_tax_rate": "Bill - Local Tax Rate %",
|
||||||
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
|
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
|
||||||
"city": "City",
|
"city": "City",
|
||||||
"closingperiod": "Closing Period",
|
"closingperiod": "Closing Period",
|
||||||
|
"companycode": "ADP Company Code",
|
||||||
"country": "Country",
|
"country": "Country",
|
||||||
"dailybodytarget": "Scoreboard - Daily Body Target",
|
"dailybodytarget": "Scoreboard - Daily Body Target",
|
||||||
"dailypainttarget": "Scoreboard - Daily Paint Target",
|
"dailypainttarget": "Scoreboard - Daily Paint Target",
|
||||||
"default_adjustment_rate": "Default Labor Deduction Adjustment Rate",
|
"default_adjustment_rate": "Default Labor Deduction Adjustment Rate",
|
||||||
"deliver": {
|
"deliver": {
|
||||||
"templates": "Delivery Templates",
|
"require_actual_delivery_date": "Require Actual Delivery",
|
||||||
"require_actual_delivery_date": "Require Actual Delivery"
|
"templates": "Delivery Templates"
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
"apcontrol": "AP Control Number",
|
"apcontrol": "AP Control Number",
|
||||||
@@ -330,6 +332,10 @@
|
|||||||
"next_contact_hours": "Automatic Next Contact Date - Hours from Intake",
|
"next_contact_hours": "Automatic Next Contact Date - Hours from Intake",
|
||||||
"templates": "Intake Templates"
|
"templates": "Intake Templates"
|
||||||
},
|
},
|
||||||
|
"intellipay_config": {
|
||||||
|
"cash_discount_percentage": "Cash Discount %",
|
||||||
|
"enable_cash_discount": "Enable Cash Discounting"
|
||||||
|
},
|
||||||
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
||||||
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
||||||
"invoice_state_tax_rate": "Invoices - State Tax Rate",
|
"invoice_state_tax_rate": "Invoices - State Tax Rate",
|
||||||
@@ -661,6 +667,8 @@
|
|||||||
"filehandlers": "Adjusters",
|
"filehandlers": "Adjusters",
|
||||||
"insurancecos": "Insurance Companies",
|
"insurancecos": "Insurance Companies",
|
||||||
"intakechecklist": "Intake Checklist",
|
"intakechecklist": "Intake Checklist",
|
||||||
|
"intellipay": "IntelliPay",
|
||||||
|
"intellipay_cash_discount": "Please ensure that cash discounting has been enabled on your merchant account. Reach out to IntelliPay Support if you need assistance. ",
|
||||||
"jobstatuses": "Job Statuses",
|
"jobstatuses": "Job Statuses",
|
||||||
"laborrates": "Labor Rates",
|
"laborrates": "Labor Rates",
|
||||||
"licensing": "Licensing",
|
"licensing": "Licensing",
|
||||||
@@ -700,10 +708,10 @@
|
|||||||
"workingdays": "Working Days"
|
"workingdays": "Working Days"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"save": "Shop configuration saved successfully. ",
|
|
||||||
"unsavedchanges": "Unsaved changes will be lost. Are you sure you want to continue?",
|
|
||||||
"areyousure": "Are you sure you want to continue?",
|
"areyousure": "Are you sure you want to continue?",
|
||||||
"defaultviewcreated": "Default view created successfully."
|
"defaultviewcreated": "Default view created successfully.",
|
||||||
|
"save": "Shop configuration saved successfully. ",
|
||||||
|
"unsavedchanges": "Unsaved changes will be lost. Are you sure you want to continue?"
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"centermustexist": "The chosen responsibility center does not exist.",
|
"centermustexist": "The chosen responsibility center does not exist.",
|
||||||
@@ -1133,8 +1141,8 @@
|
|||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"defaults": "Defaults",
|
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
|
"autoupdate": "{{app}} will automatically update in {{time}} seconds. Please save all changes.",
|
||||||
"calculate": "Calculate",
|
"calculate": "Calculate",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
@@ -1142,6 +1150,8 @@
|
|||||||
"copied": "Copied!",
|
"copied": "Copied!",
|
||||||
"copylink": "Copy Link",
|
"copylink": "Copy Link",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
|
"defaults": "Defaults",
|
||||||
|
"delay": "Delay Update (5 mins)",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"deleteall": "Delete All",
|
"deleteall": "Delete All",
|
||||||
"deselectall": "Deselect All",
|
"deselectall": "Deselect All",
|
||||||
@@ -1153,10 +1163,12 @@
|
|||||||
"print": "Print",
|
"print": "Print",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
|
"remove_alert": "Are you sure you want to dismiss the alert?",
|
||||||
"reset": "Reset your changes.",
|
"reset": "Reset your changes.",
|
||||||
"resetpassword": "Reset Password",
|
"resetpassword": "Reset Password",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"saveandnew": "Save and New",
|
"saveandnew": "Save and New",
|
||||||
|
"saveas": "Save As",
|
||||||
"selectall": "Select All",
|
"selectall": "Select All",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"sendbysms": "Send by SMS",
|
"sendbysms": "Send by SMS",
|
||||||
@@ -1164,9 +1176,7 @@
|
|||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"tryagain": "Try Again",
|
"tryagain": "Try Again",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"viewreleasenotes": "See What's Changed",
|
"viewreleasenotes": "See What's Changed"
|
||||||
"remove_alert": "Are you sure you want to dismiss the alert?",
|
|
||||||
"saveas": "Save As"
|
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
"fcm": "You must allow notification permissions to have real time messaging. Click to try again.",
|
||||||
@@ -1181,7 +1191,6 @@
|
|||||||
"vehicle": "Vehicle"
|
"vehicle": "Vehicle"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"unsavedchanges": "Unsaved changes.",
|
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"areyousure": "Are you sure?",
|
"areyousure": "Are you sure?",
|
||||||
"barcode": "Barcode",
|
"barcode": "Barcode",
|
||||||
@@ -1250,6 +1259,7 @@
|
|||||||
"tuesday": "Tuesday",
|
"tuesday": "Tuesday",
|
||||||
"tvmode": "TV Mode",
|
"tvmode": "TV Mode",
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
|
"unsavedchanges": "Unsaved changes.",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"view": "View",
|
"view": "View",
|
||||||
"wednesday": "Wednesday",
|
"wednesday": "Wednesday",
|
||||||
@@ -1363,6 +1373,7 @@
|
|||||||
},
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
|
"create_short_link": "Generate Short Link",
|
||||||
"goback": "Go Back",
|
"goback": "Go Back",
|
||||||
"proceedtopayment": "Proceed to Payment",
|
"proceedtopayment": "Proceed to Payment",
|
||||||
"refundpayment": "Refund Payment"
|
"refundpayment": "Refund Payment"
|
||||||
@@ -2739,41 +2750,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"constants": {
|
|
||||||
"main_profile": "Default"
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"small": "Small",
|
|
||||||
"medium": "Medium",
|
|
||||||
"large": "Large",
|
|
||||||
"vertical": "Vertical",
|
|
||||||
"horizontal": "Horizontal"
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"layout": "Layout",
|
|
||||||
"information": "Information",
|
|
||||||
"statistics_title": "Statistics",
|
|
||||||
"board_settings": "Board Settings",
|
|
||||||
"filters_title": "Filters",
|
|
||||||
"filters": {
|
|
||||||
"md_ins_cos": "Insurance Companies",
|
|
||||||
"md_estimators": "Estimators"
|
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"total_hours_in_production": "Hours in Production",
|
|
||||||
"total_lab_in_production": "Body Hours in Production",
|
|
||||||
"total_lar_in_production": "Refinish Hours in Production",
|
|
||||||
"total_amount_in_production": "Dollars in Production",
|
|
||||||
"jobs_in_production": "Jobs in Production",
|
|
||||||
"total_hours_on_board": "Hours on Board",
|
|
||||||
"total_lab_on_board": "Body Hours on Board",
|
|
||||||
"total_lar_on_board": "Refinish Hours on Board",
|
|
||||||
"total_amount_on_board": "Dollars on Board",
|
|
||||||
"total_jobs_on_board": "Jobs on Board",
|
|
||||||
"tasks_in_production": "Tasks in Production",
|
|
||||||
"tasks_on_board": "Tasks on Board"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"actions": {
|
"actions": {
|
||||||
"addcolumns": "Add Columns",
|
"addcolumns": "Add Columns",
|
||||||
"bodypriority-clear": "Clear Body Priority",
|
"bodypriority-clear": "Clear Body Priority",
|
||||||
@@ -2788,29 +2764,23 @@
|
|||||||
"suspend": "Suspend",
|
"suspend": "Suspend",
|
||||||
"unsuspend": "Unsuspend"
|
"unsuspend": "Unsuspend"
|
||||||
},
|
},
|
||||||
|
"constants": {
|
||||||
|
"main_profile": "Default"
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"boardupdate": "Error encountered updating Job. {{message}}",
|
"boardupdate": "Error encountered updating Job. {{message}}",
|
||||||
"removing": "Error removing from production board. {{error}}",
|
|
||||||
"settings": "Error saving board settings: {{error}}",
|
|
||||||
"name_exists": "A Profile with this name already exists. Please choose a different name.",
|
"name_exists": "A Profile with this name already exists. Please choose a different name.",
|
||||||
"name_required": "Profile name is required."
|
"name_required": "Profile name is required.",
|
||||||
|
"removing": "Error removing from production board. {{error}}",
|
||||||
|
"settings": "Error saving board settings: {{error}}"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"kiosk_mode": "Kiosk Mode",
|
|
||||||
"on": "On",
|
|
||||||
"off": "Off",
|
|
||||||
"wide": "Wide",
|
|
||||||
"tall": "Tall",
|
|
||||||
"vertical": "Vertical",
|
|
||||||
"horizontal": "Horizontal",
|
|
||||||
"orientation": "Board Orientation",
|
|
||||||
"card_size": "Card Size",
|
|
||||||
"model_info": "Vehicle Info",
|
|
||||||
"actual_in": "Actual In",
|
"actual_in": "Actual In",
|
||||||
|
"addnewprofile": "Add New Profile",
|
||||||
"alert": "Alert",
|
"alert": "Alert",
|
||||||
"tasks": "Tasks",
|
|
||||||
"alertoff": "Remove alert from Job",
|
"alertoff": "Remove alert from Job",
|
||||||
"alerton": "Add alert to Job",
|
"alerton": "Add alert to Job",
|
||||||
|
"alerts": "Alerts",
|
||||||
"ats": "Alternative Transportation",
|
"ats": "Alternative Transportation",
|
||||||
"bodyhours": "B",
|
"bodyhours": "B",
|
||||||
"bodypriority": "B/P",
|
"bodypriority": "B/P",
|
||||||
@@ -2820,6 +2790,7 @@
|
|||||||
"qbo_usa": "QBO USA"
|
"qbo_usa": "QBO USA"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"card_size": "Card Size",
|
||||||
"cardcolor": "Colored Cards",
|
"cardcolor": "Colored Cards",
|
||||||
"cardsettings": "Card Settings",
|
"cardsettings": "Card Settings",
|
||||||
"clm_no": "Claim Number",
|
"clm_no": "Claim Number",
|
||||||
@@ -2828,48 +2799,88 @@
|
|||||||
"detailpriority": "D/P",
|
"detailpriority": "D/P",
|
||||||
"employeeassignments": "Employee Assignments",
|
"employeeassignments": "Employee Assignments",
|
||||||
"employeesearch": "Employee Search",
|
"employeesearch": "Employee Search",
|
||||||
|
"estimator": "Estimator",
|
||||||
|
"horizontal": "Horizontal",
|
||||||
"ins_co_nm": "Insurance Company Name",
|
"ins_co_nm": "Insurance Company Name",
|
||||||
"jobdetail": "Job Details",
|
"jobdetail": "Job Details",
|
||||||
|
"kiosk_mode": "Kiosk Mode",
|
||||||
"laborhrs": "Labor Hours",
|
"laborhrs": "Labor Hours",
|
||||||
"legend": "Legend:",
|
"legend": "Legend:",
|
||||||
|
"model_info": "Vehicle Info",
|
||||||
"note": "Production Note",
|
"note": "Production Note",
|
||||||
|
"off": "Off",
|
||||||
|
"on": "On",
|
||||||
|
"orientation": "Board Orientation",
|
||||||
"ownr_nm": "Customer Name",
|
"ownr_nm": "Customer Name",
|
||||||
"paintpriority": "P/P",
|
"paintpriority": "P/P",
|
||||||
"partsstatus": "Parts Status",
|
"partsstatus": "Parts Status",
|
||||||
"estimator": "Estimator",
|
|
||||||
"subtotal": "Subtotal",
|
|
||||||
"production_note": "Production Note",
|
"production_note": "Production Note",
|
||||||
"refinishhours": "R",
|
"refinishhours": "R",
|
||||||
"scheduled_completion": "Scheduled Completion",
|
"scheduled_completion": "Scheduled Completion",
|
||||||
"selectview": "Select a View",
|
"selectview": "Select a View",
|
||||||
"stickyheader": "Sticky Header (BETA)",
|
"stickyheader": "Sticky Header (BETA)",
|
||||||
"sublets": "Sublets",
|
"sublets": "Sublets",
|
||||||
|
"subtotal": "Subtotal",
|
||||||
|
"tall": "Tall",
|
||||||
|
"tasks": "Tasks",
|
||||||
"totalhours": "Total Hrs ",
|
"totalhours": "Total Hrs ",
|
||||||
"touchtime": "T/T",
|
"touchtime": "T/T",
|
||||||
|
"vertical": "Vertical",
|
||||||
"viewname": "View Name",
|
"viewname": "View Name",
|
||||||
"alerts": "Alerts",
|
"wide": "Wide"
|
||||||
"addnewprofile": "Add New Profile"
|
},
|
||||||
|
"options": {
|
||||||
|
"horizontal": "Horizontal",
|
||||||
|
"large": "Large",
|
||||||
|
"medium": "Medium",
|
||||||
|
"small": "Small",
|
||||||
|
"vertical": "Vertical"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"board_settings": "Board Settings",
|
||||||
|
"filters": {
|
||||||
|
"md_estimators": "Estimators",
|
||||||
|
"md_ins_cos": "Insurance Companies"
|
||||||
|
},
|
||||||
|
"filters_title": "Filters",
|
||||||
|
"information": "Information",
|
||||||
|
"layout": "Layout",
|
||||||
|
"statistics": {
|
||||||
|
"jobs_in_production": "Jobs in Production",
|
||||||
|
"tasks_in_production": "Tasks in Production",
|
||||||
|
"tasks_on_board": "Tasks on Board",
|
||||||
|
"total_amount_in_production": "Dollars in Production",
|
||||||
|
"total_amount_on_board": "Dollars on Board",
|
||||||
|
"total_hours_in_production": "Hours in Production",
|
||||||
|
"total_hours_on_board": "Hours on Board",
|
||||||
|
"total_jobs_on_board": "Jobs on Board",
|
||||||
|
"total_lab_in_production": "Body Hours in Production",
|
||||||
|
"total_lab_on_board": "Body Hours on Board",
|
||||||
|
"total_lar_in_production": "Refinish Hours in Production",
|
||||||
|
"total_lar_on_board": "Refinish Hours on Board"
|
||||||
|
},
|
||||||
|
"statistics_title": "Statistics"
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"currency_symbol": "$",
|
||||||
|
"hours": "Hours",
|
||||||
|
"jobs": "Jobs",
|
||||||
|
"jobs_in_production": "Jobs in Production",
|
||||||
|
"tasks": "Tasks",
|
||||||
|
"tasks_in_production": "Tasks in Production",
|
||||||
|
"tasks_on_board": "Tasks on Board",
|
||||||
|
"total_amount_in_production": "Dollars in Production",
|
||||||
|
"total_amount_on_board": "Dollars on Board",
|
||||||
|
"total_hours_in_production": "Hours in Production",
|
||||||
|
"total_hours_on_board": "Hours on Board",
|
||||||
|
"total_jobs_on_board": "Jobs on Board",
|
||||||
|
"total_lab_in_production": "Body Hours in Production",
|
||||||
|
"total_lab_on_board": "Body Hours on Board",
|
||||||
|
"total_lar_in_production": "Refinish Hours in Production",
|
||||||
|
"total_lar_on_board": "Refinish Hours on Board"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"removed": "Job removed from production."
|
"removed": "Job removed from production."
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"total_hours_in_production": "Hours in Production",
|
|
||||||
"total_lab_in_production": "Body Hours in Production",
|
|
||||||
"total_lar_in_production": "Refinish Hours in Production",
|
|
||||||
"total_amount_in_production": "Dollars in Production",
|
|
||||||
"jobs_in_production": "Jobs in Production",
|
|
||||||
"total_hours_on_board": "Hours on Board",
|
|
||||||
"total_lab_on_board": "Body Hours on Board",
|
|
||||||
"total_lar_on_board": "Refinish Hours on Board",
|
|
||||||
"total_amount_on_board": "Dollars on Board",
|
|
||||||
"total_jobs_on_board": "Jobs on Board",
|
|
||||||
"tasks_in_production": "Tasks in Production",
|
|
||||||
"tasks_on_board": "Tasks on Board",
|
|
||||||
"tasks": "Tasks",
|
|
||||||
"hours": "Hours",
|
|
||||||
"currency_symbol": "$",
|
|
||||||
"jobs": "Jobs"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
@@ -2927,6 +2938,8 @@
|
|||||||
"vendor": "Vendor"
|
"vendor": "Vendor"
|
||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
|
"adp_payroll_flat": "ADP Payroll - Flat Rate",
|
||||||
|
"adp_payroll_straight": "ADP Payroll - Straight Time",
|
||||||
"anticipated_revenue": "Anticipated Revenue",
|
"anticipated_revenue": "Anticipated Revenue",
|
||||||
"ar_aging": "AR Aging",
|
"ar_aging": "AR Aging",
|
||||||
"attendance_detail": "Attendance (All Employees)",
|
"attendance_detail": "Attendance (All Employees)",
|
||||||
@@ -3418,6 +3431,18 @@
|
|||||||
"vehicledetail": "Vehicle Details {{vehicle}} | {{app}}",
|
"vehicledetail": "Vehicle Details {{vehicle}} | {{app}}",
|
||||||
"vehicles": "All Vehicles | {{app}}"
|
"vehicles": "All Vehicles | {{app}}"
|
||||||
},
|
},
|
||||||
|
"trello": {
|
||||||
|
"labels": {
|
||||||
|
"add_card": "Add Card",
|
||||||
|
"add_lane": "Add Lane",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"delete_lane": "Delete Lane",
|
||||||
|
"description": "Description",
|
||||||
|
"label": "Label",
|
||||||
|
"lane_actions": "Lane Actions",
|
||||||
|
"title": "Title"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tt_approvals": {
|
"tt_approvals": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"approveselected": "Approve Selected"
|
"approveselected": "Approve Selected"
|
||||||
@@ -3556,18 +3581,6 @@
|
|||||||
"validation": {
|
"validation": {
|
||||||
"unique_vendor_name": "You must enter a unique vendor name."
|
"unique_vendor_name": "You must enter a unique vendor name."
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"trello": {
|
|
||||||
"labels": {
|
|
||||||
"add_card": "Add Card",
|
|
||||||
"add_lane": "Add Lane",
|
|
||||||
"delete_lane": "Delete Lane",
|
|
||||||
"lane_actions": "Lane Actions",
|
|
||||||
"title": "Title",
|
|
||||||
"description": "Description",
|
|
||||||
"label": "Label",
|
|
||||||
"cancel": "Cancel"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,9 +270,9 @@
|
|||||||
"testrender": ""
|
"testrender": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"creatingdefaultview": "",
|
||||||
"loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico.",
|
"loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico.",
|
||||||
"saving": "",
|
"saving": ""
|
||||||
"creatingdefaultview": ""
|
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
"ReceivableCustomField": "",
|
"ReceivableCustomField": "",
|
||||||
@@ -285,19 +285,21 @@
|
|||||||
},
|
},
|
||||||
"appt_length": "",
|
"appt_length": "",
|
||||||
"attach_pdf_to_email": "",
|
"attach_pdf_to_email": "",
|
||||||
|
"batchid": "",
|
||||||
"bill_allow_post_to_closed": "",
|
"bill_allow_post_to_closed": "",
|
||||||
"bill_federal_tax_rate": "",
|
"bill_federal_tax_rate": "",
|
||||||
"bill_local_tax_rate": "",
|
"bill_local_tax_rate": "",
|
||||||
"bill_state_tax_rate": "",
|
"bill_state_tax_rate": "",
|
||||||
"city": "",
|
"city": "",
|
||||||
"closingperiod": "",
|
"closingperiod": "",
|
||||||
|
"companycode": "",
|
||||||
"country": "",
|
"country": "",
|
||||||
"dailybodytarget": "",
|
"dailybodytarget": "",
|
||||||
"dailypainttarget": "",
|
"dailypainttarget": "",
|
||||||
"default_adjustment_rate": "",
|
"default_adjustment_rate": "",
|
||||||
"deliver": {
|
"deliver": {
|
||||||
"templates": "",
|
"require_actual_delivery_date": "",
|
||||||
"require_actual_delivery_date": ""
|
"templates": ""
|
||||||
},
|
},
|
||||||
"dms": {
|
"dms": {
|
||||||
"apcontrol": "",
|
"apcontrol": "",
|
||||||
@@ -330,6 +332,10 @@
|
|||||||
"next_contact_hours": "",
|
"next_contact_hours": "",
|
||||||
"templates": ""
|
"templates": ""
|
||||||
},
|
},
|
||||||
|
"intellipay_config": {
|
||||||
|
"cash_discount_percentage": "",
|
||||||
|
"enable_cash_discount": ""
|
||||||
|
},
|
||||||
"invoice_federal_tax_rate": "",
|
"invoice_federal_tax_rate": "",
|
||||||
"invoice_local_tax_rate": "",
|
"invoice_local_tax_rate": "",
|
||||||
"invoice_state_tax_rate": "",
|
"invoice_state_tax_rate": "",
|
||||||
@@ -661,6 +667,8 @@
|
|||||||
"filehandlers": "",
|
"filehandlers": "",
|
||||||
"insurancecos": "",
|
"insurancecos": "",
|
||||||
"intakechecklist": "",
|
"intakechecklist": "",
|
||||||
|
"intellipay": "",
|
||||||
|
"intellipay_cash_discount": "",
|
||||||
"jobstatuses": "",
|
"jobstatuses": "",
|
||||||
"laborrates": "",
|
"laborrates": "",
|
||||||
"licensing": "",
|
"licensing": "",
|
||||||
@@ -700,10 +708,10 @@
|
|||||||
"workingdays": ""
|
"workingdays": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"save": "",
|
|
||||||
"unsavedchanges": "",
|
|
||||||
"areyousure": "",
|
"areyousure": "",
|
||||||
"defaultviewcreated": ""
|
"defaultviewcreated": "",
|
||||||
|
"save": "",
|
||||||
|
"unsavedchanges": ""
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"centermustexist": "",
|
"centermustexist": "",
|
||||||
@@ -1133,8 +1141,8 @@
|
|||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"defaults": "defaults",
|
|
||||||
"add": "",
|
"add": "",
|
||||||
|
"autoupdate": "",
|
||||||
"calculate": "",
|
"calculate": "",
|
||||||
"cancel": "",
|
"cancel": "",
|
||||||
"clear": "",
|
"clear": "",
|
||||||
@@ -1142,6 +1150,8 @@
|
|||||||
"copied": "",
|
"copied": "",
|
||||||
"copylink": "",
|
"copylink": "",
|
||||||
"create": "",
|
"create": "",
|
||||||
|
"defaults": "defaults",
|
||||||
|
"delay": "",
|
||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
"deleteall": "",
|
"deleteall": "",
|
||||||
"deselectall": "",
|
"deselectall": "",
|
||||||
@@ -1153,10 +1163,12 @@
|
|||||||
"print": "",
|
"print": "",
|
||||||
"refresh": "",
|
"refresh": "",
|
||||||
"remove": "",
|
"remove": "",
|
||||||
|
"remove_alert": "",
|
||||||
"reset": " Restablecer a original.",
|
"reset": " Restablecer a original.",
|
||||||
"resetpassword": "",
|
"resetpassword": "",
|
||||||
"save": "Salvar",
|
"save": "Salvar",
|
||||||
"saveandnew": "",
|
"saveandnew": "",
|
||||||
|
"saveas": "",
|
||||||
"selectall": "",
|
"selectall": "",
|
||||||
"send": "",
|
"send": "",
|
||||||
"sendbysms": "",
|
"sendbysms": "",
|
||||||
@@ -1164,9 +1176,7 @@
|
|||||||
"submit": "",
|
"submit": "",
|
||||||
"tryagain": "",
|
"tryagain": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"viewreleasenotes": "",
|
"viewreleasenotes": ""
|
||||||
"remove_alert": "",
|
|
||||||
"saveas": ""
|
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"fcm": "",
|
"fcm": "",
|
||||||
@@ -1181,7 +1191,6 @@
|
|||||||
"vehicle": ""
|
"vehicle": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"unsavedchanges": "",
|
|
||||||
"actions": "Comportamiento",
|
"actions": "Comportamiento",
|
||||||
"areyousure": "",
|
"areyousure": "",
|
||||||
"barcode": "código de barras",
|
"barcode": "código de barras",
|
||||||
@@ -1250,6 +1259,7 @@
|
|||||||
"tuesday": "",
|
"tuesday": "",
|
||||||
"tvmode": "",
|
"tvmode": "",
|
||||||
"unknown": "Desconocido",
|
"unknown": "Desconocido",
|
||||||
|
"unsavedchanges": "",
|
||||||
"username": "",
|
"username": "",
|
||||||
"view": "",
|
"view": "",
|
||||||
"wednesday": "",
|
"wednesday": "",
|
||||||
@@ -1363,6 +1373,7 @@
|
|||||||
},
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
|
"create_short_link": "",
|
||||||
"goback": "",
|
"goback": "",
|
||||||
"proceedtopayment": "",
|
"proceedtopayment": "",
|
||||||
"refundpayment": ""
|
"refundpayment": ""
|
||||||
@@ -2739,41 +2750,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"constants": {
|
|
||||||
"main_profile": ""
|
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"small": "",
|
|
||||||
"medium": "",
|
|
||||||
"large": "",
|
|
||||||
"vertical": "",
|
|
||||||
"horizontal": ""
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"layout": "",
|
|
||||||
"information": "",
|
|
||||||
"statistics_title": "",
|
|
||||||
"board_settings": "",
|
|
||||||
"filters_title": "",
|
|
||||||
"filters": {
|
|
||||||
"md_ins_cos": "",
|
|
||||||
"md_estimators": ""
|
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"total_hours_in_production": "",
|
|
||||||
"total_lab_in_production": "",
|
|
||||||
"total_lar_in_production": "",
|
|
||||||
"total_amount_in_production": "",
|
|
||||||
"jobs_in_production": "",
|
|
||||||
"total_hours_on_board": "",
|
|
||||||
"total_lab_on_board": "",
|
|
||||||
"total_lar_on_board": "",
|
|
||||||
"total_amount_on_board": "",
|
|
||||||
"total_jobs_on_board": "",
|
|
||||||
"tasks_in_production": "",
|
|
||||||
"tasks_on_board": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"actions": {
|
"actions": {
|
||||||
"addcolumns": "",
|
"addcolumns": "",
|
||||||
"bodypriority-clear": "",
|
"bodypriority-clear": "",
|
||||||
@@ -2788,29 +2764,23 @@
|
|||||||
"suspend": "",
|
"suspend": "",
|
||||||
"unsuspend": ""
|
"unsuspend": ""
|
||||||
},
|
},
|
||||||
|
"constants": {
|
||||||
|
"main_profile": ""
|
||||||
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"boardupdate": "",
|
"boardupdate": "",
|
||||||
"removing": "",
|
|
||||||
"settings": "",
|
|
||||||
"name_exists": "",
|
"name_exists": "",
|
||||||
"name_required": ""
|
"name_required": "",
|
||||||
|
"removing": "",
|
||||||
|
"settings": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"kiosk_mode": "",
|
|
||||||
"on": "",
|
|
||||||
"off": "",
|
|
||||||
"wide": "",
|
|
||||||
"tall": "",
|
|
||||||
"vertical": "",
|
|
||||||
"horizontal": "",
|
|
||||||
"orientation": "",
|
|
||||||
"card_size": "",
|
|
||||||
"model_info": "",
|
|
||||||
"actual_in": "",
|
"actual_in": "",
|
||||||
|
"addnewprofile": "",
|
||||||
"alert": "",
|
"alert": "",
|
||||||
"tasks": "",
|
|
||||||
"alertoff": "",
|
"alertoff": "",
|
||||||
"alerton": "",
|
"alerton": "",
|
||||||
|
"alerts": "",
|
||||||
"ats": "",
|
"ats": "",
|
||||||
"bodyhours": "",
|
"bodyhours": "",
|
||||||
"bodypriority": "",
|
"bodypriority": "",
|
||||||
@@ -2820,6 +2790,7 @@
|
|||||||
"qbo_usa": ""
|
"qbo_usa": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"card_size": "",
|
||||||
"cardcolor": "",
|
"cardcolor": "",
|
||||||
"cardsettings": "",
|
"cardsettings": "",
|
||||||
"clm_no": "",
|
"clm_no": "",
|
||||||
@@ -2828,48 +2799,88 @@
|
|||||||
"detailpriority": "",
|
"detailpriority": "",
|
||||||
"employeeassignments": "",
|
"employeeassignments": "",
|
||||||
"employeesearch": "",
|
"employeesearch": "",
|
||||||
|
"estimator": "",
|
||||||
|
"horizontal": "",
|
||||||
"ins_co_nm": "",
|
"ins_co_nm": "",
|
||||||
"jobdetail": "",
|
"jobdetail": "",
|
||||||
|
"kiosk_mode": "",
|
||||||
"laborhrs": "",
|
"laborhrs": "",
|
||||||
"legend": "",
|
"legend": "",
|
||||||
|
"model_info": "",
|
||||||
"note": "",
|
"note": "",
|
||||||
|
"off": "",
|
||||||
|
"on": "",
|
||||||
|
"orientation": "",
|
||||||
"ownr_nm": "",
|
"ownr_nm": "",
|
||||||
"paintpriority": "",
|
"paintpriority": "",
|
||||||
"partsstatus": "",
|
"partsstatus": "",
|
||||||
"estimator": "",
|
|
||||||
"subtotal": "",
|
|
||||||
"production_note": "",
|
"production_note": "",
|
||||||
"refinishhours": "",
|
"refinishhours": "",
|
||||||
"scheduled_completion": "",
|
"scheduled_completion": "",
|
||||||
"selectview": "",
|
"selectview": "",
|
||||||
"stickyheader": "",
|
"stickyheader": "",
|
||||||
"sublets": "",
|
"sublets": "",
|
||||||
|
"subtotal": "",
|
||||||
|
"tall": "",
|
||||||
|
"tasks": "",
|
||||||
"totalhours": "",
|
"totalhours": "",
|
||||||
"touchtime": "",
|
"touchtime": "",
|
||||||
|
"vertical": "",
|
||||||
"viewname": "",
|
"viewname": "",
|
||||||
"alerts": "",
|
"wide": ""
|
||||||
"addnewprofile": ""
|
},
|
||||||
|
"options": {
|
||||||
|
"horizontal": "",
|
||||||
|
"large": "",
|
||||||
|
"medium": "",
|
||||||
|
"small": "",
|
||||||
|
"vertical": ""
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"board_settings": "",
|
||||||
|
"filters": {
|
||||||
|
"md_estimators": "",
|
||||||
|
"md_ins_cos": ""
|
||||||
|
},
|
||||||
|
"filters_title": "",
|
||||||
|
"information": "",
|
||||||
|
"layout": "",
|
||||||
|
"statistics": {
|
||||||
|
"jobs_in_production": "",
|
||||||
|
"tasks_in_production": "",
|
||||||
|
"tasks_on_board": "",
|
||||||
|
"total_amount_in_production": "",
|
||||||
|
"total_amount_on_board": "",
|
||||||
|
"total_hours_in_production": "",
|
||||||
|
"total_hours_on_board": "",
|
||||||
|
"total_jobs_on_board": "",
|
||||||
|
"total_lab_in_production": "",
|
||||||
|
"total_lab_on_board": "",
|
||||||
|
"total_lar_in_production": "",
|
||||||
|
"total_lar_on_board": ""
|
||||||
|
},
|
||||||
|
"statistics_title": ""
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"currency_symbol": "",
|
||||||
|
"hours": "",
|
||||||
|
"jobs": "",
|
||||||
|
"jobs_in_production": "",
|
||||||
|
"tasks": "",
|
||||||
|
"tasks_in_production": "",
|
||||||
|
"tasks_on_board": "",
|
||||||
|
"total_amount_in_production": "",
|
||||||
|
"total_amount_on_board": "",
|
||||||
|
"total_hours_in_production": "",
|
||||||
|
"total_hours_on_board": "",
|
||||||
|
"total_jobs_on_board": "",
|
||||||
|
"total_lab_in_production": "",
|
||||||
|
"total_lab_on_board": "",
|
||||||
|
"total_lar_in_production": "",
|
||||||
|
"total_lar_on_board": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"removed": ""
|
"removed": ""
|
||||||
},
|
|
||||||
"statistics": {
|
|
||||||
"total_hours_in_production": "",
|
|
||||||
"total_lab_in_production": "",
|
|
||||||
"total_lar_in_production": "",
|
|
||||||
"total_amount_in_production": "",
|
|
||||||
"jobs_in_production": "",
|
|
||||||
"total_hours_on_board": "",
|
|
||||||
"total_lab_on_board": "",
|
|
||||||
"total_lar_on_board": "",
|
|
||||||
"total_amount_on_board": "",
|
|
||||||
"total_jobs_on_board": "",
|
|
||||||
"tasks_in_production": "",
|
|
||||||
"tasks_on_board": "",
|
|
||||||
"tasks": "",
|
|
||||||
"hours": "",
|
|
||||||
"currency_symbol": "",
|
|
||||||
"jobs": ""
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
@@ -2927,6 +2938,8 @@
|
|||||||
"vendor": ""
|
"vendor": ""
|
||||||
},
|
},
|
||||||
"templates": {
|
"templates": {
|
||||||
|
"adp_payroll_flat": "",
|
||||||
|
"adp_payroll_straight": "",
|
||||||
"anticipated_revenue": "",
|
"anticipated_revenue": "",
|
||||||
"ar_aging": "",
|
"ar_aging": "",
|
||||||
"attendance_detail": "",
|
"attendance_detail": "",
|
||||||
@@ -3418,6 +3431,18 @@
|
|||||||
"vehicledetail": "Detalles del vehículo {{vehicle}} | {{app}}",
|
"vehicledetail": "Detalles del vehículo {{vehicle}} | {{app}}",
|
||||||
"vehicles": "Todos los vehiculos | {{app}}"
|
"vehicles": "Todos los vehiculos | {{app}}"
|
||||||
},
|
},
|
||||||
|
"trello": {
|
||||||
|
"labels": {
|
||||||
|
"add_card": "",
|
||||||
|
"add_lane": "",
|
||||||
|
"cancel": "",
|
||||||
|
"delete_lane": "",
|
||||||
|
"description": "",
|
||||||
|
"label": "",
|
||||||
|
"lane_actions": "",
|
||||||
|
"title": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"tt_approvals": {
|
"tt_approvals": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"approveselected": ""
|
"approveselected": ""
|
||||||
@@ -3556,18 +3581,6 @@
|
|||||||
"validation": {
|
"validation": {
|
||||||
"unique_vendor_name": ""
|
"unique_vendor_name": ""
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"trello": {
|
|
||||||
"labels": {
|
|
||||||
"add_card": "",
|
|
||||||
"add_lane": "",
|
|
||||||
"delete_lane": "",
|
|
||||||
"lane_actions": "",
|
|
||||||
"title": "",
|
|
||||||
"description": "",
|
|
||||||
"label": "",
|
|
||||||
"cancel": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2158,6 +2158,32 @@ export const TemplateList = (type, context) => {
|
|||||||
field: i18n.t("tasks.fields.created_at")
|
field: i18n.t("tasks.fields.created_at")
|
||||||
},
|
},
|
||||||
group: "jobs"
|
group: "jobs"
|
||||||
|
},
|
||||||
|
adp_payroll_flat: {
|
||||||
|
title: i18n.t("reportcenter.templates.adp_payroll_flat"),
|
||||||
|
subject: i18n.t("reportcenter.templates.adp_payroll_flat"),
|
||||||
|
key: "adp_payroll_flat",
|
||||||
|
reporttype: "text",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||||
|
field: i18n.t("timetickets.fields.committed_at")
|
||||||
|
},
|
||||||
|
group: "payroll",
|
||||||
|
adp_payroll: true
|
||||||
|
},
|
||||||
|
adp_payroll_straight: {
|
||||||
|
title: i18n.t("reportcenter.templates.adp_payroll_straight"),
|
||||||
|
subject: i18n.t("reportcenter.templates.adp_payroll_straight"),
|
||||||
|
key: "adp_payroll_straight",
|
||||||
|
reporttype: "text",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.timetickets"),
|
||||||
|
field: i18n.t("timetickets.fields.date")
|
||||||
|
},
|
||||||
|
group: "payroll",
|
||||||
|
adp_payroll: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
84
client/src/utils/countdownHook.js
Normal file
84
client/src/utils/countdownHook.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const useCountDown = (timeToCount = 60 * 1000, interval = 1000) => {
|
||||||
|
const [timeLeft, setTimeLeft] = React.useState(0);
|
||||||
|
const timer = React.useRef({});
|
||||||
|
|
||||||
|
const run = (ts) => {
|
||||||
|
if (!timer.current.started) {
|
||||||
|
timer.current.started = ts;
|
||||||
|
timer.current.lastInterval = ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
const localInterval = Math.min(interval, timer.current.timeLeft || Infinity);
|
||||||
|
if (ts - timer.current.lastInterval >= localInterval) {
|
||||||
|
timer.current.lastInterval += localInterval;
|
||||||
|
setTimeLeft((timeLeft) => {
|
||||||
|
timer.current.timeLeft = timeLeft - localInterval;
|
||||||
|
return timer.current.timeLeft;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts - timer.current.started < timer.current.timeToCount) {
|
||||||
|
timer.current.requestId = window.requestAnimationFrame(run);
|
||||||
|
} else {
|
||||||
|
timer.current = {};
|
||||||
|
setTimeLeft(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const start = React.useCallback(
|
||||||
|
(ttc) => {
|
||||||
|
window.cancelAnimationFrame(timer.current.requestId);
|
||||||
|
|
||||||
|
const newTimeToCount = ttc !== undefined ? ttc : timeToCount;
|
||||||
|
timer.current.started = null;
|
||||||
|
timer.current.lastInterval = null;
|
||||||
|
timer.current.timeToCount = newTimeToCount;
|
||||||
|
timer.current.requestId = window.requestAnimationFrame(run);
|
||||||
|
|
||||||
|
setTimeLeft(newTimeToCount);
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const pause = React.useCallback(() => {
|
||||||
|
window.cancelAnimationFrame(timer.current.requestId);
|
||||||
|
timer.current.started = null;
|
||||||
|
timer.current.lastInterval = null;
|
||||||
|
timer.current.timeToCount = timer.current.timeLeft;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const resume = React.useCallback(
|
||||||
|
() => {
|
||||||
|
if (!timer.current.started && timer.current.timeLeft > 0) {
|
||||||
|
window.cancelAnimationFrame(timer.current.requestId);
|
||||||
|
timer.current.requestId = window.requestAnimationFrame(run);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const reset = React.useCallback(() => {
|
||||||
|
if (timer.current.timeLeft) {
|
||||||
|
window.cancelAnimationFrame(timer.current.requestId);
|
||||||
|
timer.current = {};
|
||||||
|
setTimeLeft(0);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const actions = React.useMemo(
|
||||||
|
() => ({ start, pause, resume, reset }), // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return () => window.cancelAnimationFrame(timer.current.requestId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [timeLeft, actions];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCountDown;
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
export const BETA_KEY = "betaSwitchImex";
|
|
||||||
|
|
||||||
export const checkBeta = () => {
|
|
||||||
const cookie = document.cookie.split("; ").find((row) => row.startsWith(BETA_KEY));
|
|
||||||
return cookie ? cookie.split("=")[1] === "true" : false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setBeta = (value) => {
|
|
||||||
const domain = window.location.hostname.split(".").slice(-2).join(".");
|
|
||||||
document.cookie = `${BETA_KEY}=${value}; path=/; domain=.${domain}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleBeta = () => {
|
|
||||||
if (window.location.hostname.startsWith("localhost")) {
|
|
||||||
console.log("Not on beta or test, so no need to handle beta.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isBeta = checkBeta();
|
|
||||||
const currentHostName = window.location.hostname;
|
|
||||||
|
|
||||||
// Determine if the host name starts with "beta" or "www.beta"
|
|
||||||
const isBetaHost = currentHostName.startsWith("beta.");
|
|
||||||
const isBetaHostWithWWW = currentHostName.startsWith("www.beta.");
|
|
||||||
|
|
||||||
if (isBeta) {
|
|
||||||
// If beta is on and we are not on a beta domain, redirect to the beta version
|
|
||||||
if (!isBetaHost && !isBetaHostWithWWW) {
|
|
||||||
const newHostName = currentHostName.startsWith("www.")
|
|
||||||
? `www.beta.${currentHostName.replace(/^www\./, "")}`
|
|
||||||
: `beta.${currentHostName}`;
|
|
||||||
const href = `${window.location.protocol}//${newHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
||||||
window.location.replace(href);
|
|
||||||
}
|
|
||||||
// Otherwise, if beta is on and we're already on a beta domain, stay there
|
|
||||||
} else {
|
|
||||||
// If beta is off and we are on a beta domain, redirect to the non-beta version
|
|
||||||
if (isBetaHost || isBetaHostWithWWW) {
|
|
||||||
const newHostName = currentHostName.replace(/^www\.beta\./, "www.").replace(/^beta\./, "");
|
|
||||||
const href = `${window.location.protocol}//${newHostName}${window.location.pathname}${window.location.search}${window.location.hash}`;
|
|
||||||
window.location.replace(href);
|
|
||||||
}
|
|
||||||
// Otherwise, if beta is off and we're not on a beta domain, stay there
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default handleBeta;
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
version: 2
|
version: 2
|
||||||
endpoint: https://db.dev.bodyshop.app
|
endpoint: https://db.dev.imex.online
|
||||||
admin_secret: Dev-BodyShopApp!
|
admin_secret: Dev-BodyShopApp!
|
||||||
metadata_directory: metadata
|
metadata_directory: metadata
|
||||||
actions:
|
actions:
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
- name: AutoHouse Data Pump
|
||||||
|
webhook: '{{HASURA_API_URL}}/data/ah'
|
||||||
|
schedule: 0 6 * * *
|
||||||
|
include_in_metadata: true
|
||||||
|
payload: {}
|
||||||
|
headers:
|
||||||
|
- name: x-imex-auth
|
||||||
|
value_from_env: DATAPUMP_AUTH
|
||||||
|
- name: Claimscorp Data Pump
|
||||||
|
webhook: '{{HASURA_API_URL}}/data/cc'
|
||||||
|
schedule: 30 6 * * *
|
||||||
|
include_in_metadata: true
|
||||||
|
payload: {}
|
||||||
|
headers:
|
||||||
|
- name: x-imex-auth
|
||||||
|
value_from_env: DATAPUMP_AUTH
|
||||||
- name: Kaizen Data Pump
|
- name: Kaizen Data Pump
|
||||||
webhook: '{{HASURA_API_URL}}/data/kaizen'
|
webhook: '{{HASURA_API_URL}}/data/kaizen'
|
||||||
schedule: 30 5 * * *
|
schedule: 30 5 * * *
|
||||||
|
|||||||
@@ -939,6 +939,7 @@
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- intellipay_config
|
||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- jobsizelimit
|
- jobsizelimit
|
||||||
- last_name_first
|
- last_name_first
|
||||||
@@ -1040,6 +1041,7 @@
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- intellipay_config
|
||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- last_name_first
|
- last_name_first
|
||||||
- localmediaserverhttp
|
- localmediaserverhttp
|
||||||
@@ -4240,6 +4242,63 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
event_triggers:
|
event_triggers:
|
||||||
|
- name: job_modified
|
||||||
|
definition:
|
||||||
|
enable_manual: false
|
||||||
|
update:
|
||||||
|
columns:
|
||||||
|
- clm_no
|
||||||
|
- v_make_desc
|
||||||
|
- date_next_contact
|
||||||
|
- status
|
||||||
|
- employee_csr
|
||||||
|
- employee_prep
|
||||||
|
- clm_total
|
||||||
|
- suspended
|
||||||
|
- employee_body
|
||||||
|
- ro_number
|
||||||
|
- actual_in
|
||||||
|
- ownr_co_nm
|
||||||
|
- v_model_yr
|
||||||
|
- comment
|
||||||
|
- job_totals
|
||||||
|
- v_vin
|
||||||
|
- ownr_fn
|
||||||
|
- scheduled_completion
|
||||||
|
- special_coverage_policy
|
||||||
|
- v_color
|
||||||
|
- ca_gst_registrant
|
||||||
|
- scheduled_delivery
|
||||||
|
- actual_delivery
|
||||||
|
- actual_completion
|
||||||
|
- kanbanparent
|
||||||
|
- est_ct_fn
|
||||||
|
- employee_refinish
|
||||||
|
- ownr_ph1
|
||||||
|
- date_last_contacted
|
||||||
|
- alt_transport
|
||||||
|
- inproduction
|
||||||
|
- est_ct_ln
|
||||||
|
- production_vars
|
||||||
|
- category
|
||||||
|
- v_model_desc
|
||||||
|
- date_invoiced
|
||||||
|
- est_co_nm
|
||||||
|
- ownr_ln
|
||||||
|
retry_conf:
|
||||||
|
interval_sec: 10
|
||||||
|
num_retries: 0
|
||||||
|
timeout_sec: 60
|
||||||
|
webhook_from_env: HASURA_API_URL
|
||||||
|
headers:
|
||||||
|
- name: event-secret
|
||||||
|
value_from_env: EVENT_SECRET
|
||||||
|
request_transform:
|
||||||
|
method: POST
|
||||||
|
query_params: {}
|
||||||
|
template_engine: Kriti
|
||||||
|
url: '{{$base_url}}/job/job-updated'
|
||||||
|
version: 2
|
||||||
- name: job_status_transition
|
- name: job_status_transition
|
||||||
definition:
|
definition:
|
||||||
enable_manual: true
|
enable_manual: true
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "intellipay_config" jsonb
|
||||||
|
-- not null default jsonb_build_object();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "intellipay_config" jsonb
|
||||||
|
not null default jsonb_build_object();
|
||||||
@@ -94,7 +94,10 @@ exports.default = async (req, res) => {
|
|||||||
ret.push({
|
ret.push({
|
||||||
billid: bill.id,
|
billid: bill.id,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: (error && error.authResponse && error.authResponse.body) || (error && error.message)
|
errorMessage:
|
||||||
|
(error && error.authResponse && error.authResponse.body) ||
|
||||||
|
error.response?.data?.Fault?.Error.map((e) => e.Detail).join(", ") ||
|
||||||
|
(error && error.message)
|
||||||
});
|
});
|
||||||
|
|
||||||
//Add the export log error.
|
//Add the export log error.
|
||||||
@@ -209,14 +212,14 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor, bodyshop)
|
|||||||
AccountBasedExpenseLineDetail: {
|
AccountBasedExpenseLineDetail: {
|
||||||
...(bill.job.class ? { ClassRef: { value: classes[bill.job.class] } } : {}),
|
...(bill.job.class ? { ClassRef: { value: classes[bill.job.class] } } : {}),
|
||||||
AccountRef: {
|
AccountRef: {
|
||||||
value: accounts[bodyshop.md_responsibility_centers.taxes.federal.accountdesc]
|
value: accounts[bodyshop.md_responsibility_centers.taxes.federal_itc.accountdesc]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
Amount: Dinero({
|
Amount: Dinero({
|
||||||
amount: Math.round(
|
amount: Math.round(
|
||||||
bill.billlines.reduce((acc, val) => {
|
bill.billlines.reduce((acc, val) => {
|
||||||
return acc + val.actual_cost * val.quantity;
|
return acc + val.applicable_taxes?.federal ? (val.actual_cost * val.quantity ?? 0) : 0;
|
||||||
}, 0) * 100
|
}, 0) * 100
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -274,6 +277,8 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor, bodyshop)
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||||
error: error, //(error && error.authResponse && error.authResponse.body) || (error && error.message),
|
error: error, //(error && error.authResponse && error.authResponse.body) || (error && error.message),
|
||||||
|
validationError: JSON.stringify(error?.response?.data),
|
||||||
|
accountmeta: JSON.stringify({ accounts, taxCodes, classes }),
|
||||||
method: "InsertBill"
|
method: "InsertBill"
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -179,7 +179,11 @@ exports.default = async (req, res) => {
|
|||||||
ret.push({
|
ret.push({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: (error && error.authResponse && error.authResponse.body) || (error && error.message)
|
errorMessage:
|
||||||
|
error?.authResponse?.body ||
|
||||||
|
error?.response?.data?.Fault?.Error.map((e) => e.Detail).join(", ") ||
|
||||||
|
error?.response?.data ||
|
||||||
|
error?.message
|
||||||
});
|
});
|
||||||
console.log(error);
|
console.log(error);
|
||||||
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
|
||||||
@@ -254,7 +258,6 @@ async function InsertInsuranceCo(oauthClient, qbo_realmId, req, job, bodyshop) {
|
|||||||
throw new Error(
|
throw new Error(
|
||||||
`Insurance Company '${job.ins_co_nm}' not found in shop configuration. Please make sure it exists or change the insurance company name on the job to one that exists.`
|
`Insurance Company '${job.ins_co_nm}' not found in shop configuration. Please make sure it exists or change the insurance company name on the job to one that exists.`
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const Customer = {
|
const Customer = {
|
||||||
DisplayName: job.ins_co_nm.trim(),
|
DisplayName: job.ins_co_nm.trim(),
|
||||||
@@ -575,7 +578,9 @@ async function InsertInvoice(oauthClient, qbo_realmId, req, job, bodyshop, paren
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-receivables-error", "DEBUG", req.user.email, job.id, {
|
logger.log("qbo-receivables-error", "DEBUG", req.user.email, job.id, {
|
||||||
error,
|
error,
|
||||||
method: "InsertOwner"
|
method: "InsertInvoice",
|
||||||
|
validationError: JSON.stringify(error?.response?.data),
|
||||||
|
accountmeta: JSON.stringify({ items, taxCodes, classes })
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
|
// Only process if in production environment.
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Query for the List of Bodyshop Clients.
|
//Query for the List of Bodyshop Clients.
|
||||||
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
logger.log("autohouse-start", "DEBUG", "api", null, null);
|
||||||
const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
|
const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
|
// Only process if in production environment.
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Query for the List of Bodyshop Clients.
|
//Query for the List of Bodyshop Clients.
|
||||||
logger.log("claimscorp-start", "DEBUG", "api", null, null);
|
logger.log("claimscorp-start", "DEBUG", "api", null, null);
|
||||||
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS);
|
const { bodyshops } = await client.request(queries.GET_CLAIMSCORP_SHOPS);
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ const ftpSetup = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
|
// Only process if in production environment.
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Query for the List of Bodyshop Clients.
|
//Query for the List of Bodyshop Clients.
|
||||||
logger.log("kaizen-start", "DEBUG", "api", null, null);
|
logger.log("kaizen-start", "DEBUG", "api", null, null);
|
||||||
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
const kaizenShopsIDs = ["SUMMIT", "STRATHMORE", "SUNRIDGE", "SHAW"];
|
||||||
|
|||||||
@@ -96,7 +96,21 @@ const sendServerEmail = async ({ subject, text }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendTaskEmail = async ({ to, subject, text, attachments }) => {
|
const sendProManagerWelcomeEmail = async ({to, subject, html}) => {
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: `ProManager <noreply@promanager.web-est.com>`,
|
||||||
|
to,
|
||||||
|
subject,
|
||||||
|
html
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
logger.log("server-email-failure", "error", null, null, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendTaskEmail = async ({ to, subject, type = "text", html, text, attachments }) => {
|
||||||
try {
|
try {
|
||||||
transporter.sendMail(
|
transporter.sendMail(
|
||||||
{
|
{
|
||||||
@@ -107,7 +121,7 @@ const sendTaskEmail = async ({ to, subject, text, attachments }) => {
|
|||||||
}),
|
}),
|
||||||
to: to,
|
to: to,
|
||||||
subject: subject,
|
subject: subject,
|
||||||
text: text,
|
...(type === "text" ? { text } : { html }),
|
||||||
attachments: attachments || null
|
attachments: attachments || null
|
||||||
},
|
},
|
||||||
(err, info) => {
|
(err, info) => {
|
||||||
@@ -309,5 +323,6 @@ module.exports = {
|
|||||||
sendEmail,
|
sendEmail,
|
||||||
sendServerEmail,
|
sendServerEmail,
|
||||||
sendTaskEmail,
|
sendTaskEmail,
|
||||||
|
sendProManagerWelcomeEmail,
|
||||||
emailBounce
|
emailBounce
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -94,8 +94,9 @@ const formatPriority = (priority) => {
|
|||||||
* @param taskId
|
* @param taskId
|
||||||
* @returns {{header, body: string, subHeader: string}}
|
* @returns {{header, body: string, subHeader: string}}
|
||||||
*/
|
*/
|
||||||
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
|
||||||
const endPoints = InstanceManager({
|
const getEndpoints = () =>
|
||||||
|
InstanceManager({
|
||||||
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
||||||
rome:
|
rome:
|
||||||
bodyshop.convenient_company === "promanager"
|
bodyshop.convenient_company === "promanager"
|
||||||
@@ -106,6 +107,9 @@ const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, j
|
|||||||
? "https//test.romeonline.io"
|
? "https//test.romeonline.io"
|
||||||
: "https://romeonline.io"
|
: "https://romeonline.io"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
||||||
|
const endPoints = getEndpoints();
|
||||||
return {
|
return {
|
||||||
header: title,
|
header: title,
|
||||||
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
||||||
@@ -333,5 +337,6 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
taskAssignedEmail,
|
taskAssignedEmail,
|
||||||
tasksRemindEmail
|
tasksRemindEmail,
|
||||||
|
getEndpoints
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,30 +1,28 @@
|
|||||||
const admin = require("firebase-admin");
|
|
||||||
const logger = require("../utils/logger");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const { auth } = require("firebase-admin");
|
|
||||||
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
});
|
});
|
||||||
const client = require("../graphql-client/graphql-client").client;
|
|
||||||
|
|
||||||
|
const admin = require("firebase-admin");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
const { sendProManagerWelcomeEmail } = require("../email/sendemail");
|
||||||
|
const client = require("../graphql-client/graphql-client").client;
|
||||||
const serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON);
|
const serviceAccount = require(process.env.FIREBASE_ADMINSDK_JSON);
|
||||||
const adminEmail = require("../utils/adminEmail");
|
const generateEmailTemplate = require("../email/generateTemplate");
|
||||||
|
|
||||||
admin.initializeApp({
|
admin.initializeApp({
|
||||||
credential: admin.credential.cert(serviceAccount),
|
credential: admin.credential.cert(serviceAccount),
|
||||||
databaseURL: process.env.FIREBASE_DATABASE_URL
|
databaseURL: process.env.FIREBASE_DATABASE_URL
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.admin = admin;
|
const createUser = async (req, res) => {
|
||||||
|
|
||||||
exports.createUser = async (req, res) => {
|
|
||||||
logger.log("admin-create-user", "ADMIN", req.user.email, null, {
|
logger.log("admin-create-user", "ADMIN", req.user.email, null, {
|
||||||
request: req.body,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const { email, displayName, password, shopid, authlevel } = req.body;
|
const { email, displayName, password, shopid, authlevel, validemail } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userRecord = await admin.auth().createUser({ email, displayName, password });
|
const userRecord = await admin.auth().createUser({ email, displayName, password });
|
||||||
|
|
||||||
@@ -42,6 +40,7 @@ exports.createUser = async (req, res) => {
|
|||||||
user: {
|
user: {
|
||||||
email: email.toLowerCase(),
|
email: email.toLowerCase(),
|
||||||
authid: userRecord.uid,
|
authid: userRecord.uid,
|
||||||
|
validemail,
|
||||||
associations: {
|
associations: {
|
||||||
data: [{ shopid, authlevel, active: true }]
|
data: [{ shopid, authlevel, active: true }]
|
||||||
}
|
}
|
||||||
@@ -58,21 +57,115 @@ exports.createUser = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.updateUser = (req, res) => {
|
const sendPromanagerWelcomeEmail = (req, res) => {
|
||||||
|
const { authid, email } = req.body;
|
||||||
|
|
||||||
|
// Fetch user from Firebase
|
||||||
|
admin
|
||||||
|
.auth()
|
||||||
|
.getUser(authid)
|
||||||
|
.then((userRecord) => {
|
||||||
|
if (!userRecord) {
|
||||||
|
return Promise.reject({ status: 404, message: "User not found in Firebase." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch user data from the database using GraphQL
|
||||||
|
return client.request(
|
||||||
|
`
|
||||||
|
query GET_USER_BY_EMAIL($email: String!) {
|
||||||
|
users(where: { email: { _eq: $email } }) {
|
||||||
|
email
|
||||||
|
validemail
|
||||||
|
associations {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
bodyshop {
|
||||||
|
id
|
||||||
|
convenient_company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
{ email: email.toLowerCase() }
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then((dbUserResult) => {
|
||||||
|
const dbUser = dbUserResult?.users?.[0];
|
||||||
|
if (!dbUser) {
|
||||||
|
return Promise.reject({ status: 404, message: "User not found in database." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate email before proceeding
|
||||||
|
if (!dbUser.validemail) {
|
||||||
|
logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, {
|
||||||
|
message: "User email is not valid, skipping email.",
|
||||||
|
email
|
||||||
|
});
|
||||||
|
return res.status(200).json({ message: "User email is not valid, email not sent." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user's company is ProManager
|
||||||
|
const convenientCompany = dbUser.associations?.[0]?.bodyshop?.convenient_company;
|
||||||
|
if (convenientCompany !== "promanager") {
|
||||||
|
logger.log("admin-send-welcome-email-skip", "ADMIN", req.user.email, null, {
|
||||||
|
message: 'convenient_company is not "promanager", skipping email.',
|
||||||
|
convenientCompany
|
||||||
|
});
|
||||||
|
return res.status(200).json({ message: `convenient_company is not "promanager", email not sent.` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate password reset link
|
||||||
|
return admin
|
||||||
|
.auth()
|
||||||
|
.generatePasswordResetLink(dbUser.email)
|
||||||
|
.then((resetLink) => ({ dbUser, resetLink }));
|
||||||
|
})
|
||||||
|
.then(({ dbUser, resetLink }) => {
|
||||||
|
// Send welcome email (replace with your actual email-sending service)
|
||||||
|
return sendProManagerWelcomeEmail({
|
||||||
|
to: dbUser.email,
|
||||||
|
subject: "Welcome to the ProManager platform.",
|
||||||
|
html: generateEmailTemplate({
|
||||||
|
header: "",
|
||||||
|
subHeader: "",
|
||||||
|
body: `
|
||||||
|
<p>Welcome to the ProManager platform. Please click the link below to reset your password:</p>
|
||||||
|
<p><a href="${resetLink}">Reset your password</a></p>
|
||||||
|
<p>User Details:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Email: ${dbUser.email}</li>
|
||||||
|
</ul>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// Log success and return response
|
||||||
|
logger.log("admin-send-welcome-email", "ADMIN", req.user.email, null, {
|
||||||
|
request: req.body,
|
||||||
|
ioadmin: true,
|
||||||
|
emailSentTo: email
|
||||||
|
});
|
||||||
|
res.status(200).json({ message: "Welcome email sent successfully." });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.log("admin-send-welcome-email-error", "ERROR", req.user.email, null, { error });
|
||||||
|
|
||||||
|
if (!res.headersSent) {
|
||||||
|
res.status(error.status || 500).json({
|
||||||
|
message: error.message || "Error sending welcome email.",
|
||||||
|
error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateUser = (req, res) => {
|
||||||
logger.log("admin-update-user", "ADMIN", req.user.email, null, {
|
logger.log("admin-update-user", "ADMIN", req.user.email, null, {
|
||||||
request: req.body,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
|
||||||
logger.log("admin-update-user-unauthorized", "ERROR", req.user.email, null, {
|
|
||||||
request: req.body,
|
|
||||||
user: req.user
|
|
||||||
});
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
admin
|
admin
|
||||||
.auth()
|
.auth()
|
||||||
.updateUser(
|
.updateUser(
|
||||||
@@ -105,26 +198,45 @@ exports.updateUser = (req, res) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.getUser = (req, res) => {
|
const getUser = (req, res) => {
|
||||||
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
|
logger.log("admin-get-user", "ADMIN", req.user.email, null, {
|
||||||
request: req.body,
|
request: req.body,
|
||||||
ioadmin: true
|
ioadmin: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!adminEmail.includes(req.user.email) && !req.user.ioadmin) {
|
|
||||||
logger.log("admin-update-user-unauthorized", "ERROR", req.user.email, null, {
|
|
||||||
request: req.body,
|
|
||||||
user: req.user
|
|
||||||
});
|
|
||||||
res.sendStatus(404);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
admin
|
admin
|
||||||
.auth()
|
.auth()
|
||||||
.getUser(req.body.uid)
|
.getUser(req.body.uid)
|
||||||
.then((userRecord) => {
|
.then((userRecord) => {
|
||||||
res.json(userRecord);
|
return client
|
||||||
|
.request(
|
||||||
|
`
|
||||||
|
query GET_USER_BY_AUTHID($authid: String!) {
|
||||||
|
users(where: { authid: { _eq: $authid } }) {
|
||||||
|
email
|
||||||
|
validemail
|
||||||
|
associations {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
bodyshop {
|
||||||
|
id
|
||||||
|
convenient_company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{ authid: req.body.uid }
|
||||||
|
)
|
||||||
|
.then((dbUserResult) => {
|
||||||
|
res.json({
|
||||||
|
...userRecord,
|
||||||
|
db: {
|
||||||
|
validemail: dbUserResult?.users?.[0]?.validemail,
|
||||||
|
company: dbUserResult?.users?.[0]?.associations?.[0]?.bodyshop?.convenient_company
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
logger.log("admin-get-user-error", "ERROR", req.user.email, null, {
|
logger.log("admin-get-user-error", "ERROR", req.user.email, null, {
|
||||||
@@ -134,7 +246,7 @@ exports.getUser = (req, res) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.sendNotification = async (req, res) => {
|
const sendNotification = async (req, res) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Send a message to the device corresponding to the provided
|
// Send a message to the device corresponding to the provided
|
||||||
// registration token.
|
// registration token.
|
||||||
@@ -167,7 +279,7 @@ exports.sendNotification = async (req, res) => {
|
|||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.subscribe = async (req, res) => {
|
const subscribe = async (req, res) => {
|
||||||
const result = await admin
|
const result = await admin
|
||||||
.messaging()
|
.messaging()
|
||||||
.subscribeToTopic(req.body.fcm_tokens, `${req.body.imexshopid}-${req.body.type}`);
|
.subscribeToTopic(req.body.fcm_tokens, `${req.body.imexshopid}-${req.body.type}`);
|
||||||
@@ -175,7 +287,7 @@ exports.subscribe = async (req, res) => {
|
|||||||
res.json(result);
|
res.json(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.unsubscribe = async (req, res) => {
|
const unsubscribe = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const result = await admin
|
const result = await admin
|
||||||
.messaging()
|
.messaging()
|
||||||
@@ -187,6 +299,17 @@ exports.unsubscribe = async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
admin,
|
||||||
|
createUser,
|
||||||
|
updateUser,
|
||||||
|
getUser,
|
||||||
|
sendPromanagerWelcomeEmail,
|
||||||
|
sendNotification,
|
||||||
|
subscribe,
|
||||||
|
unsubscribe
|
||||||
|
};
|
||||||
|
|
||||||
//Admin claims code.
|
//Admin claims code.
|
||||||
// const uid = "JEqqYlsadwPEXIiyRBR55fflfko1";
|
// const uid = "JEqqYlsadwPEXIiyRBR55fflfko1";
|
||||||
|
|
||||||
|
|||||||
@@ -2502,6 +2502,13 @@ exports.GET_JOBS_BY_PKS = `query GET_JOBS_BY_PKS($ids: [uuid!]!) {
|
|||||||
jobs(where: {id: {_in: $ids}}) {
|
jobs(where: {id: {_in: $ids}}) {
|
||||||
id
|
id
|
||||||
shopid
|
shopid
|
||||||
|
ro_number
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
v_make_desc
|
||||||
|
v_model_yr
|
||||||
|
v_model_desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ const axios = require("axios");
|
|||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
const InstanceManager = require("../utils/instanceMgr").default;
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
const { sendTaskEmail } = require("../email/sendemail");
|
||||||
|
const generateEmailTemplate = require("../email/generateTemplate");
|
||||||
|
const { getEndpoints } = require("../email/tasksEmails");
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
});
|
});
|
||||||
@@ -129,6 +131,7 @@ exports.generate_payment_url = async (req, res) => {
|
|||||||
//...req.body,
|
//...req.body,
|
||||||
amount: Dinero({ amount: Math.round(req.body.amount * 100) }).toFormat("0.00"),
|
amount: Dinero({ amount: Math.round(req.body.amount * 100) }).toFormat("0.00"),
|
||||||
account: req.body.account,
|
account: req.body.account,
|
||||||
|
comment: req.body.comment,
|
||||||
invoice: req.body.invoice,
|
invoice: req.body.invoice,
|
||||||
createshorturl: true
|
createshorturl: true
|
||||||
//The postback URL is set at the CP teller global terminal settings page.
|
//The postback URL is set at the CP teller global terminal settings page.
|
||||||
@@ -162,7 +165,67 @@ exports.postback = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.invoice) {
|
if (comment) {
|
||||||
|
//Shifted the order to have this first to retain backwards compatibility for the old style of short link.
|
||||||
|
//This has been triggered by IO and may have multiple jobs.
|
||||||
|
const parsedComment = JSON.parse(comment);
|
||||||
|
|
||||||
|
//Adding in the user email to the short pay email.
|
||||||
|
//Need to check this to ensure backwards compatibility for clients that don't update.
|
||||||
|
|
||||||
|
const partialPayments = Array.isArray(parsedComment) ? parsedComment : parsedComment.payments;
|
||||||
|
|
||||||
|
const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, {
|
||||||
|
ids: partialPayments.map((p) => p.jobid)
|
||||||
|
});
|
||||||
|
|
||||||
|
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
||||||
|
paymentInput: partialPayments.map((p) => ({
|
||||||
|
amount: p.amount,
|
||||||
|
transactionid: values.authcode,
|
||||||
|
payer: "Customer",
|
||||||
|
type: values.cardtype,
|
||||||
|
jobid: p.jobid,
|
||||||
|
date: moment(Date.now()),
|
||||||
|
payment_responses: {
|
||||||
|
data: {
|
||||||
|
amount: values.total,
|
||||||
|
bodyshopid: jobs.jobs[0].shopid,
|
||||||
|
jobid: p.jobid,
|
||||||
|
declinereason: "Approved",
|
||||||
|
ext_paymentid: values.paymentid,
|
||||||
|
successful: true,
|
||||||
|
response: values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
logger.log("intellipay-postback-app-success", "DEBUG", req.user?.email, null, {
|
||||||
|
iprequest: values,
|
||||||
|
paymentResult
|
||||||
|
});
|
||||||
|
|
||||||
|
if (values.origin === "OneLink" && parsedComment.userEmail) {
|
||||||
|
//Send an email, it was a text to pay link.
|
||||||
|
const endPoints = getEndpoints();
|
||||||
|
sendTaskEmail({
|
||||||
|
to: parsedComment.userEmail,
|
||||||
|
subject: `New Payment(s) Received - RO ${jobs.jobs.map((j) => j.ro_number).join(", ")}`,
|
||||||
|
type: "html",
|
||||||
|
html: generateEmailTemplate({
|
||||||
|
header: "New Payment(s) Received",
|
||||||
|
subHeader: "",
|
||||||
|
body: jobs.jobs
|
||||||
|
.map(
|
||||||
|
(job) =>
|
||||||
|
`Reference: <a href="${endPoints}/manage/jobs/${job.id}">${job.ro_number || "N/A"}</a> | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()} | $${partialPayments.find((p) => p.jobid === job.id).amount}`
|
||||||
|
)
|
||||||
|
.join("<br/>")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
} else if (values.invoice) {
|
||||||
//This is a link email that's been sent out.
|
//This is a link email that's been sent out.
|
||||||
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
||||||
id: values.invoice
|
id: values.invoice
|
||||||
@@ -198,39 +261,6 @@ exports.postback = async (req, res) => {
|
|||||||
paymentResult
|
paymentResult
|
||||||
});
|
});
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} else if (comment) {
|
|
||||||
//This has been triggered by IO and may have multiple jobs.
|
|
||||||
const partialPayments = JSON.parse(comment);
|
|
||||||
const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, {
|
|
||||||
ids: partialPayments.map((p) => p.jobid)
|
|
||||||
});
|
|
||||||
|
|
||||||
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
|
||||||
paymentInput: partialPayments.map((p) => ({
|
|
||||||
amount: p.amount,
|
|
||||||
transactionid: values.authcode,
|
|
||||||
payer: "Customer",
|
|
||||||
type: values.cardtype,
|
|
||||||
jobid: p.jobid,
|
|
||||||
date: moment(Date.now()),
|
|
||||||
payment_responses: {
|
|
||||||
data: {
|
|
||||||
amount: values.total,
|
|
||||||
bodyshopid: jobs.jobs[0].shopid,
|
|
||||||
jobid: p.jobid,
|
|
||||||
declinereason: "Approved",
|
|
||||||
ext_paymentid: values.paymentid,
|
|
||||||
successful: true,
|
|
||||||
response: values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
logger.log("intellipay-postback-app-success", "DEBUG", req.user?.email, null, {
|
|
||||||
iprequest: values,
|
|
||||||
paymentResult
|
|
||||||
});
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const fb = require("../firebase/firebase-handler");
|
|
||||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||||
const { createAssociation, createShop, updateShop, updateCounter } = require("../admin/adminops");
|
const { createAssociation, createShop, updateShop, updateCounter } = require("../admin/adminops");
|
||||||
|
const { updateUser, getUser, createUser, sendPromanagerWelcomeEmail } = require("../firebase/firebase-handler");
|
||||||
const validateAdminMiddleware = require("../middleware/validateAdminMiddleware");
|
const validateAdminMiddleware = require("../middleware/validateAdminMiddleware");
|
||||||
|
|
||||||
router.use(validateFirebaseIdTokenMiddleware);
|
router.use(validateFirebaseIdTokenMiddleware);
|
||||||
|
router.use(validateAdminMiddleware);
|
||||||
|
|
||||||
router.post("/createassociation", validateAdminMiddleware, createAssociation);
|
router.post("/createassociation", createAssociation);
|
||||||
router.post("/createshop", validateAdminMiddleware, createShop);
|
router.post("/createshop", createShop);
|
||||||
router.post("/updateshop", validateAdminMiddleware, updateShop);
|
router.post("/updateshop", updateShop);
|
||||||
router.post("/updatecounter", validateAdminMiddleware, updateCounter);
|
router.post("/updatecounter", updateCounter);
|
||||||
router.post("/updateuser", fb.updateUser);
|
router.post("/updateuser", updateUser);
|
||||||
router.post("/getuser", fb.getUser);
|
router.post("/getuser", getUser);
|
||||||
router.post("/createuser", fb.createUser);
|
router.post("/createuser", createUser);
|
||||||
|
router.post("/promanagerwelcome", sendPromanagerWelcomeEmail);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user