Merge remote-tracking branch 'origin/release/2023-03-24' into feature/america
This commit is contained in:
@@ -3665,6 +3665,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>addpartsrule</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>addspeedprint</name>
|
<name>addspeedprint</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -4339,6 +4360,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>dms_control_override</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>dms_wip_acctnumber</name>
|
<name>dms_wip_acctnumber</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -4551,6 +4593,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>enforce_conversion_category</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>enforce_conversion_csr</name>
|
<name>enforce_conversion_csr</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -5390,6 +5453,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>md_lost_sale_reasons</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>md_parts_order_comment</name>
|
<name>md_parts_order_comment</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -5411,6 +5495,53 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>md_parts_scan</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>expression</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>flags</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>md_payment_types</name>
|
<name>md_payment_types</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -9588,6 +9719,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>partsscan</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>printlater</name>
|
<name>printlater</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -24174,6 +24326,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>lost_sale_reason</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>ma2s</name>
|
<name>ma2s</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40838,6 +41011,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>customer_list</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>cycle_time_analysis</name>
|
<name>cycle_time_analysis</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -42224,6 +42418,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>production_over_time</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>psr_by_make</name>
|
<name>psr_by_make</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -42822,6 +43037,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>ins_co_nm_filter</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>intake</name>
|
<name>intake</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ REACT_APP_CLOUDINARY_API_KEY=957865933348715
|
|||||||
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
|
||||||
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
|
||||||
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
|
||||||
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
|
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
|
||||||
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
|
||||||
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
|
||||||
REACT_APP_COUNTRY=USA
|
REACT_APP_COUNTRY=USA
|
||||||
@@ -4,69 +4,69 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"proxy": "http://localhost:4000",
|
"proxy": "http://localhost:4000",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.6.9",
|
"@apollo/client": "^3.7.9",
|
||||||
"@asseinfo/react-kanban": "^2.2.0",
|
"@asseinfo/react-kanban": "^2.2.0",
|
||||||
"@craco/craco": "^6.4.5",
|
"@craco/craco": "^7.0.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^3.3.3",
|
"@fingerprintjs/fingerprintjs": "^3.3.3",
|
||||||
"@jsreport/browser-client": "^3.1.0",
|
"@jsreport/browser-client": "^3.1.0",
|
||||||
"@sentry/react": "^7.28.1",
|
"@sentry/react": "^7.40.0",
|
||||||
"@sentry/tracing": "^7.28.1",
|
"@sentry/tracing": "^7.40.0",
|
||||||
"@splitsoftware/splitio-react": "^1.6.0",
|
"@splitsoftware/splitio-react": "^1.8.1",
|
||||||
"@tanem/react-nprogress": "^5.0.8",
|
"@tanem/react-nprogress": "^5.0.8",
|
||||||
"antd": "^4.22.3",
|
"antd": "^4.24.8",
|
||||||
"apollo-link-logger": "^2.0.0",
|
"apollo-link-logger": "^2.0.1",
|
||||||
"axios": "^0.27.2",
|
"axios": "^1.3.4",
|
||||||
"craco-less": "^1.20.0",
|
"craco-less": "^2.0.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"enquire-js": "^0.2.1",
|
"enquire-js": "^0.2.1",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
"firebase": "^9.9.1",
|
"firebase": "^9.17.1",
|
||||||
"graphql": "^16.5.0",
|
"graphql": "^16.6.0",
|
||||||
"i18next": "^21.8.14",
|
"i18next": "^22.4.10",
|
||||||
"i18next-browser-languagedetector": "^6.1.4",
|
"i18next-browser-languagedetector": "^7.0.1",
|
||||||
"jsoneditor": "^9.9.0",
|
"jsoneditor": "^9.9.0",
|
||||||
"jsreport-browser-client-dist": "^1.3.0",
|
"jsreport-browser-client-dist": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.10.9",
|
"libphonenumber-js": "^1.10.21",
|
||||||
"logrocket": "^3.0.1",
|
"logrocket": "^3.0.1",
|
||||||
"markerjs2": "^2.22.0",
|
"markerjs2": "^2.28.1",
|
||||||
"moment-business-days": "^1.2.0",
|
"moment-business-days": "^1.2.0",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.41",
|
||||||
"normalize-url": "^7.0.3",
|
"normalize-url": "^8.0.0",
|
||||||
"phone": "^3.1.23",
|
"phone": "^3.1.35",
|
||||||
"preval.macro": "^5.0.0",
|
"preval.macro": "^5.0.0",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.3",
|
||||||
"rc-queue-anim": "^2.0.0",
|
"rc-queue-anim": "^2.0.0",
|
||||||
"rc-scroll-anim": "^2.7.6",
|
"rc-scroll-anim": "^2.7.6",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-big-calendar": "^1.5.0",
|
"react-big-calendar": "^1.6.8",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-drag-listview": "^0.2.1",
|
"react-drag-listview": "^0.2.1",
|
||||||
"react-grid-gallery": "^0.5.5",
|
"react-grid-gallery": "^0.5.5",
|
||||||
"react-grid-layout": "^1.3.4",
|
"react-grid-layout": "^1.3.4",
|
||||||
"react-i18next": "^11.18.1",
|
"react-i18next": "^12.2.0",
|
||||||
"react-icons": "^4.4.0",
|
"react-icons": "^4.7.1",
|
||||||
"react-number-format": "^4.9.3",
|
"react-number-format": "^5.1.3",
|
||||||
"react-redux": "^7.2.8",
|
"react-redux": "^8.0.5",
|
||||||
"react-resizable": "^3.0.4",
|
"react-resizable": "^3.0.4",
|
||||||
"react-router-dom": "^5.3.0",
|
"react-router-dom": "^5.3.0",
|
||||||
"react-scripts": "^4.0.3",
|
"react-scripts": "^5.0.1",
|
||||||
"react-sticky": "^6.0.3",
|
"react-sticky": "^6.0.3",
|
||||||
"react-sublime-video": "^0.2.5",
|
"react-sublime-video": "^0.2.5",
|
||||||
"react-virtualized": "^9.22.3",
|
"react-virtualized": "^9.22.3",
|
||||||
"recharts": "^2.1.12",
|
"recharts": "^2.4.3",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.1",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-saga": "^1.1.3",
|
"redux-saga": "^1.2.2",
|
||||||
"redux-state-sync": "^3.1.4",
|
"redux-state-sync": "^3.1.4",
|
||||||
"reselect": "^4.1.6",
|
"reselect": "^4.1.7",
|
||||||
"sass": "^1.54.0",
|
"sass": "^1.58.3",
|
||||||
"socket.io-client": "^4.5.1",
|
"socket.io-client": "^4.6.1",
|
||||||
"styled-components": "^5.3.5",
|
"styled-components": "^5.3.6",
|
||||||
"subscriptions-transport-ws": "^0.11.0",
|
"subscriptions-transport-ws": "^0.11.0",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"workbox-background-sync": "^6.5.3",
|
"workbox-background-sync": "^6.5.3",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
|||||||
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
||||||
{
|
{
|
||||||
variables: { conversationId: selectedConversation },
|
variables: { conversationId: selectedConversation },
|
||||||
|
refetchQueries: ["UNREAD_CONVERSATION_COUNT"],
|
||||||
update(cache) {
|
update(cache) {
|
||||||
cache.modify({
|
cache.modify({
|
||||||
id: cache.identify({
|
id: cache.identify({
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
import {
|
||||||
|
CONVERSATION_LIST_QUERY,
|
||||||
|
UNREAD_CONVERSATION_COUNT,
|
||||||
|
} from "../../graphql/conversations.queries";
|
||||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||||
import {
|
import {
|
||||||
selectChatVisible,
|
selectChatVisible,
|
||||||
@@ -37,9 +40,17 @@ export function ChatPopupComponent({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [pollInterval, setpollInterval] = useState(0);
|
const [pollInterval, setpollInterval] = useState(0);
|
||||||
|
|
||||||
|
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
|
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
|
skip: !chatVisible,
|
||||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,12 +68,14 @@ export function ChatPopupComponent({
|
|||||||
if (called && chatVisible) refetch();
|
if (called && chatVisible) refetch();
|
||||||
}, [chatVisible, called, refetch]);
|
}, [chatVisible, called, refetch]);
|
||||||
|
|
||||||
const unreadCount = data
|
// const unreadCount = data
|
||||||
? data.conversations.reduce(
|
// ? data.conversations.reduce(
|
||||||
(acc, val) => val.messages_aggregate.aggregate.count + acc,
|
// (acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||||
0
|
// 0
|
||||||
)
|
// )
|
||||||
: 0;
|
// : 0;
|
||||||
|
|
||||||
|
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Badge count={unreadCount}>
|
<Badge count={unreadCount}>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import moment from "moment";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
//import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
||||||
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
||||||
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component";
|
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component";
|
||||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||||
@@ -165,7 +165,9 @@ export default function ContractFormComponent({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ContractLicenseDecodeButton form={form} />
|
{
|
||||||
|
//<ContractLicenseDecodeButton form={form} />
|
||||||
|
}
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<LayoutFormRow header={t("contracts.labels.driverinformation")}>
|
<LayoutFormRow header={t("contracts.labels.driverinformation")}>
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Divider,
|
Divider,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
Form,
|
||||||
Menu,
|
Menu,
|
||||||
notification,
|
notification,
|
||||||
Popover,
|
Popover,
|
||||||
|
Select,
|
||||||
Space,
|
Space,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import parsePhoneNumber from "libphonenumber-js";
|
import parsePhoneNumber from "libphonenumber-js";
|
||||||
@@ -59,7 +61,10 @@ export function ScheduleEventComponent({
|
|||||||
|
|
||||||
const blockContent = (
|
const blockContent = (
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
<Button
|
||||||
|
onClick={() => handleCancel({ id: event.id })}
|
||||||
|
disabled={event.arrived}
|
||||||
|
>
|
||||||
{t("appointments.actions.cancel")}
|
{t("appointments.actions.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -203,10 +208,46 @@ export function ScheduleEventComponent({
|
|||||||
<Button>{t("appointments.actions.sendreminder")}</Button>
|
<Button>{t("appointments.actions.sendreminder")}</Button>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
) : null}
|
) : null}
|
||||||
|
<Popover
|
||||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
trigger="click"
|
||||||
{t("appointments.actions.cancel")}
|
disabled={event.arrived}
|
||||||
</Button>
|
content={
|
||||||
|
<Form
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={({ lost_sale_reason }) => {
|
||||||
|
handleCancel({ id: event.id, lost_sale_reason });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="lost_sale_reason"
|
||||||
|
label={t("jobs.fields.lost_sale_reason")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||||
|
label: lsr,
|
||||||
|
value: lsr,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Button htmlType="submit">
|
||||||
|
{t("appointments.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
// onClick={() => handleCancel(event.id)}
|
||||||
|
disabled={event.arrived}
|
||||||
|
>
|
||||||
|
{t("appointments.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
{event.isintake ? (
|
{event.isintake ? (
|
||||||
<Button
|
<Button
|
||||||
disabled={event.arrived}
|
disabled={event.arrived}
|
||||||
@@ -249,7 +290,7 @@ export function ScheduleEventComponent({
|
|||||||
const RegularEvent = event.isintake ? (
|
const RegularEvent = event.isintake ? (
|
||||||
<Space
|
<Space
|
||||||
wrap
|
wrap
|
||||||
size='small'
|
size="small"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
event.color && event.color.hex ? event.color.hex : event.color,
|
event.color && event.color.hex ? event.color.hex : event.color,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
||||||
const [updateJob] = useMutation(UPDATE_JOB);
|
const [updateJob] = useMutation(UPDATE_JOB);
|
||||||
const handleCancel = async (id) => {
|
const handleCancel = async ({ id, lost_sale_reason }) => {
|
||||||
logImEXEvent("schedule_cancel_appt");
|
logImEXEvent("schedule_cancel_appt");
|
||||||
|
|
||||||
const cancelAppt = await cancelAppointment({
|
const cancelAppt = await cancelAppointment({
|
||||||
@@ -38,7 +38,8 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
|||||||
job: {
|
job: {
|
||||||
date_scheduled: null,
|
date_scheduled: null,
|
||||||
scheduled_in: null,
|
scheduled_in: null,
|
||||||
scheduled_completion:null,
|
scheduled_completion: null,
|
||||||
|
lost_sale_reason,
|
||||||
status: bodyshop.md_ro_statuses.default_imported,
|
status: bodyshop.md_ro_statuses.default_imported,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ export function JobLinesComponent({
|
|||||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
onCell: (record) => ({
|
onCell: (record) => ({
|
||||||
className: record.manual_line && "job-line-manual",
|
className: record.manual_line && "job-line-manual",
|
||||||
|
style: {
|
||||||
|
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||||
@@ -332,7 +335,7 @@ export function JobLinesComponent({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setJobLineEditContext({
|
setJobLineEditContext({
|
||||||
actions: { refetch: refetch, submit: form && form.submit },
|
actions: { refetch: refetch, submit: form && form.submit },
|
||||||
context: record,
|
context: { ...record, jobid: job.id },
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ import UndefinedToNull from "../../utils/undefinedtonull";
|
|||||||
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
jobLineEditModal: selectJobLineEditModal,
|
jobLineEditModal: selectJobLineEditModal,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
|
||||||
@@ -24,7 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
function JobLinesUpsertModalContainer({
|
function JobLinesUpsertModalContainer({
|
||||||
jobLineEditModal,
|
jobLineEditModal,
|
||||||
toggleModalVisible,
|
toggleModalVisible,
|
||||||
|
bodyshop,
|
||||||
}) {
|
}) {
|
||||||
|
const { CriticalPartsScanning } = useTreatments(
|
||||||
|
["CriticalPartsScanning"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
||||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||||
@@ -109,6 +119,9 @@ function JobLinesUpsertModalContainer({
|
|||||||
}
|
}
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
}
|
}
|
||||||
|
if (CriticalPartsScanning.treatment === "on") {
|
||||||
|
CriticalPartsScan(jobLineEditModal.context.jobid);
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import {
|
|||||||
useApolloClient,
|
useApolloClient,
|
||||||
useLazyQuery,
|
useLazyQuery,
|
||||||
useMutation,
|
useMutation,
|
||||||
useQuery,
|
useQuery
|
||||||
} from "@apollo/client";
|
} from "@apollo/client";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Col, notification, Row } from "antd";
|
import { Col, notification, Row } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import Dinero from "dinero.js";
|
import Dinero from "dinero.js";
|
||||||
@@ -19,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
|||||||
import {
|
import {
|
||||||
DELETE_AVAILABLE_JOB,
|
DELETE_AVAILABLE_JOB,
|
||||||
QUERY_AVAILABLE_JOBS,
|
QUERY_AVAILABLE_JOBS,
|
||||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
|
||||||
} from "../../graphql/available-jobs.queries";
|
} from "../../graphql/available-jobs.queries";
|
||||||
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||||
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
|
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
|
||||||
@@ -27,10 +28,11 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
|||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import {
|
import {
|
||||||
selectBodyshop,
|
selectBodyshop,
|
||||||
selectCurrentUser,
|
selectCurrentUser
|
||||||
} from "../../redux/user/user.selectors";
|
} from "../../redux/user/user.selectors";
|
||||||
import confirmDialog from "../../utils/asyncConfirm";
|
import confirmDialog from "../../utils/asyncConfirm";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||||
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
||||||
@@ -54,6 +56,11 @@ export function JobsAvailableContainer({
|
|||||||
currentUser,
|
currentUser,
|
||||||
insertAuditTrail,
|
insertAuditTrail,
|
||||||
}) {
|
}) {
|
||||||
|
const { CriticalPartsScanning } = useTreatments(
|
||||||
|
["CriticalPartsScanning"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
@@ -160,6 +167,9 @@ export function JobsAvailableContainer({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
|
if (CriticalPartsScanning.treatment === "on") {
|
||||||
|
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||||
|
}
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("jobs.successes.created"),
|
message: t("jobs.successes.created"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -247,7 +257,9 @@ export function JobsAvailableContainer({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
if (CriticalPartsScanning.treatment === "on") {
|
||||||
|
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
||||||
|
}
|
||||||
if (updateResult.errors) {
|
if (updateResult.errors) {
|
||||||
//error while inserting
|
//error while inserting
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export function JobsConvertButton({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const handleConvert = async ({ employee_csr, ...values }) => {
|
const handleConvert = async ({ employee_csr, category, ...values }) => {
|
||||||
if (parentFormIsFieldsTouched()) {
|
if (parentFormIsFieldsTouched()) {
|
||||||
alert(t("jobs.labels.savebeforeconversion"));
|
alert(t("jobs.labels.savebeforeconversion"));
|
||||||
return;
|
return;
|
||||||
@@ -55,6 +55,7 @@ export function JobsConvertButton({
|
|||||||
job: {
|
job: {
|
||||||
converted: true,
|
converted: true,
|
||||||
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
|
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
|
||||||
|
...(bodyshop.enforce_conversion_category ? { category } : {}),
|
||||||
...values,
|
...values,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -94,6 +95,7 @@ export function JobsConvertButton({
|
|||||||
driveable: true,
|
driveable: true,
|
||||||
towin: false,
|
towin: false,
|
||||||
employee_csr: job.employee_csr,
|
employee_csr: job.employee_csr,
|
||||||
|
category: job.category,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -197,6 +199,26 @@ export function JobsConvertButton({
|
|||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
{bodyshop.enforce_conversion_category && (
|
||||||
|
<Form.Item
|
||||||
|
name={"category"}
|
||||||
|
label={t("jobs.fields.category")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: bodyshop.enforce_conversion_category,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select allowClear>
|
||||||
|
{bodyshop.md_categories.map((s) => (
|
||||||
|
<Select.Option key={s} value={s}>
|
||||||
|
{s}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.ca_gst_registrant")}
|
label={t("jobs.fields.ca_gst_registrant")}
|
||||||
name="ca_gst_registrant"
|
name="ca_gst_registrant"
|
||||||
|
|||||||
@@ -9,7 +9,11 @@ const colSpan = {
|
|||||||
lg: { span: 12 },
|
lg: { span: 12 },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) {
|
export default function JobsCreateVehicleInfoComponent({
|
||||||
|
loading,
|
||||||
|
vehicles,
|
||||||
|
form,
|
||||||
|
}) {
|
||||||
const [state, setState] = useContext(JobCreateContext);
|
const [state, setState] = useContext(JobCreateContext);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
@@ -58,7 +62,7 @@ export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...colSpan}>
|
<Col {...colSpan}>
|
||||||
<JobsCreateVehicleInfoNewComponent />
|
<JobsCreateVehicleInfoNewComponent form={form}/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default function JobsCreateVehicleInfoContainer({ form }) {
|
|||||||
<JobsCreateVehicleInfoComponent
|
<JobsCreateVehicleInfoComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
vehicles={data ? data.search_vehicles : null}
|
vehicles={data ? data.search_vehicles : null}
|
||||||
|
form={form}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import { useTranslation } from "react-i18next";
|
|||||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
import JobsCreateVehicleInfoPredefined from "./jobs-create-vehicle-info.predefined.component";
|
||||||
|
|
||||||
export default function JobsCreateVehicleInfoNewComponent() {
|
export default function JobsCreateVehicleInfoNewComponent({ form }) {
|
||||||
const [state] = useContext(JobCreateContext);
|
const [state] = useContext(JobCreateContext);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -25,7 +26,7 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
|||||||
<Input disabled={!state.vehicle.new} />
|
<Input disabled={!state.vehicle.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow noDivider>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("vehicles.fields.v_color")}
|
label={t("vehicles.fields.v_color")}
|
||||||
name={["vehicle", "data", "v_color"]}
|
name={["vehicle", "data", "v_color"]}
|
||||||
@@ -52,8 +53,9 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow noDivider>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
span={10}
|
||||||
label={t("vehicles.fields.v_make_desc")}
|
label={t("vehicles.fields.v_make_desc")}
|
||||||
name={["vehicle", "data", "v_make_desc"]}
|
name={["vehicle", "data", "v_make_desc"]}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -66,6 +68,7 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
|||||||
<Input disabled={!state.vehicle.new} />
|
<Input disabled={!state.vehicle.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
|
span={11}
|
||||||
label={t("vehicles.fields.v_model_desc")}
|
label={t("vehicles.fields.v_model_desc")}
|
||||||
name={["vehicle", "data", "v_model_desc"]}
|
name={["vehicle", "data", "v_model_desc"]}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -77,6 +80,11 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
|||||||
>
|
>
|
||||||
<Input disabled={!state.vehicle.new} />
|
<Input disabled={!state.vehicle.new} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<JobsCreateVehicleInfoPredefined
|
||||||
|
disabled={!state.vehicle.new}
|
||||||
|
form={form}
|
||||||
|
span={1}
|
||||||
|
/>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
|
||||||
<LayoutFormRow header={t("vehicles.forms.registration")} grow>
|
<LayoutFormRow header={t("vehicles.forms.registration")} grow>
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Input, Popover, Table } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import PredefinedVehicles from "./predefined-vehicles.js";
|
||||||
|
|
||||||
|
export default function JobsCreateVehicleInfoPredefined({ disabled, form }) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleOpenChange = (newOpen) => {
|
||||||
|
setOpen(newOpen);
|
||||||
|
setSearch("");
|
||||||
|
};
|
||||||
|
const filteredPredefinedVehicles =
|
||||||
|
search === ""
|
||||||
|
? PredefinedVehicles
|
||||||
|
: PredefinedVehicles.filter(
|
||||||
|
(v) =>
|
||||||
|
v.make.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
|
v.model.toLowerCase().includes(search.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
const popContent = () => (
|
||||||
|
<div>
|
||||||
|
<Table
|
||||||
|
size="small"
|
||||||
|
title={() => <Input.Search onSearch={(value) => setSearch(value)} />}
|
||||||
|
dataSource={filteredPredefinedVehicles}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
dataIndex: "make",
|
||||||
|
key: "make",
|
||||||
|
title: t("vehicles.fields.v_make_desc"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: "model",
|
||||||
|
key: "model",
|
||||||
|
title: t("vehicles.fields.v_model_desc"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: "select",
|
||||||
|
key: "select",
|
||||||
|
title: t("general.labels.actions"),
|
||||||
|
render: (value, record) => (
|
||||||
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={() => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
vehicle: {
|
||||||
|
data: {
|
||||||
|
v_make_desc: record.make,
|
||||||
|
v_model_desc: record.model,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setOpen(false);
|
||||||
|
setSearch("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
content={popContent}
|
||||||
|
trigger="click"
|
||||||
|
open={open}
|
||||||
|
placement="left"
|
||||||
|
onOpenChange={handleOpenChange}
|
||||||
|
destroyTooltipOnHide
|
||||||
|
>
|
||||||
|
<SearchOutlined style={{ cursor: "pointer" }} />
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -289,6 +289,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
|||||||
>
|
>
|
||||||
<Input disabled={jobRO} />
|
<Input disabled={jobRO} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.lost_sale_reason")}
|
||||||
|
name="lost_sale_reason"
|
||||||
|
>
|
||||||
|
<Input disabled={jobRO} allowClear />
|
||||||
|
</Form.Item>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Dropdown,
|
||||||
|
Form,
|
||||||
|
Menu,
|
||||||
|
notification,
|
||||||
|
Popconfirm,
|
||||||
|
Popover,
|
||||||
|
Select,
|
||||||
|
} from "antd";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -130,35 +139,63 @@ export function JobsDetailHeaderActions({
|
|||||||
<Menu.Item
|
<Menu.Item
|
||||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||||
>
|
>
|
||||||
<Popconfirm
|
<Popover
|
||||||
title={t("general.labels.areyousure")}
|
trigger="click"
|
||||||
okText="Yes"
|
|
||||||
cancelText="No"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||||
onConfirm={async () => {
|
content={
|
||||||
const jobUpdate = await cancelAllAppointments({
|
<Form
|
||||||
variables: {
|
layout="vertical"
|
||||||
jobid: job.id,
|
onFinish={async ({ lost_sale_reason }) => {
|
||||||
job: {
|
const jobUpdate = await cancelAllAppointments({
|
||||||
date_scheduled: null,
|
variables: {
|
||||||
scheduled_in: null,
|
jobid: job.id,
|
||||||
scheduled_completion: null,
|
job: {
|
||||||
status: bodyshop.md_ro_statuses.default_imported,
|
date_scheduled: null,
|
||||||
},
|
scheduled_in: null,
|
||||||
},
|
scheduled_completion: null,
|
||||||
});
|
lost_sale_reason,
|
||||||
if (!jobUpdate.errors) {
|
status: bodyshop.md_ro_statuses.default_imported,
|
||||||
notification["success"]({
|
},
|
||||||
message: t("appointments.successes.canceled"),
|
},
|
||||||
});
|
});
|
||||||
return;
|
if (!jobUpdate.errors) {
|
||||||
}
|
notification["success"]({
|
||||||
}}
|
message: t("appointments.successes.canceled"),
|
||||||
getPopupContainer={(trigger) => trigger.parentNode}
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="lost_sale_reason"
|
||||||
|
label={t("jobs.fields.lost_sale_reason")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||||
|
label: lsr,
|
||||||
|
value: lsr,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Button
|
||||||
|
htmlType="submit"
|
||||||
|
disabled={
|
||||||
|
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("appointments.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{t("menus.jobsactions.cancelallappointments")}
|
{t("menus.jobsactions.cancelallappointments")}
|
||||||
</Popconfirm>
|
</Popover>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
disabled={
|
disabled={
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import cleanAxios from "../../utils/CleanAxios";
|
import cleanAxios from "../../utils/CleanAxios";
|
||||||
import formatBytes from "../../utils/formatbytes";
|
import formatBytes from "../../utils/formatbytes";
|
||||||
import yauzl from "yauzl";
|
//import yauzl from "yauzl";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -69,44 +69,44 @@ export function JobsDocumentsDownloadButton({
|
|||||||
setDownload(null);
|
setDownload(null);
|
||||||
if (Direct_Media_Download.treatment === "on") {
|
if (Direct_Media_Download.treatment === "on") {
|
||||||
try {
|
try {
|
||||||
const parentDir = await window.showDirectoryPicker({
|
// const parentDir = await window.showDirectoryPicker({
|
||||||
id: "media",
|
// id: "media",
|
||||||
startIn: "downloads",
|
// startIn: "downloads",
|
||||||
});
|
// });
|
||||||
|
|
||||||
const directory = await parentDir.getDirectoryHandle(identifier, {
|
// const directory = await parentDir.getDirectoryHandle(identifier, {
|
||||||
create: true,
|
// create: true,
|
||||||
});
|
// });
|
||||||
|
|
||||||
yauzl.fromBuffer(
|
// yauzl.fromBuffer(
|
||||||
Buffer.from(theDownloadedZip.data),
|
// Buffer.from(theDownloadedZip.data),
|
||||||
{},
|
// {},
|
||||||
(err, zipFile) => {
|
// (err, zipFile) => {
|
||||||
if (err) throw err;
|
// if (err) throw err;
|
||||||
zipFile.on("entry", (entry) => {
|
// zipFile.on("entry", (entry) => {
|
||||||
zipFile.openReadStream(entry, async (readErr, readStream) => {
|
// zipFile.openReadStream(entry, async (readErr, readStream) => {
|
||||||
if (readErr) {
|
// if (readErr) {
|
||||||
zipFile.close();
|
// zipFile.close();
|
||||||
throw readErr;
|
// throw readErr;
|
||||||
}
|
// }
|
||||||
if (err) throw err;
|
// if (err) throw err;
|
||||||
let fileSystemHandle = await directory.getFileHandle(
|
// let fileSystemHandle = await directory.getFileHandle(
|
||||||
entry.fileName,
|
// entry.fileName,
|
||||||
{
|
// {
|
||||||
create: true,
|
// create: true,
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
const writable = await fileSystemHandle.createWritable();
|
// const writable = await fileSystemHandle.createWritable();
|
||||||
readStream.on("data", async function (chunk) {
|
// readStream.on("data", async function (chunk) {
|
||||||
await writable.write(chunk);
|
// await writable.write(chunk);
|
||||||
});
|
// });
|
||||||
readStream.on("end", async function () {
|
// readStream.on("end", async function () {
|
||||||
await writable.close();
|
// await writable.close();
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
);
|
// );
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
standardMediaDownload(theDownloadedZip.data);
|
standardMediaDownload(theDownloadedZip.data);
|
||||||
|
|||||||
@@ -260,6 +260,19 @@ export function JobsList({ bodyshop }) {
|
|||||||
dataIndex: "ins_co_nm",
|
dataIndex: "ins_co_nm",
|
||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s,
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -272,6 +272,19 @@ export function JobsReadyList({ bodyshop }) {
|
|||||||
dataIndex: "ins_co_nm",
|
dataIndex: "ins_co_nm",
|
||||||
key: "ins_co_nm",
|
key: "ins_co_nm",
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
|
filters:
|
||||||
|
(jobs &&
|
||||||
|
jobs
|
||||||
|
.map((j) => j.ins_co_nm)
|
||||||
|
.filter(onlyUnique)
|
||||||
|
.map((s) => {
|
||||||
|
return {
|
||||||
|
text: s,
|
||||||
|
value: [s],
|
||||||
|
};
|
||||||
|
})) ||
|
||||||
|
[],
|
||||||
|
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||||
responsive: ["md"],
|
responsive: ["md"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,15 +1,40 @@
|
|||||||
import { Button, Form, notification, PageHeader } from "antd";
|
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UPDATE_OWNER } from "../../graphql/owners.queries";
|
import { DELETE_OWNER, UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||||
import OwnerDetailFormComponent from "./owner-detail-form.component";
|
import OwnerDetailFormComponent from "./owner-detail-form.component";
|
||||||
|
|
||||||
function OwnerDetailFormContainer({ owner, refetch }) {
|
function OwnerDetailFormContainer({ owner, refetch }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const history = useHistory();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [updateOwner] = useMutation(UPDATE_OWNER);
|
const [updateOwner] = useMutation(UPDATE_OWNER);
|
||||||
|
const [deleteOwner] = useMutation(DELETE_OWNER);
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await deleteOwner({
|
||||||
|
variables: { id: owner.id },
|
||||||
|
});
|
||||||
|
console.log(result);
|
||||||
|
if (result.errors) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("owners.errors.deleting", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
} else {
|
||||||
|
notification["success"]({
|
||||||
|
message: t("owners.successes.delete"),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
history.push(`/manage/owners`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -41,15 +66,29 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
|||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={t("menus.header.owners")}
|
title={t("menus.header.owners")}
|
||||||
extra={
|
extra={[
|
||||||
|
<Popconfirm
|
||||||
|
trigger="click"
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
disabled={owner.jobs.length !== 0}
|
||||||
|
title={t("owners.labels.deleteconfirm")}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="danger"
|
||||||
|
loading={loading}
|
||||||
|
disabled={owner.jobs.length !== 0}
|
||||||
|
>
|
||||||
|
{t("general.actions.delete")}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>,
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onClick={() => form.submit()}
|
onClick={() => form.submit()}
|
||||||
>
|
>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>,
|
||||||
}
|
]}
|
||||||
/>
|
/>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
|
|||||||
@@ -124,7 +124,11 @@ function PaymentModalContainer({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) form.resetFields();
|
if (visible) {
|
||||||
|
form.resetFields();
|
||||||
|
form.resetFields();
|
||||||
|
form.setFieldsValue(context);
|
||||||
|
}
|
||||||
}, [visible, form, context]);
|
}, [visible, form, context]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -139,6 +143,7 @@ function PaymentModalContainer({
|
|||||||
: t("payments.labels.edit")
|
: t("payments.labels.edit")
|
||||||
}
|
}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
destroyOnClose
|
||||||
okText={t("general.actions.save")}
|
okText={t("general.actions.save")}
|
||||||
onOk={() => form.submit()}
|
onOk={() => form.submit()}
|
||||||
width="50%"
|
width="50%"
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export function ProductionListTable({
|
|||||||
const assoc = bodyshop.associations.find(
|
const assoc = bodyshop.associations.find(
|
||||||
(a) => a.useremail === currentUser.email
|
(a) => a.useremail === currentUser.email
|
||||||
);
|
);
|
||||||
|
|
||||||
if (assoc) {
|
if (assoc) {
|
||||||
await updateDefaultProdView({
|
await updateDefaultProdView({
|
||||||
variables: { assocId: assoc.id, view: value },
|
variables: { assocId: assoc.id, view: value },
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default function ProfileShopsComponent({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
console.log("🚀 ~ file: profile-shops.component.jsx:45 ~ data", data);
|
|
||||||
const filteredData =
|
const filteredData =
|
||||||
search === ""
|
search === ""
|
||||||
? data
|
? data
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import React from "react";
|
|||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import {
|
import {
|
||||||
QUERY_ALL_ASSOCIATIONS,
|
QUERY_ALL_ASSOCIATIONS,
|
||||||
UPDATE_ASSOCIATION,
|
UPDATE_ACTIVE_ASSOCIATION,
|
||||||
} from "../../graphql/associations.queries";
|
} from "../../graphql/associations.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import ProfileShopsComponent from "./profile-shops.component";
|
import ProfileShopsComponent from "./profile-shops.component";
|
||||||
@@ -13,9 +13,13 @@ import { getToken } from "firebase/messaging";
|
|||||||
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
@@ -25,14 +29,18 @@ export default connect(
|
|||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
)(ProfileShopsContainer);
|
)(ProfileShopsContainer);
|
||||||
|
|
||||||
export function ProfileShopsContainer({ bodyshop }) {
|
export function ProfileShopsContainer({ bodyshop, currentUser }) {
|
||||||
const { loading, error, data } = useQuery(QUERY_ALL_ASSOCIATIONS, {
|
const { loading, error, data } = useQuery(QUERY_ALL_ASSOCIATIONS, {
|
||||||
fetchPolicy: "network-only",
|
fetchPolicy: "network-only",
|
||||||
nextFetchPolicy: "network-only",
|
nextFetchPolicy: "network-only",
|
||||||
|
variables: {
|
||||||
|
email: currentUser.email,
|
||||||
|
},
|
||||||
|
skip: !currentUser,
|
||||||
});
|
});
|
||||||
const [updateAssocation] = useMutation(UPDATE_ASSOCIATION);
|
const [updateActiveAssociation] = useMutation(UPDATE_ACTIVE_ASSOCIATION);
|
||||||
|
|
||||||
const updateActiveShop = async (activeShopId) => {
|
const updateActiveShop = async (newActiveAssocId) => {
|
||||||
logImEXEvent("profile_change_active_shop");
|
logImEXEvent("profile_change_active_shop");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -46,16 +54,12 @@ export function ProfileShopsContainer({ bodyshop }) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("No FCM token. Skipping unsubscribe.");
|
console.log("No FCM token. Skipping unsubscribe.");
|
||||||
}
|
}
|
||||||
await Promise.all(
|
|
||||||
data.associations.map(async (record) => {
|
await updateActiveAssociation({
|
||||||
await updateAssocation({
|
variables: {
|
||||||
variables: {
|
newActiveAssocId: newActiveAssocId,
|
||||||
assocId: record.id,
|
},
|
||||||
assocActive: record.id === activeShopId ? true : false,
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
//Force window refresh.
|
//Force window refresh.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Checkbox,
|
||||||
|
Col,
|
||||||
|
PageHeader,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
import { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import useLocalStorage from "../../utils/useLocalStorage";
|
import useLocalStorage from "../../utils/useLocalStorage";
|
||||||
@@ -9,22 +18,39 @@ import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
|
|||||||
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
||||||
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
|
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
|
||||||
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.component";
|
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.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
|
||||||
|
)(ScheduleCalendarComponent);
|
||||||
|
|
||||||
export default function ScheduleCalendarComponent({ data, refetch }) {
|
export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||||
const [filter, setFilter] = useLocalStorage("filter_events", {
|
const [filter, setFilter] = useLocalStorage("filter_events", {
|
||||||
intake: true,
|
intake: true,
|
||||||
manual: true,
|
manual: true,
|
||||||
employeevacation: true,
|
employeevacation: true,
|
||||||
|
ins_co_nm: null,
|
||||||
});
|
});
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
return data.filter(
|
return data.filter(
|
||||||
(d) =>
|
(d) =>
|
||||||
d.block ||
|
(d.block ||
|
||||||
(filter.intake && d.isintake) ||
|
(filter.intake && d.isintake) ||
|
||||||
(filter.manual && !d.isintake && d.block === false) ||
|
(filter.manual && !d.isintake && d.block === false) ||
|
||||||
(d.__typename === "employee_vacation" &&
|
(d.__typename === "employee_vacation" &&
|
||||||
filter.employeevacation &&
|
filter.employeevacation &&
|
||||||
!!d.employee)
|
!!d.employee)) &&
|
||||||
|
(filter.ins_co_nm && filter.ins_co_nm.length > 0
|
||||||
|
? filter.ins_co_nm.includes(d.job.ins_co_nm)
|
||||||
|
: true)
|
||||||
);
|
);
|
||||||
}, [data, filter]);
|
}, [data, filter]);
|
||||||
|
|
||||||
@@ -37,6 +63,21 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
|
|||||||
extra={
|
extra={
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<ScheduleAtsSummary appointments={filteredData} />
|
<ScheduleAtsSummary appointments={filteredData} />
|
||||||
|
<Select
|
||||||
|
style={{ minWidth: "15rem" }}
|
||||||
|
mode="multiple"
|
||||||
|
placeholder={t("schedule.labels.ins_co_nm_filter")}
|
||||||
|
allowClear
|
||||||
|
onClear={() => setFilter({ ...filter, ins_co_nm: [] })}
|
||||||
|
value={filter?.ins_co_nm}
|
||||||
|
onChange={(e) => {
|
||||||
|
setFilter({ ...filter, ins_co_nm: e });
|
||||||
|
}}
|
||||||
|
options={bodyshop.md_ins_cos.map((i) => ({
|
||||||
|
label: i.name,
|
||||||
|
value: i.name,
|
||||||
|
}))}
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={filter?.intake}
|
checked={filter?.intake}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ export function ScheduleJobModalContainer({
|
|||||||
date_scheduled: new Date(),
|
date_scheduled: new Date(),
|
||||||
scheduled_in: values.start,
|
scheduled_in: values.start,
|
||||||
scheduled_completion: values.scheduled_completion,
|
scheduled_completion: values.scheduled_completion,
|
||||||
|
lost_sale_reason: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Tabs } from "antd";
|
import { Button, Card, Tabs } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -8,6 +9,7 @@ import ShopInfoGeneral from "./shop-info.general.component";
|
|||||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||||
|
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||||
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||||
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
@@ -23,6 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||||
|
|
||||||
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||||
|
const { CriticalPartsScanning } = useTreatments(
|
||||||
|
["CriticalPartsScanning"],
|
||||||
|
{},
|
||||||
|
bodyshop.imexshopid
|
||||||
|
);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -71,6 +78,11 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
||||||
<ShopInfoLaborRates form={form} />
|
<ShopInfoLaborRates form={form} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
{CriticalPartsScanning.treatment === "on" && (
|
||||||
|
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
|
||||||
|
<ShopInfoPartsScan form={form} />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -473,6 +473,13 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["enforce_conversion_category"]}
|
||||||
|
label={t("bodyshop.fields.enforce_conversion_category")}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={["target_touchtime"]}
|
name={["target_touchtime"]}
|
||||||
label={t("bodyshop.fields.target_touchtime")}
|
label={t("bodyshop.fields.target_touchtime")}
|
||||||
|
|||||||
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
import { Button, Form, Input, Space } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
export default function ShopInfoPartsScan({ form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
||||||
|
<Form.List name={["md_parts_scan"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_parts_scan.expression")}
|
||||||
|
key={`${index}expression`}
|
||||||
|
name={[field.name, "expression"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.md_parts_scan.flags")}
|
||||||
|
key={`${index}flags`}
|
||||||
|
name={[field.name, "flags"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Space wrap>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("bodyshop.actions.addpartsrule")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -217,7 +217,9 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
{t("jobs.fields.ponumber")}
|
{t("jobs.fields.ponumber")}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
<Select.Option value="account_number">
|
<Select.Option value="account_number">
|
||||||
{t("jobs.fields.dms.control_type.account_number")}
|
{t(
|
||||||
|
"jobs.fields.dms.control_type.account_number"
|
||||||
|
)}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@@ -423,6 +425,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
<Input onBlur={handleBlur} />
|
<Input onBlur={handleBlur} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
{bodyshop.cdk_dealerid && (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.dms.dms_control_override")}
|
||||||
|
key={`${index}dms_control_override`}
|
||||||
|
name={[field.name, "dms_control_override"]}
|
||||||
|
>
|
||||||
|
<Input onBlur={handleBlur} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -546,6 +557,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
|||||||
<Input onBlur={handleBlur} />
|
<Input onBlur={handleBlur} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
)}
|
||||||
|
{bodyshop.cdk_dealerid && (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.dms.dms_control_override")}
|
||||||
|
key={`${index}dms_control_override`}
|
||||||
|
name={[field.name, "dms_control_override"]}
|
||||||
|
>
|
||||||
|
<Input onBlur={handleBlur} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
|
|||||||
@@ -76,6 +76,19 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
|||||||
>
|
>
|
||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_lost_sale_reasons"]}
|
||||||
|
label={t("bodyshop.fields.md_lost_sale_reasons")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
// required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
||||||
<Space wrap size="large">
|
<Space wrap size="large">
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ export default function ShopUsersAuthEdit({ association }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!visible && (
|
{!visible && (
|
||||||
<div style={{ cursor: "pointer" }} onClick={() => setVisible(true)}>
|
<div
|
||||||
|
style={{ cursor: "pointer" }} //onClick={() => setVisible(true)}
|
||||||
|
>
|
||||||
{association.authlevel || t("general.labels.na")}
|
{association.authlevel || t("general.labels.na")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -82,9 +82,10 @@ export function TimeTicketModalComponent({
|
|||||||
label={t("timetickets.fields.ro_number")}
|
label={t("timetickets.fields.ro_number")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required:
|
required: !(
|
||||||
!form.getFieldValue("cost_center") ===
|
form.getFieldValue("cost_center") ===
|
||||||
"timetickets.labels.shift",
|
"timetickets.labels.shift"
|
||||||
|
),
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,16 +1,41 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Button, Form, notification, PageHeader } from "antd";
|
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
|
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
import { DELETE_VEHICLE, UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
|
||||||
function VehicleDetailFormContainer({ vehicle, refetch }) {
|
function VehicleDetailFormContainer({ vehicle, refetch }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
|
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
|
||||||
|
const [deleteVehicle] = useMutation(DELETE_VEHICLE);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const result = await deleteVehicle({
|
||||||
|
variables: { id: vehicle.id },
|
||||||
|
});
|
||||||
|
console.log(result);
|
||||||
|
if (result.errors) {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("vehicles.errors.deleting", {
|
||||||
|
error: JSON.stringify(result.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
} else {
|
||||||
|
notification["success"]({
|
||||||
|
message: t("vehicles.successes.delete"),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
history.push(`/manage/vehicles`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -40,15 +65,29 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
|
|||||||
<>
|
<>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={t("menus.header.vehicles")}
|
title={t("menus.header.vehicles")}
|
||||||
extra={
|
extra={[
|
||||||
|
<Popconfirm
|
||||||
|
trigger="click"
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
disabled={vehicle.jobs.length !== 0}
|
||||||
|
title={t("vehicles.labels.deleteconfirm")}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="danger"
|
||||||
|
loading={loading}
|
||||||
|
disabled={vehicle.jobs.length !== 0}
|
||||||
|
>
|
||||||
|
{t("general.actions.delete")}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>,
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onClick={() => form.submit()}
|
onClick={() => form.submit()}
|
||||||
>
|
>
|
||||||
{t("general.actions.save")}
|
{t("general.actions.save")}
|
||||||
</Button>
|
</Button>,
|
||||||
}
|
]}
|
||||||
/>
|
/>
|
||||||
<Form
|
<Form
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
|
|||||||
scheduled_in
|
scheduled_in
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
status
|
status
|
||||||
|
lost_sale_reason
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
export const QUERY_ALL_ASSOCIATIONS = gql`
|
export const QUERY_ALL_ASSOCIATIONS = gql`
|
||||||
query QUERY_ALL_ASSOCIATIONS {
|
query QUERY_ALL_ASSOCIATIONS($email: String) {
|
||||||
associations(order_by: { bodyshop: { shopname: asc } }) {
|
associations(
|
||||||
|
where: { useremail: { _eq: $email } }
|
||||||
|
order_by: { bodyshop: { shopname: asc } }
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
active
|
active
|
||||||
bodyshop {
|
bodyshop {
|
||||||
@@ -27,6 +30,30 @@ export const UPDATE_ASSOCIATION = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export const UPDATE_ACTIVE_ASSOCIATION = gql`
|
||||||
|
mutation UPDATE_ACTIVE_ASSOCIATION($newActiveAssocId: uuid) {
|
||||||
|
nweActive: update_associations(
|
||||||
|
where: { id: { _eq: $newActiveAssocId } }
|
||||||
|
_set: { active: true }
|
||||||
|
) {
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inactive: update_associations(
|
||||||
|
where: { id: { _neq: $newActiveAssocId } }
|
||||||
|
_set: { active: false }
|
||||||
|
) {
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const UPDATE_ACTIVE_PROD_LIST_VIEW = gql`
|
export const UPDATE_ACTIVE_PROD_LIST_VIEW = gql`
|
||||||
mutation UPDATE_ACTIVE_PROD_LIST_VIEW($assocId: uuid, $view: String) {
|
mutation UPDATE_ACTIVE_PROD_LIST_VIEW($assocId: uuid, $view: String) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
query QUERY_BODYSHOP {
|
query QUERY_BODYSHOP {
|
||||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||||
associations {
|
associations {
|
||||||
|
id
|
||||||
authlevel
|
authlevel
|
||||||
useremail
|
useremail
|
||||||
default_prod_list_view
|
default_prod_list_view
|
||||||
@@ -112,6 +113,9 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
localmediaservernetwork
|
localmediaservernetwork
|
||||||
localmediatoken
|
localmediatoken
|
||||||
enforce_conversion_csr
|
enforce_conversion_csr
|
||||||
|
md_lost_sale_reasons
|
||||||
|
md_parts_scan
|
||||||
|
enforce_conversion_category
|
||||||
employees {
|
employees {
|
||||||
user_email
|
user_email
|
||||||
id
|
id
|
||||||
@@ -222,6 +226,9 @@ export const UPDATE_SHOP = gql`
|
|||||||
localmediaservernetwork
|
localmediaservernetwork
|
||||||
localmediatoken
|
localmediatoken
|
||||||
enforce_conversion_csr
|
enforce_conversion_csr
|
||||||
|
md_lost_sale_reasons
|
||||||
|
md_parts_scan
|
||||||
|
enforce_conversion_category
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -31,6 +31,18 @@ import { gql } from "@apollo/client";
|
|||||||
// }
|
// }
|
||||||
// `;
|
// `;
|
||||||
|
|
||||||
|
export const UNREAD_CONVERSATION_COUNT = gql`
|
||||||
|
query UNREAD_CONVERSATION_COUNT {
|
||||||
|
messages_aggregate(
|
||||||
|
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const CONVERSATION_LIST_QUERY = gql`
|
export const CONVERSATION_LIST_QUERY = gql`
|
||||||
query CONVERSATION_LIST_QUERY {
|
query CONVERSATION_LIST_QUERY {
|
||||||
conversations(
|
conversations(
|
||||||
|
|||||||
@@ -536,6 +536,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
driveable
|
driveable
|
||||||
towin
|
towin
|
||||||
loss_of_use
|
loss_of_use
|
||||||
|
lost_sale_reason
|
||||||
vehicle {
|
vehicle {
|
||||||
id
|
id
|
||||||
plate_no
|
plate_no
|
||||||
@@ -722,6 +723,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
convertedtolbr
|
convertedtolbr
|
||||||
ah_detail_line
|
ah_detail_line
|
||||||
act_price_before_ppc
|
act_price_before_ppc
|
||||||
|
critical
|
||||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||||
id
|
id
|
||||||
quantity
|
quantity
|
||||||
@@ -2061,6 +2063,7 @@ export const QUERY_JOB_EXPORT_DMS = gql`
|
|||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
|
ins_co_nm
|
||||||
kmin
|
kmin
|
||||||
kmout
|
kmout
|
||||||
v_make_desc
|
v_make_desc
|
||||||
|
|||||||
@@ -94,6 +94,14 @@ export const UPDATE_OWNER = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DELETE_OWNER = gql`
|
||||||
|
mutation DELETE_OWNER($id: uuid!) {
|
||||||
|
delete_owners_by_pk(id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_ALL_OWNERS = gql`
|
export const QUERY_ALL_OWNERS = gql`
|
||||||
query QUERY_ALL_OWNERS {
|
query QUERY_ALL_OWNERS {
|
||||||
owners {
|
owners {
|
||||||
|
|||||||
@@ -55,6 +55,14 @@ export const UPDATE_VEHICLE = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const DELETE_VEHICLE = gql`
|
||||||
|
mutation DELETE_VEHICLE($id: uuid!) {
|
||||||
|
delete_vehicles_by_pk(id: $id) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_ALL_VEHICLES = gql`
|
export const QUERY_ALL_VEHICLES = gql`
|
||||||
query QUERY_ALL_VEHICLES {
|
query QUERY_ALL_VEHICLES {
|
||||||
vehicles {
|
vehicles {
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
socket.on("connect_error", (err) => {
|
||||||
|
console.log(`connect_error due to ${err}`, err);
|
||||||
|
notification.error({ message: err.message });
|
||||||
|
});
|
||||||
socket.on("log-event", (payload) => {
|
socket.on("log-event", (payload) => {
|
||||||
setLogs((logs) => {
|
setLogs((logs) => {
|
||||||
return [...logs, payload];
|
return [...logs, payload];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default function JobsCreateComponent({ form }) {
|
|||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.vehicleinfo"),
|
title: t("jobs.labels.create.vehicleinfo"),
|
||||||
content: <JobsCreateVehicleInfoContainer />,
|
content: <JobsCreateVehicleInfoContainer form={form} />,
|
||||||
validation:
|
validation:
|
||||||
!!state.vehicle.new ||
|
!!state.vehicle.new ||
|
||||||
!!state.vehicle.selectedid ||
|
!!state.vehicle.selectedid ||
|
||||||
|
|||||||
@@ -230,6 +230,7 @@
|
|||||||
"addapptcolor": "Add Appointment Color",
|
"addapptcolor": "Add Appointment Color",
|
||||||
"addbucket": "Add Definition",
|
"addbucket": "Add Definition",
|
||||||
"addpartslocation": "Add Parts Location",
|
"addpartslocation": "Add Parts Location",
|
||||||
|
"addpartsrule": "Add Parts Scan Rule",
|
||||||
"addspeedprint": "Add Speed Print",
|
"addspeedprint": "Add Speed Print",
|
||||||
"addtemplate": "Add Template",
|
"addtemplate": "Add Template",
|
||||||
"newlaborrate": "New Labor Rate",
|
"newlaborrate": "New Labor Rate",
|
||||||
@@ -270,6 +271,7 @@
|
|||||||
"disablebillwip": "Disable bill WIP for A/P Posting",
|
"disablebillwip": "Disable bill WIP for A/P Posting",
|
||||||
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
|
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
|
||||||
"dms_acctnumber": "DMS Account #",
|
"dms_acctnumber": "DMS Account #",
|
||||||
|
"dms_control_override": "Static Control # Override",
|
||||||
"dms_wip_acctnumber": "DMS W.I.P. Account #",
|
"dms_wip_acctnumber": "DMS W.I.P. Account #",
|
||||||
"generic_customer_number": "Generic Customer Number",
|
"generic_customer_number": "Generic Customer Number",
|
||||||
"itc_federal": "Federal Tax is ITC?",
|
"itc_federal": "Federal Tax is ITC?",
|
||||||
@@ -281,6 +283,7 @@
|
|||||||
},
|
},
|
||||||
"email": "General Shop Email",
|
"email": "General Shop Email",
|
||||||
"enforce_class": "Enforce Class on Conversion?",
|
"enforce_class": "Enforce Class on Conversion?",
|
||||||
|
"enforce_conversion_category": "Enforce Category on Conversion?",
|
||||||
"enforce_conversion_csr": "Enforce CSR on Conversion?",
|
"enforce_conversion_csr": "Enforce CSR on Conversion?",
|
||||||
"enforce_referral": "Enforce Referrals",
|
"enforce_referral": "Enforce Referrals",
|
||||||
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
||||||
@@ -328,7 +331,12 @@
|
|||||||
"zip": "Zip/Postal Code"
|
"zip": "Zip/Postal Code"
|
||||||
},
|
},
|
||||||
"md_jobline_presets": "Jobline Presets",
|
"md_jobline_presets": "Jobline Presets",
|
||||||
|
"md_lost_sale_reasons": "Lost Sale Reasons",
|
||||||
"md_parts_order_comment": "Parts Orders Comments",
|
"md_parts_order_comment": "Parts Orders Comments",
|
||||||
|
"md_parts_scan": {
|
||||||
|
"expression": "RegEX Expression",
|
||||||
|
"flags": "Flags"
|
||||||
|
},
|
||||||
"md_payment_types": "Payment Types",
|
"md_payment_types": "Payment Types",
|
||||||
"md_referral_sources": "Referral Sources",
|
"md_referral_sources": "Referral Sources",
|
||||||
"messaginglabel": "Messaging Preset Label",
|
"messaginglabel": "Messaging Preset Label",
|
||||||
@@ -579,6 +587,7 @@
|
|||||||
"notespresets": "Notes Presets",
|
"notespresets": "Notes Presets",
|
||||||
"orderstatuses": "Order Statuses",
|
"orderstatuses": "Order Statuses",
|
||||||
"partslocations": "Parts Locations",
|
"partslocations": "Parts Locations",
|
||||||
|
"partsscan": "Critical Parts Scanning",
|
||||||
"printlater": "Print Later",
|
"printlater": "Print Later",
|
||||||
"qbo": "Use QuickBooks Online?",
|
"qbo": "Use QuickBooks Online?",
|
||||||
"qbo_departmentid": "QBO Department ID",
|
"qbo_departmentid": "QBO Department ID",
|
||||||
@@ -1448,6 +1457,7 @@
|
|||||||
"loss_date": "Loss Date",
|
"loss_date": "Loss Date",
|
||||||
"loss_desc": "Loss Description",
|
"loss_desc": "Loss Description",
|
||||||
"loss_of_use": "Loss of Use",
|
"loss_of_use": "Loss of Use",
|
||||||
|
"lost_sale_reason": "Lost Sale Reason",
|
||||||
"ma2s": "2 Stage Paint",
|
"ma2s": "2 Stage Paint",
|
||||||
"ma3s": "3 Stage Pain",
|
"ma3s": "3 Stage Pain",
|
||||||
"mabl": "MABL?",
|
"mabl": "MABL?",
|
||||||
@@ -1985,6 +1995,7 @@
|
|||||||
"update": "Update Selected Records"
|
"update": "Update Selected Records"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "Error deleting owner. {{error}}.",
|
||||||
"noaccess": "The record does not exist or you do not have access to it. ",
|
"noaccess": "The record does not exist or you do not have access to it. ",
|
||||||
"saving": "Error saving owner. {{error}}.",
|
"saving": "Error saving owner. {{error}}.",
|
||||||
"selectexistingornew": "Select an existing owner record or create a new one. "
|
"selectexistingornew": "Select an existing owner record or create a new one. "
|
||||||
@@ -2017,6 +2028,7 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"create_new": "Create a new owner record.",
|
"create_new": "Create a new owner record.",
|
||||||
|
"deleteconfirm": "Are you sure you want to delete this owner? This cannot be undone.",
|
||||||
"existing_owners": "Existing Owners",
|
"existing_owners": "Existing Owners",
|
||||||
"fromclaim": "Current Claim",
|
"fromclaim": "Current Claim",
|
||||||
"fromowner": "Historical Owner Record",
|
"fromowner": "Historical Owner Record",
|
||||||
@@ -2024,6 +2036,7 @@
|
|||||||
"updateowner": "Update Owner"
|
"updateowner": "Update Owner"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "Owner deleted successfully.",
|
||||||
"save": "Owner saved successfully."
|
"save": "Owner saved successfully."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2423,6 +2436,7 @@
|
|||||||
"credits_not_received_date": "Credits not Received by Date",
|
"credits_not_received_date": "Credits not Received by Date",
|
||||||
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
|
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
|
||||||
"csi": "CSI Responses",
|
"csi": "CSI Responses",
|
||||||
|
"customer_list": "Customer List",
|
||||||
"cycle_time_analysis": "Cycle Time Analysis",
|
"cycle_time_analysis": "Cycle Time Analysis",
|
||||||
"estimates_written_converted": "Estimates Written/Converted",
|
"estimates_written_converted": "Estimates Written/Converted",
|
||||||
"estimator_detail": "Jobs by Estimator (Detail)",
|
"estimator_detail": "Jobs by Estimator (Detail)",
|
||||||
@@ -2489,6 +2503,7 @@
|
|||||||
"production_by_target_date": "Production by Target Date",
|
"production_by_target_date": "Production by Target Date",
|
||||||
"production_by_technician": "Production by Technician",
|
"production_by_technician": "Production by Technician",
|
||||||
"production_by_technician_one": "Production filtered by Technician",
|
"production_by_technician_one": "Production filtered by Technician",
|
||||||
|
"production_over_time": "Production Level over Time",
|
||||||
"psr_by_make": "Percent of Sales by Vehicle Make",
|
"psr_by_make": "Percent of Sales by Vehicle Make",
|
||||||
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
||||||
"purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)",
|
"purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)",
|
||||||
@@ -2521,6 +2536,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"atssummary": "ATS Summary",
|
"atssummary": "ATS Summary",
|
||||||
"employeevacation": "Employee Vacations",
|
"employeevacation": "Employee Vacations",
|
||||||
|
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||||
"intake": "Intake Events",
|
"intake": "Intake Events",
|
||||||
"manual": "Manual Events",
|
"manual": "Manual Events",
|
||||||
"manualevent": "Add Manual Event"
|
"manualevent": "Add Manual Event"
|
||||||
@@ -2781,6 +2797,7 @@
|
|||||||
},
|
},
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "Error deleting vehicle. {{error}}.",
|
||||||
"noaccess": "The vehicle does not exist or you do not have access to it.",
|
"noaccess": "The vehicle does not exist or you do not have access to it.",
|
||||||
"selectexistingornew": "Select an existing vehicle record or create a new one. ",
|
"selectexistingornew": "Select an existing vehicle record or create a new one. ",
|
||||||
"validation": "Please ensure all fields are entered correctly.",
|
"validation": "Please ensure all fields are entered correctly.",
|
||||||
@@ -2816,12 +2833,14 @@
|
|||||||
"registration": "Registration"
|
"registration": "Registration"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"deleteconfirm": "Are you sure you want to delete this vehicle? This cannot be undone.",
|
||||||
"fromvehicle": "Historical Vehicle Record",
|
"fromvehicle": "Historical Vehicle Record",
|
||||||
"novehinfo": "No Vehicle Information",
|
"novehinfo": "No Vehicle Information",
|
||||||
"relatedjobs": "Related Jobs",
|
"relatedjobs": "Related Jobs",
|
||||||
"updatevehicle": "Update Vehicle Information"
|
"updatevehicle": "Update Vehicle Information"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "Vehicle deleted successfully.",
|
||||||
"save": "Vehicle saved successfully."
|
"save": "Vehicle saved successfully."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -230,6 +230,7 @@
|
|||||||
"addapptcolor": "",
|
"addapptcolor": "",
|
||||||
"addbucket": "",
|
"addbucket": "",
|
||||||
"addpartslocation": "",
|
"addpartslocation": "",
|
||||||
|
"addpartsrule": "",
|
||||||
"addspeedprint": "",
|
"addspeedprint": "",
|
||||||
"addtemplate": "",
|
"addtemplate": "",
|
||||||
"newlaborrate": "",
|
"newlaborrate": "",
|
||||||
@@ -270,6 +271,7 @@
|
|||||||
"disablebillwip": "",
|
"disablebillwip": "",
|
||||||
"disablecontactvehiclecreation": "",
|
"disablecontactvehiclecreation": "",
|
||||||
"dms_acctnumber": "",
|
"dms_acctnumber": "",
|
||||||
|
"dms_control_override": "",
|
||||||
"dms_wip_acctnumber": "",
|
"dms_wip_acctnumber": "",
|
||||||
"generic_customer_number": "",
|
"generic_customer_number": "",
|
||||||
"itc_federal": "",
|
"itc_federal": "",
|
||||||
@@ -281,6 +283,7 @@
|
|||||||
},
|
},
|
||||||
"email": "",
|
"email": "",
|
||||||
"enforce_class": "",
|
"enforce_class": "",
|
||||||
|
"enforce_conversion_category": "",
|
||||||
"enforce_conversion_csr": "",
|
"enforce_conversion_csr": "",
|
||||||
"enforce_referral": "",
|
"enforce_referral": "",
|
||||||
"federal_tax_id": "",
|
"federal_tax_id": "",
|
||||||
@@ -328,7 +331,12 @@
|
|||||||
"zip": ""
|
"zip": ""
|
||||||
},
|
},
|
||||||
"md_jobline_presets": "",
|
"md_jobline_presets": "",
|
||||||
|
"md_lost_sale_reasons": "",
|
||||||
"md_parts_order_comment": "",
|
"md_parts_order_comment": "",
|
||||||
|
"md_parts_scan": {
|
||||||
|
"expression": "",
|
||||||
|
"flags": ""
|
||||||
|
},
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
@@ -579,6 +587,7 @@
|
|||||||
"notespresets": "",
|
"notespresets": "",
|
||||||
"orderstatuses": "",
|
"orderstatuses": "",
|
||||||
"partslocations": "",
|
"partslocations": "",
|
||||||
|
"partsscan": "",
|
||||||
"printlater": "",
|
"printlater": "",
|
||||||
"qbo": "",
|
"qbo": "",
|
||||||
"qbo_departmentid": "",
|
"qbo_departmentid": "",
|
||||||
@@ -1448,6 +1457,7 @@
|
|||||||
"loss_date": "Fecha de pérdida",
|
"loss_date": "Fecha de pérdida",
|
||||||
"loss_desc": "",
|
"loss_desc": "",
|
||||||
"loss_of_use": "",
|
"loss_of_use": "",
|
||||||
|
"lost_sale_reason": "",
|
||||||
"ma2s": "",
|
"ma2s": "",
|
||||||
"ma3s": "",
|
"ma3s": "",
|
||||||
"mabl": "",
|
"mabl": "",
|
||||||
@@ -1985,6 +1995,7 @@
|
|||||||
"update": ""
|
"update": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "",
|
||||||
"noaccess": "El registro no existe o no tiene acceso a él.",
|
"noaccess": "El registro no existe o no tiene acceso a él.",
|
||||||
"saving": "",
|
"saving": "",
|
||||||
"selectexistingornew": ""
|
"selectexistingornew": ""
|
||||||
@@ -2017,6 +2028,7 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"create_new": "Crea un nuevo registro de propietario.",
|
"create_new": "Crea un nuevo registro de propietario.",
|
||||||
|
"deleteconfirm": "",
|
||||||
"existing_owners": "Propietarios existentes",
|
"existing_owners": "Propietarios existentes",
|
||||||
"fromclaim": "",
|
"fromclaim": "",
|
||||||
"fromowner": "",
|
"fromowner": "",
|
||||||
@@ -2024,6 +2036,7 @@
|
|||||||
"updateowner": ""
|
"updateowner": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "",
|
||||||
"save": "Propietario guardado con éxito."
|
"save": "Propietario guardado con éxito."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2423,6 +2436,7 @@
|
|||||||
"credits_not_received_date": "",
|
"credits_not_received_date": "",
|
||||||
"credits_not_received_date_vendorid": "",
|
"credits_not_received_date_vendorid": "",
|
||||||
"csi": "",
|
"csi": "",
|
||||||
|
"customer_list": "",
|
||||||
"cycle_time_analysis": "",
|
"cycle_time_analysis": "",
|
||||||
"estimates_written_converted": "",
|
"estimates_written_converted": "",
|
||||||
"estimator_detail": "",
|
"estimator_detail": "",
|
||||||
@@ -2489,6 +2503,7 @@
|
|||||||
"production_by_target_date": "",
|
"production_by_target_date": "",
|
||||||
"production_by_technician": "",
|
"production_by_technician": "",
|
||||||
"production_by_technician_one": "",
|
"production_by_technician_one": "",
|
||||||
|
"production_over_time": "",
|
||||||
"psr_by_make": "",
|
"psr_by_make": "",
|
||||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||||
@@ -2521,6 +2536,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"atssummary": "",
|
"atssummary": "",
|
||||||
"employeevacation": "",
|
"employeevacation": "",
|
||||||
|
"ins_co_nm_filter": "",
|
||||||
"intake": "",
|
"intake": "",
|
||||||
"manual": "",
|
"manual": "",
|
||||||
"manualevent": ""
|
"manualevent": ""
|
||||||
@@ -2781,6 +2797,7 @@
|
|||||||
},
|
},
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "",
|
||||||
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
|
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
|
||||||
"selectexistingornew": "",
|
"selectexistingornew": "",
|
||||||
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
|
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
|
||||||
@@ -2816,12 +2833,14 @@
|
|||||||
"registration": ""
|
"registration": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"deleteconfirm": "",
|
||||||
"fromvehicle": "",
|
"fromvehicle": "",
|
||||||
"novehinfo": "",
|
"novehinfo": "",
|
||||||
"relatedjobs": "",
|
"relatedjobs": "",
|
||||||
"updatevehicle": ""
|
"updatevehicle": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "",
|
||||||
"save": "Vehículo guardado con éxito."
|
"save": "Vehículo guardado con éxito."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -230,6 +230,7 @@
|
|||||||
"addapptcolor": "",
|
"addapptcolor": "",
|
||||||
"addbucket": "",
|
"addbucket": "",
|
||||||
"addpartslocation": "",
|
"addpartslocation": "",
|
||||||
|
"addpartsrule": "",
|
||||||
"addspeedprint": "",
|
"addspeedprint": "",
|
||||||
"addtemplate": "",
|
"addtemplate": "",
|
||||||
"newlaborrate": "",
|
"newlaborrate": "",
|
||||||
@@ -270,6 +271,7 @@
|
|||||||
"disablebillwip": "",
|
"disablebillwip": "",
|
||||||
"disablecontactvehiclecreation": "",
|
"disablecontactvehiclecreation": "",
|
||||||
"dms_acctnumber": "",
|
"dms_acctnumber": "",
|
||||||
|
"dms_control_override": "",
|
||||||
"dms_wip_acctnumber": "",
|
"dms_wip_acctnumber": "",
|
||||||
"generic_customer_number": "",
|
"generic_customer_number": "",
|
||||||
"itc_federal": "",
|
"itc_federal": "",
|
||||||
@@ -281,6 +283,7 @@
|
|||||||
},
|
},
|
||||||
"email": "",
|
"email": "",
|
||||||
"enforce_class": "",
|
"enforce_class": "",
|
||||||
|
"enforce_conversion_category": "",
|
||||||
"enforce_conversion_csr": "",
|
"enforce_conversion_csr": "",
|
||||||
"enforce_referral": "",
|
"enforce_referral": "",
|
||||||
"federal_tax_id": "",
|
"federal_tax_id": "",
|
||||||
@@ -328,7 +331,12 @@
|
|||||||
"zip": ""
|
"zip": ""
|
||||||
},
|
},
|
||||||
"md_jobline_presets": "",
|
"md_jobline_presets": "",
|
||||||
|
"md_lost_sale_reasons": "",
|
||||||
"md_parts_order_comment": "",
|
"md_parts_order_comment": "",
|
||||||
|
"md_parts_scan": {
|
||||||
|
"expression": "",
|
||||||
|
"flags": ""
|
||||||
|
},
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
@@ -579,6 +587,7 @@
|
|||||||
"notespresets": "",
|
"notespresets": "",
|
||||||
"orderstatuses": "",
|
"orderstatuses": "",
|
||||||
"partslocations": "",
|
"partslocations": "",
|
||||||
|
"partsscan": "",
|
||||||
"printlater": "",
|
"printlater": "",
|
||||||
"qbo": "",
|
"qbo": "",
|
||||||
"qbo_departmentid": "",
|
"qbo_departmentid": "",
|
||||||
@@ -1448,6 +1457,7 @@
|
|||||||
"loss_date": "Date de perte",
|
"loss_date": "Date de perte",
|
||||||
"loss_desc": "",
|
"loss_desc": "",
|
||||||
"loss_of_use": "",
|
"loss_of_use": "",
|
||||||
|
"lost_sale_reason": "",
|
||||||
"ma2s": "",
|
"ma2s": "",
|
||||||
"ma3s": "",
|
"ma3s": "",
|
||||||
"mabl": "",
|
"mabl": "",
|
||||||
@@ -1985,6 +1995,7 @@
|
|||||||
"update": ""
|
"update": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "",
|
||||||
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
||||||
"saving": "",
|
"saving": "",
|
||||||
"selectexistingornew": ""
|
"selectexistingornew": ""
|
||||||
@@ -2017,6 +2028,7 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"create_new": "Créez un nouvel enregistrement de propriétaire.",
|
"create_new": "Créez un nouvel enregistrement de propriétaire.",
|
||||||
|
"deleteconfirm": "",
|
||||||
"existing_owners": "Propriétaires existants",
|
"existing_owners": "Propriétaires existants",
|
||||||
"fromclaim": "",
|
"fromclaim": "",
|
||||||
"fromowner": "",
|
"fromowner": "",
|
||||||
@@ -2024,6 +2036,7 @@
|
|||||||
"updateowner": ""
|
"updateowner": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "",
|
||||||
"save": "Le propriétaire a bien enregistré."
|
"save": "Le propriétaire a bien enregistré."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2423,6 +2436,7 @@
|
|||||||
"credits_not_received_date": "",
|
"credits_not_received_date": "",
|
||||||
"credits_not_received_date_vendorid": "",
|
"credits_not_received_date_vendorid": "",
|
||||||
"csi": "",
|
"csi": "",
|
||||||
|
"customer_list": "",
|
||||||
"cycle_time_analysis": "",
|
"cycle_time_analysis": "",
|
||||||
"estimates_written_converted": "",
|
"estimates_written_converted": "",
|
||||||
"estimator_detail": "",
|
"estimator_detail": "",
|
||||||
@@ -2489,6 +2503,7 @@
|
|||||||
"production_by_target_date": "",
|
"production_by_target_date": "",
|
||||||
"production_by_technician": "",
|
"production_by_technician": "",
|
||||||
"production_by_technician_one": "",
|
"production_by_technician_one": "",
|
||||||
|
"production_over_time": "",
|
||||||
"psr_by_make": "",
|
"psr_by_make": "",
|
||||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||||
@@ -2521,6 +2536,7 @@
|
|||||||
"labels": {
|
"labels": {
|
||||||
"atssummary": "",
|
"atssummary": "",
|
||||||
"employeevacation": "",
|
"employeevacation": "",
|
||||||
|
"ins_co_nm_filter": "",
|
||||||
"intake": "",
|
"intake": "",
|
||||||
"manual": "",
|
"manual": "",
|
||||||
"manualevent": ""
|
"manualevent": ""
|
||||||
@@ -2781,6 +2797,7 @@
|
|||||||
},
|
},
|
||||||
"vehicles": {
|
"vehicles": {
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"deleting": "",
|
||||||
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
|
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
|
||||||
"selectexistingornew": "",
|
"selectexistingornew": "",
|
||||||
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
|
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
|
||||||
@@ -2816,12 +2833,14 @@
|
|||||||
"registration": ""
|
"registration": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"deleteconfirm": "",
|
||||||
"fromvehicle": "",
|
"fromvehicle": "",
|
||||||
"novehinfo": "",
|
"novehinfo": "",
|
||||||
"relatedjobs": "",
|
"relatedjobs": "",
|
||||||
"updatevehicle": ""
|
"updatevehicle": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
|
"delete": "",
|
||||||
"save": "Le véhicule a été enregistré avec succès."
|
"save": "Le véhicule a été enregistré avec succès."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import NumberFormat from "react-number-format";
|
import { NumericFormat } from "react-number-format";
|
||||||
|
|
||||||
export default function CurrencyFormatter(props) {
|
export default function CurrencyFormatter(props) {
|
||||||
return (
|
return (
|
||||||
<NumberFormat
|
<NumericFormat
|
||||||
thousandSeparator={true}
|
thousandSeparator={true}
|
||||||
decimalScale={2}
|
decimalScale={2}
|
||||||
fixedDecimalScale={true}
|
fixedDecimalScale={true}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ middlewares.push(
|
|||||||
|
|
||||||
const cache = new InMemoryCache({});
|
const cache = new InMemoryCache({});
|
||||||
|
|
||||||
export default new ApolloClient({
|
const client = new ApolloClient({
|
||||||
link: ApolloLink.from(middlewares),
|
link: ApolloLink.from(middlewares),
|
||||||
cache,
|
cache,
|
||||||
connectToDevTools: process.env.NODE_ENV !== "production",
|
connectToDevTools: process.env.NODE_ENV !== "production",
|
||||||
@@ -161,3 +161,4 @@ export default new ApolloClient({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
export default client;
|
||||||
|
|||||||
@@ -1841,6 +1841,34 @@ export const TemplateList = (type, context) => {
|
|||||||
},
|
},
|
||||||
group: "purchases",
|
group: "purchases",
|
||||||
},
|
},
|
||||||
|
production_over_time: {
|
||||||
|
title: i18n.t("reportcenter.templates.production_over_time"),
|
||||||
|
subject: i18n.t(
|
||||||
|
"reportcenter.templates.production_over_time"
|
||||||
|
),
|
||||||
|
key: "production_over_time",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.actual_in"),
|
||||||
|
},
|
||||||
|
group: "jobs",
|
||||||
|
},
|
||||||
|
customer_list: {
|
||||||
|
title: i18n.t("reportcenter.templates.customer_list"),
|
||||||
|
subject: i18n.t(
|
||||||
|
"reportcenter.templates.customer_list"
|
||||||
|
),
|
||||||
|
key: "customer_list",
|
||||||
|
//idtype: "vendor",
|
||||||
|
disabled: false,
|
||||||
|
rangeFilter: {
|
||||||
|
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||||
|
field: i18n.t("jobs.fields.date_invoiced"),
|
||||||
|
},
|
||||||
|
group: "customers",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "courtesycarcontract"
|
...(!type || type === "courtesycarcontract"
|
||||||
|
|||||||
12
client/src/utils/criticalPartsScan.js
Normal file
12
client/src/utils/criticalPartsScan.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import { notification } from "antd";
|
||||||
|
|
||||||
|
async function CriticalPartsScan(jobid) {
|
||||||
|
try {
|
||||||
|
await axios.post("/job/partsscan", { jobid });
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({ type: "error", message: JSON.stringify(error) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CriticalPartsScan;
|
||||||
11164
client/yarn.lock
11164
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -223,11 +223,9 @@
|
|||||||
- kanban_settings
|
- kanban_settings
|
||||||
- qbo_realmId
|
- qbo_realmId
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
user:
|
||||||
associations:
|
authid:
|
||||||
user:
|
_eq: X-Hasura-User-Id
|
||||||
authid:
|
|
||||||
_eq: X-Hasura-User-Id
|
|
||||||
check: null
|
check: null
|
||||||
- table:
|
- table:
|
||||||
name: audit_trail
|
name: audit_trail
|
||||||
@@ -798,6 +796,13 @@
|
|||||||
table:
|
table:
|
||||||
name: owners
|
name: owners
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: bodyshopid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
- name: phonebooks
|
- name: phonebooks
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -855,6 +860,7 @@
|
|||||||
- deliverchecklist
|
- deliverchecklist
|
||||||
- email
|
- email
|
||||||
- enforce_class
|
- enforce_class
|
||||||
|
- enforce_conversion_category
|
||||||
- enforce_conversion_csr
|
- enforce_conversion_csr
|
||||||
- enforce_referral
|
- enforce_referral
|
||||||
- entegral_configuration
|
- entegral_configuration
|
||||||
@@ -885,11 +891,13 @@
|
|||||||
- md_ins_cos
|
- md_ins_cos
|
||||||
- md_jobline_presets
|
- md_jobline_presets
|
||||||
- md_labor_rates
|
- md_labor_rates
|
||||||
|
- md_lost_sale_reasons
|
||||||
- md_messaging_presets
|
- md_messaging_presets
|
||||||
- md_notes_presets
|
- md_notes_presets
|
||||||
- md_order_statuses
|
- md_order_statuses
|
||||||
- md_parts_locations
|
- md_parts_locations
|
||||||
- md_parts_order_comment
|
- md_parts_order_comment
|
||||||
|
- md_parts_scan
|
||||||
- md_payment_types
|
- md_payment_types
|
||||||
- md_rbac
|
- md_rbac
|
||||||
- md_referral_sources
|
- md_referral_sources
|
||||||
@@ -952,6 +960,7 @@
|
|||||||
- deliverchecklist
|
- deliverchecklist
|
||||||
- email
|
- email
|
||||||
- enforce_class
|
- enforce_class
|
||||||
|
- enforce_conversion_category
|
||||||
- enforce_conversion_csr
|
- enforce_conversion_csr
|
||||||
- enforce_referral
|
- enforce_referral
|
||||||
- federal_tax_id
|
- federal_tax_id
|
||||||
@@ -977,11 +986,13 @@
|
|||||||
- md_ins_cos
|
- md_ins_cos
|
||||||
- md_jobline_presets
|
- md_jobline_presets
|
||||||
- md_labor_rates
|
- md_labor_rates
|
||||||
|
- md_lost_sale_reasons
|
||||||
- md_messaging_presets
|
- md_messaging_presets
|
||||||
- md_notes_presets
|
- md_notes_presets
|
||||||
- md_order_statuses
|
- md_order_statuses
|
||||||
- md_parts_locations
|
- md_parts_locations
|
||||||
- md_parts_order_comment
|
- md_parts_order_comment
|
||||||
|
- md_parts_scan
|
||||||
- md_payment_types
|
- md_payment_types
|
||||||
- md_rbac
|
- md_rbac
|
||||||
- md_referral_sources
|
- md_referral_sources
|
||||||
@@ -2482,6 +2493,7 @@
|
|||||||
_eq: true
|
_eq: true
|
||||||
columns:
|
columns:
|
||||||
- act_price
|
- act_price
|
||||||
|
- act_price_before_ppc
|
||||||
- ah_detail_line
|
- ah_detail_line
|
||||||
- alt_co_id
|
- alt_co_id
|
||||||
- alt_overrd
|
- alt_overrd
|
||||||
@@ -2548,6 +2560,7 @@
|
|||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
- act_price
|
- act_price
|
||||||
|
- act_price_before_ppc
|
||||||
- ah_detail_line
|
- ah_detail_line
|
||||||
- alt_co_id
|
- alt_co_id
|
||||||
- alt_overrd
|
- alt_overrd
|
||||||
@@ -2562,6 +2575,7 @@
|
|||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
- convertedtolbr_data
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
|
- critical
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
- db_ref
|
- db_ref
|
||||||
@@ -2625,6 +2639,7 @@
|
|||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
- act_price
|
- act_price
|
||||||
|
- act_price_before_ppc
|
||||||
- ah_detail_line
|
- ah_detail_line
|
||||||
- alt_co_id
|
- alt_co_id
|
||||||
- alt_overrd
|
- alt_overrd
|
||||||
@@ -2639,6 +2654,7 @@
|
|||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
- convertedtolbr_data
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
|
- critical
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
- db_ref
|
- db_ref
|
||||||
@@ -2903,6 +2919,13 @@
|
|||||||
table:
|
table:
|
||||||
name: parts_orders
|
name: parts_orders
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: jobid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
- name: payments
|
- name: payments
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -3382,6 +3405,7 @@
|
|||||||
- loss_desc
|
- loss_desc
|
||||||
- loss_of_use
|
- loss_of_use
|
||||||
- loss_type
|
- loss_type
|
||||||
|
- lost_sale_reason
|
||||||
- materials
|
- materials
|
||||||
- other_amount_payable
|
- other_amount_payable
|
||||||
- owner_owing
|
- owner_owing
|
||||||
@@ -3656,6 +3680,7 @@
|
|||||||
- loss_desc
|
- loss_desc
|
||||||
- loss_of_use
|
- loss_of_use
|
||||||
- loss_type
|
- loss_type
|
||||||
|
- lost_sale_reason
|
||||||
- materials
|
- materials
|
||||||
- other_amount_payable
|
- other_amount_payable
|
||||||
- owner_owing
|
- owner_owing
|
||||||
@@ -4502,6 +4527,63 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
|
- table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
|
object_relationships:
|
||||||
|
- name: bodyshop
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: bodyshopid
|
||||||
|
- name: job
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: jobid
|
||||||
|
- name: payment
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: paymentid
|
||||||
|
insert_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- amount
|
||||||
|
- bodyshopid
|
||||||
|
- declinereason
|
||||||
|
- ext_paymentid
|
||||||
|
- jobid
|
||||||
|
- paymentid
|
||||||
|
- response
|
||||||
|
- successful
|
||||||
|
select_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- successful
|
||||||
|
- response
|
||||||
|
- amount
|
||||||
|
- declinereason
|
||||||
|
- ext_paymentid
|
||||||
|
- bodyshopid
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
- paymentid
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
allow_aggregations: true
|
||||||
- table:
|
- table:
|
||||||
name: payments
|
name: payments
|
||||||
schema: public
|
schema: public
|
||||||
@@ -4517,6 +4599,13 @@
|
|||||||
table:
|
table:
|
||||||
name: exportlog
|
name: exportlog
|
||||||
schema: public
|
schema: public
|
||||||
|
- name: payment_responses
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: paymentid
|
||||||
|
table:
|
||||||
|
name: payment_response
|
||||||
|
schema: public
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "public"."payment_response";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE TABLE "public"."payment_response" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "bodyshopid" uuid NOT NULL, "jobid" uuid, "paymentid" uuid, "successful" boolean NOT NULL DEFAULT false, "ext_paymentid" text NOT NULL, "amount" numeric NOT NULL, "declinereason" text, "response" jsonb NOT NULL DEFAULT jsonb_build_object(), PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("paymentid") REFERENCES "public"."payments"("id") ON UPDATE cascade ON DELETE cascade);
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
@@ -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 "md_lost_sale_reasons" jsonb
|
||||||
|
-- not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "md_lost_sale_reasons" jsonb
|
||||||
|
not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."jobs" add column "lost_sale_reason" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."jobs" add column "lost_sale_reason" text
|
||||||
|
null;
|
||||||
@@ -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 "md_parts_scan" jsonb
|
||||||
|
-- not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "md_parts_scan" jsonb
|
||||||
|
not null default jsonb_build_array();
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."joblines" add column "critical" boolean
|
||||||
|
-- not null default 'false';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."joblines" add column "critical" boolean
|
||||||
|
not null default 'false';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "enforce_conversion_category" boolean
|
||||||
|
-- not null default 'false';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "enforce_conversion_category" boolean
|
||||||
|
not null default 'false';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default jsonb_build_array();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default '["Scheduling Delay", "Backordered Parts", "Price", "Unknown"]';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."joblines" add column "act_price_before_ppc" numeric
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."joblines" add column "act_price_before_ppc" numeric
|
||||||
|
null;
|
||||||
5634
package-lock.json
generated
5634
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
@@ -17,41 +17,41 @@
|
|||||||
"start": "node server.js"
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aws-sdk": "^2.1181.0",
|
"aws-sdk": "^2.1326.0",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"bluebird": "^3.7.2",
|
"bluebird": "^3.7.2",
|
||||||
"body-parser": "^1.20.0",
|
"body-parser": "^1.20.2",
|
||||||
"cloudinary": "^1.30.1",
|
"cloudinary": "^1.34.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "2.8.5",
|
"cors": "2.8.5",
|
||||||
"csrf": "^3.1.0",
|
"csrf": "^3.1.0",
|
||||||
"dinero.js": "^1.9.1",
|
"dinero.js": "^1.9.1",
|
||||||
"dotenv": "16.0.1",
|
"dotenv": "16.0.3",
|
||||||
"express": "^4.18.1",
|
"express": "^4.18.2",
|
||||||
"firebase-admin": "^11.0.0",
|
"firebase-admin": "^11.5.0",
|
||||||
"graphql": "^16.5.0",
|
"graphql": "^16.6.0",
|
||||||
"graphql-request": "^4.2.0",
|
"graphql-request": "^4.2.0",
|
||||||
"graylog2": "^0.2.1",
|
"graylog2": "^0.2.1",
|
||||||
"inline-css": "^4.0.1",
|
"inline-css": "^4.0.2",
|
||||||
"intuit-oauth": "^4.0.0",
|
"intuit-oauth": "^4.0.0",
|
||||||
"json-2-csv": "^3.17.1",
|
"json-2-csv": "^3.19.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.41",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"node-mailjet": "^5.1.0",
|
"node-mailjet": "^6.0.2",
|
||||||
"node-persist": "^3.1.0",
|
"node-persist": "^3.1.3",
|
||||||
"node-quickbooks": "^2.0.39",
|
"node-quickbooks": "^2.0.41",
|
||||||
"nodemailer": "^6.7.7",
|
"nodemailer": "^6.9.1",
|
||||||
"phone": "^3.1.23",
|
"phone": "^3.1.35",
|
||||||
"query-string": "^7.1.1",
|
"query-string": "^7.1.1",
|
||||||
"soap": "^0.45.0",
|
"soap": "^1.0.0",
|
||||||
"socket.io": "^4.5.0",
|
"socket.io": "^4.6.1",
|
||||||
"ssh2-sftp-client": "^9.0.2",
|
"ssh2-sftp-client": "^9.0.4",
|
||||||
"stripe": "^9.15.0",
|
"stripe": "^9.15.0",
|
||||||
"twilio": "^3.80.0",
|
"twilio": "^4.8.0",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^9.0.0",
|
||||||
"xml2js": "^0.4.23",
|
"xml2js": "^0.4.23",
|
||||||
"xmlbuilder2": "^3.0.2"
|
"xmlbuilder2": "^3.0.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ app.post("/job/costingmulti", fb.validateFirebaseIdToken, job.costingmulti);
|
|||||||
var ppc = require("./server/ccc/partspricechange");
|
var ppc = require("./server/ccc/partspricechange");
|
||||||
app.post("/job/ppc", fb.validateFirebaseIdToken, ppc.generatePpc);
|
app.post("/job/ppc", fb.validateFirebaseIdToken, ppc.generatePpc);
|
||||||
|
|
||||||
|
var partsScan = require("./server/parts-scan/parts-scan");
|
||||||
|
app.post("/job/partsscan", fb.validateFirebaseIdToken, partsScan.partsScan);
|
||||||
//Scheduling
|
//Scheduling
|
||||||
var scheduling = require("./server/scheduling/scheduling-job");
|
var scheduling = require("./server/scheduling/scheduling-job");
|
||||||
app.post("/scheduling/job", fb.validateFirebaseIdToken, scheduling.job);
|
app.post("/scheduling/job", fb.validateFirebaseIdToken, scheduling.job);
|
||||||
|
|||||||
@@ -1092,7 +1092,13 @@ async function GenerateTransWips(socket) {
|
|||||||
if (alloc.sale.getAmount() > 0 && !alloc.tax) {
|
if (alloc.sale.getAmount() > 0 && !alloc.tax) {
|
||||||
const item = {
|
const item = {
|
||||||
acct: alloc.profitCenter.dms_acctnumber,
|
acct: alloc.profitCenter.dms_acctnumber,
|
||||||
cntl: socket.JobData.ro_number,
|
cntl:
|
||||||
|
alloc.profitCenter.dms_control_override &&
|
||||||
|
alloc.profitCenter.dms_control_override !== null &&
|
||||||
|
alloc.profitCenter.dms_control_override !== undefined &&
|
||||||
|
alloc.profitCenter.dms_control_override?.trim() !== ""
|
||||||
|
? alloc.profitCenter.dms_control_override
|
||||||
|
: socket.JobData.ro_number,
|
||||||
cntl2: null,
|
cntl2: null,
|
||||||
credtMemoNo: null,
|
credtMemoNo: null,
|
||||||
postAmt: alloc.sale.multiply(-1).getAmount(),
|
postAmt: alloc.sale.multiply(-1).getAmount(),
|
||||||
@@ -1109,7 +1115,13 @@ async function GenerateTransWips(socket) {
|
|||||||
if (alloc.cost.getAmount() > 0 && !alloc.tax) {
|
if (alloc.cost.getAmount() > 0 && !alloc.tax) {
|
||||||
const item = {
|
const item = {
|
||||||
acct: alloc.costCenter.dms_acctnumber,
|
acct: alloc.costCenter.dms_acctnumber,
|
||||||
cntl: socket.JobData.ro_number,
|
cntl:
|
||||||
|
alloc.costCenter.dms_control_override &&
|
||||||
|
alloc.costCenter.dms_control_override !== null &&
|
||||||
|
alloc.costCenter.dms_control_override !== undefined &&
|
||||||
|
alloc.costCenter.dms_control_override?.trim() !== ""
|
||||||
|
? alloc.costCenter.dms_control_override
|
||||||
|
: socket.JobData.ro_number,
|
||||||
cntl2: null,
|
cntl2: null,
|
||||||
credtMemoNo: null,
|
credtMemoNo: null,
|
||||||
postAmt: alloc.cost.getAmount(),
|
postAmt: alloc.cost.getAmount(),
|
||||||
@@ -1123,7 +1135,13 @@ async function GenerateTransWips(socket) {
|
|||||||
|
|
||||||
const itemWip = {
|
const itemWip = {
|
||||||
acct: alloc.costCenter.dms_wip_acctnumber,
|
acct: alloc.costCenter.dms_wip_acctnumber,
|
||||||
cntl: socket.JobData.ro_number,
|
cntl:
|
||||||
|
alloc.costCenter.dms_control_override &&
|
||||||
|
alloc.costCenter.dms_control_override !== null &&
|
||||||
|
alloc.costCenter.dms_control_override !== undefined &&
|
||||||
|
alloc.costCenter.dms_control_override?.trim() !== ""
|
||||||
|
? alloc.costCenter.dms_control_override
|
||||||
|
: socket.JobData.ro_number,
|
||||||
cntl2: null,
|
cntl2: null,
|
||||||
credtMemoNo: null,
|
credtMemoNo: null,
|
||||||
postAmt: alloc.cost.multiply(-1).getAmount(),
|
postAmt: alloc.cost.multiply(-1).getAmount(),
|
||||||
@@ -1158,7 +1176,13 @@ async function GenerateTransWips(socket) {
|
|||||||
if (alloc.sale.getAmount() > 0) {
|
if (alloc.sale.getAmount() > 0) {
|
||||||
const item2 = {
|
const item2 = {
|
||||||
acct: alloc.profitCenter.dms_acctnumber,
|
acct: alloc.profitCenter.dms_acctnumber,
|
||||||
cntl: socket.JobData.ro_number,
|
cntl:
|
||||||
|
alloc.profitCenter.dms_control_override &&
|
||||||
|
alloc.profitCenter.dms_control_override !== null &&
|
||||||
|
alloc.profitCenter.dms_control_override !== undefined &&
|
||||||
|
alloc.profitCenter.dms_control_override?.trim() !== ""
|
||||||
|
? alloc.profitCenter.dms_control_override
|
||||||
|
: socket.JobData.ro_number,
|
||||||
cntl2: null,
|
cntl2: null,
|
||||||
credtMemoNo: null,
|
credtMemoNo: null,
|
||||||
postAmt: alloc.sale.multiply(-1).getAmount(),
|
postAmt: alloc.sale.multiply(-1).getAmount(),
|
||||||
|
|||||||
@@ -42,14 +42,16 @@ function pollFunc(fn, timeout, interval) {
|
|||||||
pollFunc(getEntegralShopData, 0, 5 * 60 * 1000); //Set the metadata to refresh every 5 minutes.
|
pollFunc(getEntegralShopData, 0, 5 * 60 * 1000); //Set the metadata to refresh every 5 minutes.
|
||||||
|
|
||||||
async function getEntegralShopData() {
|
async function getEntegralShopData() {
|
||||||
await storage.init({ logging: true });
|
// await storage.init({ logging: true });
|
||||||
const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
|
// const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
|
||||||
logger.log("set-entegral-shops-local-storage", "DEBUG", "API", null, null);
|
// logger.log("set-entegral-shops-local-storage", "DEBUG", "API", null, null);
|
||||||
await storage.setItem("entegralShops", bodyshops);
|
// await storage.setItem("entegralShops", bodyshops);
|
||||||
return true; //Continue execution.
|
// return true; //Continue execution.
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
return;
|
||||||
//Query for the List of Bodyshop Clients.
|
//Query for the List of Bodyshop Clients.
|
||||||
const job = req.body.event.data.new;
|
const job = req.body.event.data.new;
|
||||||
logger.log("arms-job-update", "DEBUG", "api", job.id, null);
|
logger.log("arms-job-update", "DEBUG", "api", job.id, null);
|
||||||
|
|||||||
@@ -814,7 +814,11 @@ const CreateCosts = (job) => {
|
|||||||
].add(
|
].add(
|
||||||
Dinero({
|
Dinero({
|
||||||
amount: Math.round((ticket_val.rate || 0) * 100),
|
amount: Math.round((ticket_val.rate || 0) * 100),
|
||||||
}).multiply(ticket_val.actualhrs || ticket_val.productivehrs || 0)
|
}).multiply(
|
||||||
|
(ticket_val.flat_rate
|
||||||
|
? ticket_val.productivehrs
|
||||||
|
: ticket_val.actualhrs) || 0
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return ticket_acc;
|
return ticket_acc;
|
||||||
|
|||||||
@@ -775,6 +775,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
|
|||||||
cost_center
|
cost_center
|
||||||
actualhrs
|
actualhrs
|
||||||
productivehrs
|
productivehrs
|
||||||
|
flat_rate
|
||||||
}
|
}
|
||||||
area_of_damage
|
area_of_damage
|
||||||
employee_prep_rel {
|
employee_prep_rel {
|
||||||
@@ -905,7 +906,7 @@ exports.UPDATE_JOB = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
|
exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs_by_pk(id: $id) {
|
||||||
updated_at
|
updated_at
|
||||||
alt_transport
|
alt_transport
|
||||||
@@ -1733,4 +1734,28 @@ exports.GET_JOB_FOR_PPC = `query GET_JOB_FOR_PPC($jobid: uuid!) {
|
|||||||
timezone
|
timezone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
exports.QUERY_PARTS_SCAN = `query QUERY_PARTS_SCAN ($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
bodyshop {
|
||||||
|
id
|
||||||
|
md_parts_scan
|
||||||
|
}
|
||||||
|
joblines(where: {removed: {_eq: false}}) {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
critical
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCritical:[uuid!]!, $jobid: uuid!){
|
||||||
|
critical: update_joblines(where:{id:{_in:$IdsToMarkCritical}}, _set:{critical: true}){
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
notcritical: update_joblines(where:{id:{_nin:$IdsToMarkCritical}, jobid: {_eq: $jobid}}, _set:{critical: false}){
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|||||||
58
server/parts-scan/parts-scan.js
Normal file
58
server/parts-scan/parts-scan.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const Dinero = require("dinero.js");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const { job } = require("../scheduling/scheduling-job");
|
||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
const _ = require("lodash");
|
||||||
|
// Dinero.defaultCurrency = "USD";
|
||||||
|
// Dinero.globalLocale = "en-CA";
|
||||||
|
|
||||||
|
exports.partsScan = async function (req, res) {
|
||||||
|
const BearerToken = req.headers.authorization;
|
||||||
|
const { jobid } = req.body;
|
||||||
|
logger.log("job-parts-scan", "DEBUG", req.user?.email, jobid, null);
|
||||||
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
|
headers: {
|
||||||
|
Authorization: BearerToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
//Query all jobline data using the user's authorization.
|
||||||
|
const data = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.QUERY_PARTS_SCAN, {
|
||||||
|
id: jobid,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Create RegExps once for better performance.
|
||||||
|
const IdsToMarkCritical = [];
|
||||||
|
const RegExpressions = data.jobs_by_pk.bodyshop.md_parts_scan.map(
|
||||||
|
(r) => new RegExp(r.expression, r.flags)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Check each line against each regex rule.
|
||||||
|
data.jobs_by_pk.joblines.forEach((jobline) => {
|
||||||
|
RegExpressions.forEach((rExp) => {
|
||||||
|
if (jobline.line_desc.match(rExp)) {
|
||||||
|
IdsToMarkCritical.push(jobline);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.UPDATE_PARTS_CRITICAL, {
|
||||||
|
IdsToMarkCritical: _.uniqBy(IdsToMarkCritical, "id").map((i) => i.id),
|
||||||
|
jobid: jobid,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("job-parts-scan-error", "ERROR", req.user.email, jobid, {
|
||||||
|
jobid,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
res.status(400).json(JSON.stringify(error));
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user