Compare commits

..

2 Commits

Author SHA1 Message Date
Patrick Fic
b5a2be9392 Package updates. 2022-04-18 10:27:06 -07:00
Patrick Fic
df1e15be60 All package upgrades. 2022-04-13 17:46:46 -07:00
165 changed files with 4744 additions and 37974 deletions

View File

@@ -1,21 +0,0 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"rules": {
"no-console": "off"
},
"settings": {},
"plugins": ["cypress"]
}

View File

@@ -1243,69 +1243,6 @@
<folder_node> <folder_node>
<name>messages</name> <name>messages</name>
<children> <children>
<concept_node>
<name>admin_jobmarkexported</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>admin_jobmarkforreexport</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>admin_jobunvoid</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>billposted</name> <name>billposted</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -2674,27 +2611,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>calculatedcreditsnotreceived</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>creditsnotreceived</name> <name>creditsnotreceived</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -3586,27 +3502,6 @@
<folder_node> <folder_node>
<name>fields</name> <name>fields</name>
<children> <children>
<concept_node>
<name>ReceivableCustomField</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>address1</name> <name>address1</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -4556,69 +4451,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>localmediaserverhttp</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>localmediaservernetwork</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>localmediatoken</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>logo_img_footer_margin</name> <name>logo_img_footer_margin</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -5904,27 +5736,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>list-ready</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>partsqueue</name> <name>partsqueue</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -7818,27 +7629,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>additional_board_statuses</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>color</name> <name>color</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -8238,27 +8028,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>ready_statuses</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> </children>
</folder_node> </folder_node>
<concept_node> <concept_node>
@@ -8345,27 +8114,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>uselocalmediaserver</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>website</name> <name>website</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -9032,48 +8780,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>md_to_emails</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>md_to_emails_emails</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>messagingpresets</name> <name>messagingpresets</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -13358,27 +13064,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>openinexplorer</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>reassign_limitexceeded</name> <name>reassign_limitexceeded</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -14164,27 +13849,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>external_id</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>first_name</name> <name>first_name</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -14682,27 +14346,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>priorsuccesfulexport</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> </children>
</folder_node> </folder_node>
</children> </children>
@@ -16219,27 +15862,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>refresh</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>required</name> <name>required</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -23525,27 +23147,6 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<concept_node>
<name>partsstatus</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>pas</name> <name>pas</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -23698,27 +23299,6 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<concept_node>
<name>queued_for_parts</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>rate_ats</name> <name>rate_ats</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -27327,27 +26907,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>calculatedcreditsnotreceived</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>creditmemos</name> <name>creditmemos</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -30081,27 +29640,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>readyjobs</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>recent</name> <name>recent</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -31768,27 +31306,6 @@
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <children>
<concept_node>
<name>addtorelatedro</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>newnoteplaceholder</name> <name>newnoteplaceholder</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -32626,27 +32143,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>orderinhouse</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> </children>
</folder_node> </folder_node>
</children> </children>
@@ -32809,48 +32305,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>saving</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>updating</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> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>
@@ -32919,27 +32373,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>cm_received</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>comments</name> <name>comments</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -33365,27 +32798,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>mark_as_received</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>newpartsorder</name> <name>newpartsorder</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -33533,27 +32945,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>removefrompartsqueue</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>returnpartsorder</name> <name>returnpartsorder</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -33601,27 +32992,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>line_updated</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>received</name> <name>received</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -35497,27 +34867,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>filing_coversheet_landscape</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>filing_coversheet_portrait</name> <name>filing_coversheet_portrait</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -36905,32 +36254,6 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node>
<name>special</name>
<children>
<concept_node>
<name>attendance_detail_csv</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>
<folder_node> <folder_node>
<name>subjects</name> <name>subjects</name>
<children> <children>
@@ -37342,27 +36665,6 @@
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <children>
<concept_node>
<name>actual_in</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>alert</name> <name>alert</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -37814,27 +37116,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>partsstatus</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>production_note</name> <name>production_note</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -38709,27 +37990,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>credits_not_received_date_vendorid</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>csi</name> <name>csi</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -42551,27 +41811,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>jobs-ready</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>owner-detail</name> <name>owner-detail</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -43582,27 +42821,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>readyjobs</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>resetpassword</name> <name>resetpassword</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -44270,27 +43488,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>notes</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>plate_no</name> <name>plate_no</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -3,55 +3,59 @@
"version": "0.2.1", "version": "0.2.1",
"private": true, "private": true,
"proxy": "http://localhost:4000", "proxy": "http://localhost:4000",
"browser": {
"fs": false,
"path": false,
"os": false
},
"dependencies": { "dependencies": {
"@apollo/client": "^3.6.2", "@apollo/client": "^3.5.10",
"@asseinfo/react-kanban": "^2.2.0", "@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.3", "@craco/craco": "^6.4.3",
"@fingerprintjs/fingerprintjs": "^3.3.3", "@fingerprintjs/fingerprintjs": "^3.3.3",
"@sentry/react": "^6.19.7", "@jsreport/browser-client": "^3.1.0",
"@sentry/tracing": "^6.19.7", "@sentry/react": "^6.19.6",
"@splitsoftware/splitio-react": "^1.4.1", "@sentry/tracing": "^6.19.6",
"@stripe/react-stripe-js": "^1.8.0", "@splitsoftware/splitio-react": "^1.4.0",
"@stripe/stripe-js": "^1.29.0", "@stripe/react-stripe-js": "^1.7.1",
"@stripe/stripe-js": "^1.27.0",
"@tanem/react-nprogress": "^5.0.0", "@tanem/react-nprogress": "^5.0.0",
"antd": "^4.20.5", "antd": "^4.19.5",
"apollo-link-logger": "^2.0.0", "apollo-link-logger": "^2.0.0",
"axios": "^0.27.2", "axios": "^0.26.1",
"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.0",
"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.8.1", "firebase": "^9.6.10",
"graphql": "^16.5.0", "graphql": "^16.3.0",
"i18next": "^21.8.2", "i18next": "^21.6.16",
"i18next-browser-languagedetector": "^6.1.4", "i18next-browser-languagedetector": "^6.1.4",
"jsoneditor": "^9.7.4", "jsoneditor": "^9.7.4",
"jsreport-browser-client-dist": "^1.3.0", "libphonenumber-js": "^1.9.51",
"libphonenumber-js": "^1.9.53", "logrocket": "^2.2.1",
"logrocket": "^3.0.0", "markerjs2": "^2.21.0",
"markerjs2": "^2.21.4",
"moment-business-days": "^1.2.0", "moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34", "moment-timezone": "^0.5.34",
"normalize-url": "^7.0.3", "phone": "^3.1.15",
"phone": "^3.1.17",
"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.1",
"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": "^18.0.0",
"react-big-calendar": "^0.40.1", "react-big-calendar": "^0.40.0",
"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": "^18.0.0",
"react-drag-listview": "^0.2.0", "react-drag-listview": "^0.1.9",
"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.16.9", "react-i18next": "^11.16.6",
"react-icons": "^4.3.1", "react-icons": "^4.3.1",
"react-number-format": "^4.9.3", "react-number-format": "^4.9.1",
"react-redux": "^7.2.8", "react-redux": "^7.2.8",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0", "react-router-dom": "^5.3.0",
@@ -60,13 +64,13 @@
"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.9", "recharts": "^2.1.9",
"redux": "^4.2.0", "redux": "^4.1.2",
"redux-persist": "^6.0.0", "redux-persist": "^6.0.0",
"redux-saga": "^1.1.3", "redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2", "redux-state-sync": "^3.1.2",
"reselect": "^4.1.5", "reselect": "^4.1.5",
"sass": "^1.51.0", "sass": "^1.50.0",
"socket.io-client": "^4.5.0", "socket.io-client": "^4.4.1",
"styled-components": "^5.3.5", "styled-components": "^5.3.5",
"subscriptions-transport-ws": "^0.11.0", "subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
@@ -118,9 +122,9 @@
"react-error-overlay": "6.0.9" "react-error-overlay": "6.0.9"
}, },
"devDependencies": { "devDependencies": {
"@sentry/webpack-plugin": "^1.18.9", "@sentry/webpack-plugin": "^1.18.8",
"@testing-library/cypress": "^8.0.2", "@testing-library/cypress": "^8.0.2",
"cypress": "^9.6.1", "cypress": "^9.5.4",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"react-error-overlay": "6.0.11", "react-error-overlay": "6.0.11",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

View File

@@ -13,7 +13,6 @@ import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -28,7 +27,7 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(AccountingPayablesTableComponent); )(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills, refetch }) { export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]); const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false); const [transInProgress, setTransInProgress] = useState(false);
@@ -132,9 +131,11 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
dataIndex: "attempts", dataIndex: "attempts",
key: "attempts", key: "attempts",
render: (text, record) => ( render: (text, record) => {
<ExportLogsCountDisplay logs={record.exportlogs} /> const success = record.exportlogs.filter((e) => e.successful).length;
), const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
}, },
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
@@ -149,7 +150,6 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
disabled={transInProgress || !!record.exported} disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills} setSelectedBills={setSelectedBills}
refetch={refetch}
/> />
</div> </div>
), ),
@@ -182,7 +182,6 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
disabled={transInProgress || selectedBills.length === 0} disabled={transInProgress || selectedBills.length === 0}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
completedCallback={setSelectedBills} completedCallback={setSelectedBills}
refetch={refetch}
/> />
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && ( {bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent /> <QboAuthorizeComponent />

View File

@@ -13,7 +13,6 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -32,7 +31,6 @@ export function AccountingPayablesTableComponent({
bodyshop, bodyshop,
loading, loading,
payments, payments,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedPayments, setSelectedPayments] = useState([]); const [selectedPayments, setSelectedPayments] = useState([]);
@@ -132,9 +130,11 @@ export function AccountingPayablesTableComponent({
dataIndex: "attempts", dataIndex: "attempts",
key: "attempts", key: "attempts",
render: (text, record) => ( render: (text, record) => {
<ExportLogsCountDisplay logs={record.exportlogs} /> const success = record.exportlogs.filter((e) => e.successful).length;
), const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
}, },
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
@@ -148,7 +148,6 @@ export function AccountingPayablesTableComponent({
disabled={transInProgress || !!record.exportedat} disabled={transInProgress || !!record.exportedat}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
setSelectedPayments={setSelectedPayments} setSelectedPayments={setSelectedPayments}
refetch={refetch}
/> />
), ),
}, },
@@ -189,7 +188,6 @@ export function AccountingPayablesTableComponent({
disabled={transInProgress || selectedPayments.length === 0} disabled={transInProgress || selectedPayments.length === 0}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments} completedCallback={setSelectedPayments}
refetch={refetch}
/> />
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && ( {bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent /> <QboAuthorizeComponent />

View File

@@ -14,7 +14,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component"; import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -31,7 +30,6 @@ export function AccountingReceivablesTableComponent({
bodyshop, bodyshop,
loading, loading,
jobs, jobs,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]); const [selectedJobs, setSelectedJobs] = useState([]);
@@ -141,9 +139,12 @@ export function AccountingReceivablesTableComponent({
title: t("exportlogs.labels.attempts"), title: t("exportlogs.labels.attempts"),
dataIndex: "attempts", dataIndex: "attempts",
key: "attempts", key: "attempts",
render: (text, record) => (
<ExportLogsCountDisplay logs={record.exportlogs} /> render: (text, record) => {
), const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
}, },
{ {
title: t("general.labels.actions"), title: t("general.labels.actions"),
@@ -156,7 +157,6 @@ export function AccountingReceivablesTableComponent({
jobId={record.id} jobId={record.id}
disabled={!!record.date_exported} disabled={!!record.date_exported}
setSelectedJobs={setSelectedJobs} setSelectedJobs={setSelectedJobs}
refetch={refetch}
/> />
<Link to={`/manage/jobs/${record.id}/close`}> <Link to={`/manage/jobs/${record.id}/close`}>
<Button>{t("jobs.labels.viewallocations")}</Button> <Button>{t("jobs.labels.viewallocations")}</Button>
@@ -207,7 +207,6 @@ export function AccountingReceivablesTableComponent({
disabled={transInProgress || selectedJobs.length === 0} disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress} loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs} completedCallback={setSelectedJobs}
refetch={refetch}
/> />
)} )}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && ( {bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (

View File

@@ -1,136 +0,0 @@
import { Checkbox, Form, Skeleton, Typography } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-cm-returns-table.styles.scss";
export default function BillCmdReturnsTableComponent({
form,
loadOutstandingReturns,
returnLoading,
returnData,
}) {
const { t } = useTranslation();
useEffect(() => {
if (returnData) {
form.setFieldsValue({
outstanding_returns: returnData.parts_order_lines,
});
}
}, [returnData, form]);
return (
<Form.Item
shouldUpdate={(prev, cur) =>
prev.jobid !== cur.jobid ||
prev.is_credit_memo !== cur.is_credit_memo ||
prev.vendorid !== cur.vendorid
}
noStyle
>
{() => {
const isReturn = form.getFieldValue("is_credit_memo");
if (!isReturn) {
return null;
}
if (returnLoading) return <Skeleton />;
return (
<Form.List name="outstanding_returns">
{(fields, { add, remove, move }) => {
return (
<>
<Typography.Title level={4}>
{t("bills.labels.creditsnotreceived")}
</Typography.Title>
<table className="bill-cm-returns-table">
<thead>
<tr>
<th>{t("parts_orders.fields.line_desc")}</th>
<th>{t("parts_orders.fields.part_type")}</th>
<th>{t("parts_orders.fields.quantity")}</th>
<th>{t("parts_orders.fields.act_price")}</th>
<th>{t("parts_orders.fields.cost")}</th>
<th>{t("parts_orders.labels.mark_as_received")}</th>
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.key}>
<td>
<Form.Item
// label={t("joblines.fields.line_desc")}
key={`${index}line_desc`}
name={[field.name, "line_desc"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}quantity`}
name={[field.name, "quantity"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}act_price`}
name={[field.name, "act_price"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cost`}
name={[field.name, "cost"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cm_received`}
name={[field.name, "cm_received"]}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}}
</Form.List>
);
}}
</Form.Item>
);
}

View File

@@ -1,19 +0,0 @@
.bill-cm-returns-table {
table-layout: fixed;
width: 100%;
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
.ant-form-item {
margin-bottom: 0px !important;
}
}
tr:hover {
background-color: #f5f5f5;
}
}

View File

@@ -12,29 +12,27 @@ import moment from "moment";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { useLocation, useHistory } from "react-router-dom";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { import {
DELETE_BILL_LINE, DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES, INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE, UPDATE_BILL_LINE,
} from "../../graphql/bill-lines.queries"; } from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries"; import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, //currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
@@ -51,7 +49,6 @@ export default connect(
export function BillDetailEditcontainer({ export function BillDetailEditcontainer({
setPartsOrderContext, setPartsOrderContext,
insertAuditTrail, insertAuditTrail,
bodyshop,
}) { }) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useHistory();
@@ -268,21 +265,12 @@ export function BillDetailEditcontainer({
layout="vertical" layout="vertical"
> >
<BillFormContainer form={form} billEdit disabled={exported} /> <BillFormContainer form={form} billEdit disabled={exported} />
<JobDocumentsGallery
{bodyshop.uselocalmediaserver ? ( jobId={data ? data.bills_by_pk.jobid : null}
<JobsDocumentsLocalGallery billId={search.billid}
job={{ id: data ? data.bills_by_pk.jobid : null }} documentsList={data ? data.bills_by_pk.documents : []}
invoice_number={data ? data.bills_by_pk.invoice_number : null} billsCallback={refetch}
vendorid={data ? data.bills_by_pk.vendorid : null} />
/>
) : (
<JobDocumentsGallery
jobId={data ? data.bills_by_pk.jobid : null}
billId={search.billid}
documentsList={data ? data.bills_by_pk.documents : []}
billsCallback={refetch}
/>
)}
</Form> </Form>
</> </>
)} )}

View File

@@ -11,7 +11,6 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS, QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB, UPDATE_JOB,
} from "../../graphql/jobs.queries"; } from "../../graphql/jobs.queries";
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors"; import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
@@ -24,7 +23,6 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility"; import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility"; import { handleUpload } from "../documents-upload/documents-upload.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal, billEnterModal: selectBillEnterModal,
@@ -49,7 +47,6 @@ function BillEnterModalContainer({
const [enterAgain, setEnterAgain] = useState(false); const [enterAgain, setEnterAgain] = useState(false);
const [insertBill] = useMutation(INSERT_NEW_BILL); const [insertBill] = useMutation(INSERT_NEW_BILL);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE); const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const client = useApolloClient(); const client = useApolloClient();
@@ -79,8 +76,7 @@ function BillEnterModalContainer({
} }
setLoading(true); setLoading(true);
const { upload, location, outstanding_returns, ...remainingValues } = const { upload, location, ...remainingValues } = values;
values;
let adjustmentsToInsert = {}; let adjustmentsToInsert = {};
@@ -160,25 +156,6 @@ function BillEnterModalContainer({
}); });
} }
const markPolReceived =
outstanding_returns &&
outstanding_returns.filter((o) => o.cm_received === true);
if (markPolReceived && markPolReceived.length > 0) {
const r2 = await updatePartsOrderLines({
variables: { partsLineIds: markPolReceived.map((p) => p.id) },
});
if (!!r2.errors) {
setLoading(false);
setEnterAgain(false);
notification["error"]({
message: t("parts_orders.errors.updating", {
message: JSON.stringify(r2.errors),
}),
});
}
}
if (!!r1.errors) { if (!!r1.errors) {
setLoading(false); setLoading(false);
setEnterAgain(false); setEnterAgain(false);
@@ -211,33 +188,19 @@ function BillEnterModalContainer({
///////////////////////// /////////////////////////
if (upload && upload.length > 0) { if (upload && upload.length > 0) {
//insert Each of the documents? //insert Each of the documents?
upload.forEach((u) => {
if (bodyshop.uselocalmediaserver) { handleUpload(
upload.forEach((u) => { { file: u.originFileObj },
handleLocalUpload({ {
ev: { file: u.originFileObj }, bodyshop: bodyshop,
context: { uploaded_by: currentUser.email,
jobid: values.jobid, jobId: values.jobid,
invoice_number: remainingValues.invoice_number, billId: billId,
vendorid: remainingValues.vendorid, tagsArray: null,
}, callback: null,
}); }
}); );
} else { });
upload.forEach((u) => {
handleUpload(
{ file: u.originFileObj },
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: values.jobid,
billId: billId,
tagsArray: null,
callback: null,
}
);
});
}
} }
/////////////////////////// ///////////////////////////
setLoading(false); setLoading(false);

View File

@@ -47,7 +47,6 @@ export function BillFormComponent({
billEdit, billEdit,
disableInvNumber, disableInvNumber,
job, job,
loadOutstandingReturns,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
@@ -59,14 +58,6 @@ export function BillFormComponent({
); );
const handleVendorSelect = (props, opt) => { const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount); setDiscount(opt.discount);
opt &&
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: opt.value,
},
});
}; };
useEffect(() => { useEffect(() => {
@@ -74,8 +65,8 @@ export function BillFormComponent({
}, [job, form]); }, [job, form]);
useEffect(() => { useEffect(() => {
const vendorId = form.getFieldValue("vendorid"); if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
if (vendorId && vendorAutoCompleteOptions) { const vendorId = form.getFieldValue("vendorid");
const matchingVendors = vendorAutoCompleteOptions.filter( const matchingVendors = vendorAutoCompleteOptions.filter(
(v) => v.id === vendorId (v) => v.id === vendorId
); );
@@ -83,25 +74,10 @@ export function BillFormComponent({
setDiscount(matchingVendors[0].discount); setDiscount(matchingVendors[0].discount);
} }
} }
const jobId = form.getFieldValue("jobid"); if (form.getFieldValue("jobid")) {
if (jobId) { loadLines({ variables: { id: form.getFieldValue("jobid") } });
loadLines({ variables: { id: jobId } });
if (form.getFieldValue("is_credit_memo") && vendorId) {
loadOutstandingReturns({
variables: {
jobId: jobId,
vendorId: vendorId,
},
});
}
} }
}, [ }, [form, setDiscount, vendorAutoCompleteOptions, loadLines]);
form,
loadOutstandingReturns,
setDiscount,
vendorAutoCompleteOptions,
loadLines,
]);
return ( return (
<div> <div>
@@ -131,13 +107,6 @@ export function BillFormComponent({
onBlur={() => { onBlur={() => {
if (form.getFieldValue("jobid") !== null) { if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } }); loadLines({ variables: { id: form.getFieldValue("jobid") } });
if (form.getFieldValue("vendorid") !== null)
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
} }
}} }}
/> />
@@ -259,22 +228,8 @@ export function BillFormComponent({
rules={[ rules={[
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(rule, value) { validator(rule, value) {
if (
value === true &&
getFieldValue("jobid") &&
getFieldValue("vendorid")
) {
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
}
if ( if (
!bodyshop.bill_allow_post_to_closed && !bodyshop.bill_allow_post_to_closed &&
job &&
(job.status === bodyshop.md_ro_statuses.default_invoiced || (job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported || job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) && job.status === bodyshop.md_ro_statuses.default_void) &&

View File

@@ -6,8 +6,6 @@ import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries"; import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component"; import BillFormComponent from "./bill-form.component";
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -29,34 +27,20 @@ export function BillFormContainer({
GET_JOB_LINES_TO_ENTER_BILL GET_JOB_LINES_TO_ENTER_BILL
); );
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
useLazyQuery(QUERY_UNRECEIVED_LINES);
return ( return (
<> <BillFormComponent
<BillFormComponent disabled={disabled}
disabled={disabled} form={form}
form={form} billEdit={billEdit}
billEdit={billEdit} vendorAutoCompleteOptions={
vendorAutoCompleteOptions={ VendorAutoCompleteData && VendorAutoCompleteData.vendors
VendorAutoCompleteData && VendorAutoCompleteData.vendors }
} loadLines={loadLines}
loadLines={loadLines} lineData={lineData ? lineData.joblines : []}
lineData={lineData ? lineData.joblines : []} job={lineData ? lineData.jobs_by_pk : null}
job={lineData ? lineData.jobs_by_pk : null} responsibilityCenters={bodyshop.md_responsibility_centers || null}
responsibilityCenters={bodyshop.md_responsibility_centers || null} disableInvNumber={disableInvNumber}
disableInvNumber={disableInvNumber} />
loadOutstandingReturns={loadOutstandingReturns}
/>
{!billEdit && (
<BillCmdReturnsTableComponent
form={form}
loadOutstandingReturns={loadOutstandingReturns}
returnLoading={returnLoading}
returnData={returnData}
/>
)}
</>
); );
} }
export default connect(mapStateToProps, null)(BillFormContainer); export default connect(mapStateToProps, null)(BillFormContainer);

View File

@@ -43,8 +43,6 @@ export function BillsListTableComponent({
}); });
// const search = queryString.parse(useLocation().search); // const search = queryString.parse(useLocation().search);
// const selectedBill = search.billid; // const selectedBill = search.billid;
const [searchText, setSearchText] = useState("");
const Templates = TemplateList("bill"); const Templates = TemplateList("bill");
const bills = billsQuery.data ? billsQuery.data.bills : []; const bills = billsQuery.data ? billsQuery.data.bills : [];
const { refetch } = billsQuery; const { refetch } = billsQuery;
@@ -61,6 +59,7 @@ export function BillsListTableComponent({
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
} }
onClick={() => { onClick={() => {
console.log(record);
setPartsOrderContext({ setPartsOrderContext({
actions: {}, actions: {},
context: { context: {
@@ -168,24 +167,6 @@ export function BillsListTableComponent({
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
}; };
const filteredBills = bills
? searchText === ""
? bills
: bills.filter(
(b) =>
(b.invoice_number || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.vendor.name || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.total || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
return ( return (
<Card <Card
title={t("bills.labels.bills")} title={t("bills.labels.bills")}
@@ -226,10 +207,8 @@ export function BillsListTableComponent({
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => { onChange={(e) => {
e.preventDefault(); e.preventDefault();
setSearchText(e.target.value);
}} }}
/> />
</Space> </Space>
@@ -242,7 +221,7 @@ export function BillsListTableComponent({
}} }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={filteredBills} dataSource={bills}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card> </Card>

View File

@@ -6,13 +6,12 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries"; import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component"; import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, //currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -20,7 +19,6 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector); export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
export function ChatMediaSelector({ export function ChatMediaSelector({
bodyshop,
selectedMedia, selectedMedia,
setSelectedMedia, setSelectedMedia,
conversation, conversation,
@@ -29,6 +27,7 @@ export function ChatMediaSelector({
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, { const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
@@ -67,8 +66,6 @@ export function ChatMediaSelector({
</div> </div>
); );
if (bodyshop.uselocalmediaserver) return null;
return ( return (
<Popover <Popover
content={ content={

View File

@@ -19,7 +19,7 @@ export function ChatPresetsComponent({ bodyshop, setMessage, className }) {
const menu = ( const menu = (
<Menu> <Menu>
{bodyshop.md_messaging_presets.map((i, idx) => ( {bodyshop.md_messaging_presets.map((i, idx) => (
<Menu.Item onClick={() => setMessage(i.text)} key={idx}> <Menu.Item onClick={() => setMessage(i.text)} onItemHover key={idx}>
{i.label} {i.label}
</Menu.Item> </Menu.Item>
))} ))}

View File

@@ -3,7 +3,6 @@ import { Button, Card, Input, Space, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component"; import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
@@ -106,17 +105,6 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
</Link> </Link>
) : null, ) : null,
}, },
{
title: t("contracts.fields.scheduledreturn"),
dataIndex: "scheduledreturn",
key: "scheduledreturn",
render: (text, record) =>
record.cccontracts.length === 1 && (
<DateTimeFormatter>
{record.cccontracts[0].scheduledreturn}
</DateTimeFormatter>
),
},
]; ];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {

View File

@@ -1,70 +0,0 @@
import { UploadOutlined } from "@ant-design/icons";
import { Upload } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { handleUpload } from "./documents-local-upload.utility";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
export function DocumentsLocalUploadComponent({
children,
currentUser,
bodyshop,
job,
vendorid,
invoice_number,
callbackAfterUpload,
}) {
const [fileList, setFileList] = useState([]);
const handleDone = (uid) => {
setTimeout(() => {
setFileList((fileList) => fileList.filter((x) => x.uid !== uid));
}, 2000);
};
return (
<Upload.Dragger
multiple={true}
fileList={fileList}
onChange={(f) => {
if (f.event && f.event.percent === 100) handleDone(f.file.uid);
setFileList(f.fileList);
}}
customRequest={(ev) =>
handleUpload({
ev,
context: {
jobid: job.id,
vendorid,
invoice_number,
callback: callbackAfterUpload,
},
})
}
accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx"
>
{children || (
<>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
</p>
</>
)}
</Upload.Dragger>
);
}
export default connect(mapStateToProps, null)(DocumentsLocalUploadComponent);

View File

@@ -1,68 +0,0 @@
import cleanAxios from "../../utils/CleanAxios";
import { store } from "../../redux/store";
import { addMediaForJob } from "../../redux/media/media.actions";
import normalizeUrl from "normalize-url";
export const handleUpload = async ({ ev, context }) => {
const { onError, onSuccess, onProgress, file } = ev;
const { jobid, invoice_number, vendorid, callbackAfterUpload } = context;
const bodyshop = store.getState().user.bodyshop;
var options = {
headers: {
"X-Requested-With": "XMLHttpRequest",
ims_token: bodyshop.localmediatoken,
},
onUploadProgress: (e) => {
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
},
};
const formData = new FormData();
formData.append("jobid", jobid);
if (invoice_number) {
formData.append("invoice_number", invoice_number);
formData.append("vendorid", vendorid);
}
formData.append("file", file);
const imexMediaServerResponse = await cleanAxios.post(
normalizeUrl(
`${bodyshop.localmediaserverhttp}/${
invoice_number ? "bills" : "jobs"
}/upload`
),
formData,
{
...options,
}
);
if (imexMediaServerResponse.status !== 200) {
if (!!onError) {
onError(imexMediaServerResponse.statusText);
}
} else {
onSuccess && onSuccess(file);
store.dispatch(
addMediaForJob({
jobid,
media: imexMediaServerResponse.data.map((d) => {
return {
...d,
selected: false,
src: normalizeUrl(`${bodyshop.localmediaserverhttp}/${d.src}`),
thumbnail: normalizeUrl(
`${bodyshop.localmediaserverhttp}/${d.thumbnail}`
),
};
}),
})
);
}
if (callbackAfterUpload) {
callbackAfterUpload();
}
};

View File

@@ -9,7 +9,6 @@ import {
Space, Space,
Menu, Menu,
Dropdown, Dropdown,
Button,
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -21,13 +20,10 @@ import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import { selectEmailConfig } from "../../redux/email/email.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
emailConfig: selectEmailConfig,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -38,7 +34,6 @@ export default connect(
)(EmailOverlayComponent); )(EmailOverlayComponent);
export function EmailOverlayComponent({ export function EmailOverlayComponent({
emailConfig,
form, form,
selectedMediaState, selectedMediaState,
bodyshop, bodyshop,
@@ -47,12 +42,7 @@ export function EmailOverlayComponent({
const { t } = useTranslation(); const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => { const handleClick = ({ item, key, keyPath }) => {
const email = item.props.value; const email = item.props.value;
form.setFieldsValue({ form.setFieldsValue({ to: _.uniq([...form.getFieldValue("to"), email]) });
to: _.uniq([
...form.getFieldValue("to"),
...(typeof email === "string" ? [email] : email),
]),
});
}; };
const menu = ( const menu = (
@@ -65,11 +55,6 @@ export function EmailOverlayComponent({
{`${e.first_name} ${e.last_name}`} {`${e.first_name} ${e.last_name}`}
</Menu.Item> </Menu.Item>
))} ))}
{bodyshop.md_to_emails.map((e, idx) => (
<Menu.Item value={e.emails} key={idx + "group"}>
{e.label}
</Menu.Item>
))}
</Menu> </Menu>
</div> </div>
); );
@@ -158,17 +143,10 @@ export function EmailOverlayComponent({
</Form.Item> </Form.Item>
<Tabs> <Tabs>
{!bodyshop.uselocalmediaserver && ( <Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents"> <EmailDocumentsComponent selectedMediaState={selectedMediaState} />
<EmailDocumentsComponent selectedMediaState={selectedMediaState} /> </Tabs.TabPane>
</Tabs.TabPane>
)}
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments"> <Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
{bodyshop.uselocalmediaserver && emailConfig.jobid && (
<a href={CreateExplorerLinkForJob({ jobid: emailConfig.jobid })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
)}
<Form.Item <Form.Item
name="fileList" name="fileList"
valuePropName="fileList" valuePropName="fileList"

View File

@@ -1,25 +0,0 @@
import React from "react";
import { WarningOutlined } from "@ant-design/icons";
import { Space, Tooltip } from "antd";
import { useTranslation } from "react-i18next";
const style = {
fontWeight: "bold",
color: "green",
};
export default function ExportLogsCountDisplay({ logs }) {
const success = logs.filter((e) => e.successful).length;
const attempts = logs.length;
const { t } = useTranslation();
return (
<Space style={success > 0 ? style : {}}>
{`${success}/${attempts}`}
{success > 0 && (
<Tooltip title={t("exportlogs.labels.priorsuccesfulexport")}>
<WarningOutlined />
</Tooltip>
)}
</Space>
);
}

View File

@@ -3,7 +3,6 @@ import Icon, {
BarChartOutlined, BarChartOutlined,
CarFilled, CarFilled,
ClockCircleFilled, ClockCircleFilled,
CheckCircleOutlined,
DashboardFilled, DashboardFilled,
DollarCircleFilled, DollarCircleFilled,
ExportOutlined, ExportOutlined,
@@ -109,9 +108,6 @@ function Header({
<Menu.Item key="activejobs" icon={<FileFilled />}> <Menu.Item key="activejobs" icon={<FileFilled />}>
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link> <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="readyjobs" icon={<CheckCircleOutlined />}>
<Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
</Menu.Item>
<Menu.Item key="parts-queue" icon={<ToolFilled />}> <Menu.Item key="parts-queue" icon={<ToolFilled />}>
<Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link> <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
</Menu.Item> </Menu.Item>

View File

@@ -49,11 +49,11 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
GenerateDocument( GenerateDocument(
{ {
name: TemplateList("job_special").special_thirdpartypayer.key, name: TemplateList("job_special").thirdpartypayer.key,
variables: { id: jobId }, variables: { id: jobId },
context: restVals, context: restVals,
}, },
{ subject: TemplateList("job_special").special_thirdpartypayer.subject }, { subject: TemplateList("job_special").thirdpartypayer.subject },
sendtype sendtype
); );
}; };

View File

@@ -26,8 +26,6 @@ export default function JobBillsTotalComponent({
let billCms = Dinero(); let billCms = Dinero();
let lbrAdjustments = Dinero(); let lbrAdjustments = Dinero();
let totalReturns = Dinero(); let totalReturns = Dinero();
let totalReturnsMarkedNotReceived = Dinero();
let totalReturnsMarkedReceived = Dinero();
partsOrders.forEach((p) => partsOrders.forEach((p) =>
p.parts_order_lines.forEach((pol) => { p.parts_order_lines.forEach((pol) => {
@@ -37,24 +35,6 @@ export default function JobBillsTotalComponent({
amount: Math.round((pol.act_price || 0) * 100), amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity) }).multiply(pol.quantity)
); );
if (pol.cm_received === null) {
return; // Skip this calculation for bills posted prior to the CNR change.
} else {
if (pol.cm_received === false) {
totalReturnsMarkedNotReceived = totalReturnsMarkedNotReceived.add(
Dinero({
amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity)
);
} else {
totalReturnsMarkedReceived = totalReturnsMarkedReceived.add(
Dinero({
amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity)
);
}
}
} }
}) })
); );
@@ -93,7 +73,7 @@ export default function JobBillsTotalComponent({
const discrepWithLbrAdj = discrepancy.add(lbrAdjustments); const discrepWithLbrAdj = discrepancy.add(lbrAdjustments);
const discrepWithCms = discrepWithLbrAdj.add(totalReturns); const discrepWithCms = discrepWithLbrAdj.add(totalReturns);
const calculatedCreditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number. const creditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
return ( return (
<Row gutter={16}> <Row gutter={16}>
@@ -233,32 +213,6 @@ export default function JobBillsTotalComponent({
value={totalReturns.toFormat()} value={totalReturns.toFormat()}
/> />
</Tooltip> </Tooltip>
<Tooltip
title={
<div
dangerouslySetInnerHTML={{
__html: t(
"jobs.labels.plitooltips.calculatedcreditsnotreceived"
),
}}
/>
}
>
<Statistic
title={t("bills.labels.calculatedcreditsnotreceived")}
valueStyle={{
color:
calculatedCreditsNotReceived.getAmount() <= 0
? "green"
: "red",
}}
value={
calculatedCreditsNotReceived.getAmount() >= 0
? calculatedCreditsNotReceived.toFormat()
: Dinero().toFormat()
}
/>
</Tooltip>
<Tooltip <Tooltip
title={ title={
<div <div
@@ -271,14 +225,11 @@ export default function JobBillsTotalComponent({
<Statistic <Statistic
title={t("bills.labels.creditsnotreceived")} title={t("bills.labels.creditsnotreceived")}
valueStyle={{ valueStyle={{
color: color: creditsNotReceived.getAmount() <= 0 ? "green" : "red",
totalReturnsMarkedNotReceived.getAmount() <= 0
? "green"
: "red",
}} }}
value={ value={
totalReturnsMarkedNotReceived.getAmount() >= 0 creditsNotReceived.getAmount() >= 0
? totalReturnsMarkedNotReceived.toFormat() ? creditsNotReceived.toFormat()
: Dinero().toFormat() : Dinero().toFormat()
} }
/> />

View File

@@ -6,10 +6,8 @@ import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries"; import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import JobSyncButton from "../job-sync-button/job-sync-button.component"; import JobSyncButton from "../job-sync-button/job-sync-button.component";
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component"; import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
@@ -22,10 +20,6 @@ import JobDetailCardsNotesComponent from "./job-detail-cards.notes.component";
import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component"; import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component"; import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) => setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })), dispatch(setModalContext({ context: context, modal: "printCenter" })),
@@ -37,7 +31,7 @@ const span = {
lg: { span: 8 }, lg: { span: 8 },
}; };
export function JobDetailCards({ bodyshop, setPrintCenterContext }) { export function JobDetailCards({ setPrintCenterContext }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
@@ -149,14 +143,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
</Col> </Col>
{!bodyshop.uselocalmediaserver && ( <Col {...span}>
<Col {...span}> <JobDetailCardsDocumentsComponent
<JobDetailCardsDocumentsComponent loading={loading}
loading={loading} data={data ? data.jobs_by_pk : null}
data={data ? data.jobs_by_pk : null} />
/> </Col>
</Col>
)}
<Col {...span}> <Col {...span}>
<JobDetailCardsDamageComponent <JobDetailCardsDamageComponent
loading={loading} loading={loading}
@@ -169,4 +161,4 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
</Drawer> </Drawer>
); );
} }
export default connect(mapStateToProps, mapDispatchToProps)(JobDetailCards); export default connect(null, mapDispatchToProps)(JobDetailCards);

View File

@@ -6,7 +6,6 @@ import {
EditFilled, EditFilled,
PlusCircleTwoTone, PlusCircleTwoTone,
MinusCircleTwoTone, MinusCircleTwoTone,
HomeOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { import {
@@ -43,7 +42,6 @@ import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component"; import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLinesExpander from "./job-lines-expander.component"; import JobLinesExpander from "./job-lines-expander.component";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -56,8 +54,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "jobLineEdit" })), dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })), dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })),
}); });
export function JobLinesComponent({ export function JobLinesComponent({
@@ -72,7 +68,6 @@ export function JobLinesComponent({
job, job,
setJobLineEditContext, setJobLineEditContext,
form, form,
setBillEnterContext,
}) { }) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK); const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
@@ -391,62 +386,6 @@ export function JobLinesComponent({
</Space> </Space>
</Tag> </Tag>
)} )}
<Button
disabled={
(job && !job.converted) ||
(selectedLines.length > 0 ? false : true) ||
jobRO ||
technician
}
onClick={() => {
// setPartsOrderContext({
// actions: { refetch: refetch },
// context: {
// jobId: job.id,
// job: job,
// linesToOrder: selectedLines,
// },
// });
setBillEnterContext({
actions: { refetch: refetch },
context: {
disableInvNumber: true,
job: { id: job.id },
bill: {
vendorid: bodyshop.inhousevendorid,
invoice_number: "ih",
isinhouse: true,
date: new moment(),
total: 0,
billlines: selectedLines.map((p) => {
return {
joblineid: p.id,
actual_price: p.act_price,
actual_cost: 0, //p.act_price,
line_desc: p.line_desc,
line_remarks: p.line_remarks,
part_type: p.part_type,
quantity: p.quantity || 1,
applicable_taxes: {
local: false,
state: false,
federal: false,
},
};
}),
},
},
});
//Clear out the selected lines. IO-785
setSelectedLines([]);
}}
>
<HomeOutlined />
{t("parts.actions.orderinhouse")}
{selectedLines.length > 0 && ` (${selectedLines.length})`}
</Button>
<Button <Button
disabled={ disabled={
(job && !job.converted) || (job && !job.converted) ||

View File

@@ -24,7 +24,7 @@ export function JoblinePresetButton({ bodyshop, form }) {
const menu = ( const menu = (
<Menu> <Menu>
{bodyshop.md_jobline_presets.map((i, idx) => ( {bodyshop.md_jobline_presets.map((i, idx) => (
<Menu.Item onClick={() => handleSelect(i)} key={idx}> <Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
{i.label} {i.label}
</Menu.Item> </Menu.Item>
))} ))}

View File

@@ -141,9 +141,7 @@ export function JobLinesUpsertModalComponent({
rules={[ rules={[
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(rule, value) { validator(rule, value) {
if ( if (!!getFieldValue("mod_lbr_ty") === !!value) {
!!getFieldValue("mod_lbr_ty") === (!!value || value === 0)
) {
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject( return Promise.reject(

View File

@@ -1,80 +0,0 @@
import React, { useMemo } from "react";
import { Row, Col, Tag, Tooltip } from "antd";
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)(JobPartsQueueCount);
export function JobPartsQueueCount({ bodyshop, parts }) {
const partsStatus = useMemo(() => {
if (!parts) return null;
return parts.reduce(
(acc, val) => {
if (val.part_type === "PAS" || val.part_type === "PASL") return acc;
acc.total = acc.total + val.count;
acc[val.status] = acc[val.status] + val.count;
return acc;
},
{
total: 0,
null: 0,
[bodyshop.md_order_statuses.default_bo]: 0,
[bodyshop.md_order_statuses.default_ordered]: 0,
[bodyshop.md_order_statuses.default_received]: 0,
[bodyshop.md_order_statuses.default_returned]: 0,
}
);
}, [bodyshop, parts]);
if (!parts) return null;
return (
<Row>
<Col span={4}>
<Tooltip title="Total">
<Tag>{partsStatus.total}</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title="No Status">
<Tag color="gold">{partsStatus["null"]}</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_ordered}>
<Tag color="blue">
{partsStatus[bodyshop.md_order_statuses.default_ordered]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_received}>
<Tag color="green">
{partsStatus[bodyshop.md_order_statuses.default_received]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_returned}>
<Tag color="orange">
{partsStatus[bodyshop.md_order_statuses.default_returned]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_bo}>
<Tag color="red">
{partsStatus[bodyshop.md_order_statuses.default_bo]}
</Tag>
</Tooltip>
</Col>
</Row>
);
}

View File

@@ -1,23 +1,24 @@
import { useMutation } from "@apollo/client"; import { Button, notification } from "antd";
import { Checkbox, notification, Space, Spin } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useMutation } from "@apollo/client";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { useTranslation } from "react-i18next";
export default function JobRemoveFromPartsQueue({ checked, jobId }) { export default function JobRemoveFromPartsQueue({ jobId, refetch }) {
const [updateJob] = useMutation(UPDATE_JOB); const [updateJob] = useMutation(UPDATE_JOB);
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleChange = async (e) => { const handleClick = async (e) => {
setLoading(true); setLoading(true);
const result = await updateJob({ const result = await updateJob({
variables: { jobId: jobId, job: { queued_for_parts: e.target.checked } }, variables: { jobId: jobId, job: { queued_for_parts: false } },
}); });
if (!!!result.errors) { if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
if (refetch) refetch();
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.saving", { message: t("jobs.errors.saving", {
@@ -29,9 +30,8 @@ export default function JobRemoveFromPartsQueue({ checked, jobId }) {
}; };
return ( return (
<Space> <Button onClick={handleClick} loading={loading}>
<Checkbox checked={checked} onChange={handleChange} /> {t("general.actions.remove")}
{loading && <Spin size="small" />} </Button>
</Space>
); );
} }

View File

@@ -6,20 +6,17 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries"; import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) => //setUserLanguage: language => dispatch(setUserLanguage(language))
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus); export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) { export function JobsAdminStatus({ bodyshop, job }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS); const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
@@ -29,10 +26,6 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
}) })
.then((r) => { .then((r) => {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobstatuschange(status),
});
// refetch(); // refetch();
}) })
.catch((error) => { .catch((error) => {

View File

@@ -7,27 +7,8 @@ import DateTimePicker from "../form-date-time-picker/form-date-time-picker.compo
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import moment from "moment"; import moment from "moment";
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { connect } from "react-redux"; export default function JobsAdminDatesChange({ job }) {
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsAdminDatesChange);
export function JobsAdminDatesChange({ insertAuditTrail, job }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -39,23 +20,6 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
variables: { jobId: job.id, job: values }, variables: { jobId: job.id, job: values },
}); });
const changedAuditFields = form.getFieldsValue(
true,
(meta) => meta && meta.touched
);
Object.keys(changedAuditFields).forEach((key) => {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobfieldchange(
key,
changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
: changedAuditFields[key]
),
});
});
if (!!!result.errors) { if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
} else { } else {

View File

@@ -8,21 +8,18 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment"; import moment from "moment";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) => //setUserLanguage: language => dispatch(setUserLanguage(language))
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export default connect( export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(JobAdminMarkReexport); )(JobAdminMarkReexport);
export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) { export function JobAdminMarkReexport({ bodyshop, job }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [markJobForReexport] = useMutation(gql` const [markJobForReexport] = useMutation(gql`
@@ -81,10 +78,6 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
if (!result.errors) { if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkforreexport(),
});
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.saving", { message: t("jobs.errors.saving", {
@@ -103,10 +96,6 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
if (!result.errors) { if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkexported(),
});
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.saving", { message: t("jobs.errors.saving", {

View File

@@ -4,29 +4,21 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) => //setUserLanguage: language => dispatch(setUserLanguage(language))
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid); export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
export function JobsAdminUnvoid({ export function JobsAdminUnvoid({ bodyshop, job, currentUser }) {
insertAuditTrail,
bodyshop,
job,
currentUser,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(gql` const [updateJob] = useMutation(gql`
@@ -92,11 +84,6 @@ mutation UNVOID_JOB($jobId: uuid!) {
if (!result.errors) { if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") }); notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_unvoicejob(),
});
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.saving", { message: t("jobs.errors.saving", {

View File

@@ -15,6 +15,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -26,7 +27,6 @@ export function JobsCloseExportButton({
jobId, jobId,
disabled, disabled,
setSelectedJobs, setSelectedJobs,
refetch,
}) { }) {
const history = useHistory(); const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -46,10 +46,13 @@ export function JobsCloseExportButton({
//Check if it's a QBO Setup. //Check if it's a QBO Setup.
let PartnerResponse; let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, { PartnerResponse = await axios.post(
jobIds: [jobId], `/qbo/receivables`,
elgen: true, {
}); jobIds: [jobId],
},
);
} else { } else {
//Default is QBD //Default is QBD
@@ -114,64 +117,58 @@ export function JobsCloseExportButton({
}); });
}); });
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { await insertExportLog({
//QBO Logs are handled server side. variables: {
await insertExportLog({ logs: [
variables: { {
logs: [ bodyshopid: bodyshop.id,
{ jobid: jobId,
bodyshopid: bodyshop.id, successful: false,
jobid: jobId, message: JSON.stringify(
successful: false, failedTransactions.map((ft) => ft.errorMessage)
message: JSON.stringify( ),
failedTransactions.map((ft) => ft.errorMessage) useremail: currentUser.email,
), },
useremail: currentUser.email, ],
}, },
], });
},
});
}
} else { } else {
//Insert success export log. //Insert success export log.
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { await insertExportLog({
//QBO Logs are handled server side. variables: {
await insertExportLog({ logs: [
variables: { {
logs: [ bodyshopid: bodyshop.id,
{ jobid: jobId,
bodyshopid: bodyshop.id, successful: true,
jobid: jobId, useremail: currentUser.email,
successful: true,
useremail: currentUser.email,
},
],
},
});
const jobUpdateResponse = await updateJob({
variables: {
jobId: jobId,
job: {
status: bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
}, },
}, ],
}); },
});
if (!jobUpdateResponse.errors) { const jobUpdateResponse = await updateJob({
notification.open({ variables: {
type: "success", jobId: jobId,
key: "jobsuccessexport", job: {
message: t("jobs.successes.exported"), status: bodyshop.md_ro_statuses.default_exported || "Exported*",
}); date_exported: new Date(),
} else { },
notification["error"]({ },
message: t("jobs.errors.exporting", { });
error: JSON.stringify(jobUpdateResponse.error),
}), if (!jobUpdateResponse.errors) {
}); notification.open({
} type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
});
} }
if (setSelectedJobs) { if (setSelectedJobs) {
setSelectedJobs((selectedJobs) => { setSelectedJobs((selectedJobs) => {
@@ -179,7 +176,7 @@ export function JobsCloseExportButton({
}); });
} }
} }
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false); setLoading(false);
}; };

View File

@@ -146,11 +146,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel <Collapse.Panel forceRender key="claim" header={t("menus.jobsdetail.claimdetail")}>
forceRender
key="claim"
header={t("menus.jobsdetail.claimdetail")}
>
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc"> <Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
<Input /> <Input />
@@ -197,8 +193,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel <Collapse.Panel forceRender
forceRender
key="financial" key="financial"
header={t("menus.jobsdetail.financials")} header={t("menus.jobsdetail.financials")}
> >
@@ -209,7 +204,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status"> <Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
<Select allowClear> <Select>
<Select.Option value="W"> <Select.Option value="W">
{t("jobs.labels.deductible.waived")} {t("jobs.labels.deductible.waived")}
</Select.Option> </Select.Option>

View File

@@ -16,18 +16,15 @@ export function JobsDetailChangeFilehandler({ disabled, form, bodyshop }) {
}; };
const menu = ( const menu = (
<Menu <div>
onClick={handleClick} <Menu onClick={handleClick}>
style={{ {bodyshop.md_filehandlers.map((est, idx) => (
columnCount: Math.floor(bodyshop.md_filehandlers.length / 10) + 1, <Menu.Item value={est} key={idx}>
}} {`${est.ins_ct_fn} ${est.ins_ct_ln}`}
> </Menu.Item>
{bodyshop.md_filehandlers.map((est, idx) => ( ))}
<Menu.Item value={est} key={idx} style={{ breakInside: "avoid" }}> </Menu>
{`${est.ins_ct_fn} ${est.ins_ct_ln}`} </div>
</Menu.Item>
))}
</Menu>
); );
return ( return (

View File

@@ -216,22 +216,6 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel label={t("jobs.labels.relatedros")}> <DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} /> <JobsRelatedRos jobid={job.id} job={job} />
</DataLabel> </DataLabel>
{job.vehicle && job.vehicle.notes && (
<DataLabel label={t("vehicles.fields.notes")}>
<span style={{ whiteSpace: "pre" }}>{job.vehicle.notes}</span>
</DataLabel>
)}
{
// job.vehicle && job.vehicle.v_paint_codes && (
// <DataLabel label={t("vehicles.fields.v_paint_codes")}>
// <span style={{ whiteSpace: "pre" }}>
// {Object.keys(job.vehicle.v_paint_codes).map((key, idx) => (
// <Tag key={idx}>{job.vehicle.v_paint_codes[key]}</Tag>
// ))}
// </span>
// </DataLabel>
// )
}
</div> </div>
</Card> </Card>
</Col> </Col>

View File

@@ -97,7 +97,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
noStyle nostyle
shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats} shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats}
> >
{() => { {() => {

View File

@@ -1,5 +1,5 @@
import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons"; import { FileExcelFilled, EditFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Col, Row, Space } from "antd"; import { Card, Col, Row, Space, Button } from "antd";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Gallery from "react-grid-gallery"; import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";

View File

@@ -1,107 +0,0 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Space } from "antd";
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
getBillMedia,
getJobMedia,
toggleMediaSelected,
} from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
allMedia: selectAllMedia,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
getBillMedia: ({ jobid, invoice_number }) => {
dispatch(getBillMedia({ jobid, invoice_number }));
},
toggleMediaSelected: ({ jobid, filename }) =>
dispatch(toggleMediaSelected({ jobid, filename })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalGallery);
export function JobsDocumentsLocalGallery({
bodyshop,
toggleMediaSelected,
getJobMedia,
getBillMedia,
allMedia,
job,
invoice_number,
vendorid,
}) {
const { t } = useTranslation();
useEffect(() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}, [job, invoice_number, getJobMedia, getBillMedia]);
return (
<div>
<Space wrap>
<Button
onClick={() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}}
>
<SyncOutlined />
</Button>
<a href={CreateExplorerLinkForJob({ jobid: job.id })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
</Space>
<Card>
<DocumentsLocalUploadComponent
job={job}
invoice_number={invoice_number}
vendorid={vendorid}
/>
</Card>
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={(allMedia && allMedia[job.id]) || []}
backdropClosesModal={true}
onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
onClickImage={(props) => {
window.open(
props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
/>
</Card>
</div>
);
}

View File

@@ -1,96 +0,0 @@
import { Button, Form, Popover, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getJobMedia } from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import cleanAxios from "../../utils/CleanAxios";
import JobSearchSelect from "../job-search-select/job-search-select.component";
const mapStateToProps = createStructuredSelector({
allMedia: selectAllMedia,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalGalleryReassign);
export function JobsDocumentsLocalGalleryReassign({
bodyshop,
jobid,
allMedia,
getJobMedia,
}) {
const { t } = useTranslation();
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const handleFinish = async ({ jobid: newJobid }) => {
setLoading(true);
const selectedDocuments = allMedia[jobid].filter((m) => m.isSelected);
await cleanAxios.post(
`${bodyshop.localmediaserverhttp}/jobs/move`,
{
from_jobid: jobid,
jobid: newJobid,
files: selectedDocuments.map((f) => f.filename),
},
{ headers: { ims_token: bodyshop.localmediatoken } }
);
getJobMedia(jobid);
setVisible(false);
setLoading(false);
};
const popContent = (
<div>
<Form onFinish={handleFinish} layout="vertical" form={form}>
<Form.Item
label={t("documents.labels.newjobid")}
style={{ width: "20rem" }}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={"jobid"}
>
<JobSearchSelect />
</Form.Item>
</Form>
<Space>
<Button type="primary" onClick={() => form.submit()}>
{t("general.actions.submit")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</div>
);
return (
<Popover content={popContent} visible={visible}>
<Button
//disabled={selectedImages.length < 1}
onClick={() => setVisible(true)}
loading={loading}
>
{t("documents.actions.reassign")}
</Button>
</Popover>
);
}

View File

@@ -1,53 +0,0 @@
import { Button, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectAllmediaForJob,
deselectAllMediaForJob,
} from "../../redux/media/media.actions";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
selectAllmediaForJob: (jobid) => dispatch(selectAllmediaForJob(jobid)),
deselectAllmediaForJob: (jobid) => dispatch(deselectAllMediaForJob(jobid)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalGallerySelectAllComponent);
export function JobsDocumentsLocalGallerySelectAllComponent({
jobid,
selectAllmediaForJob,
deselectAllmediaForJob,
}) {
const { t } = useTranslation();
// onSelectImage={(index, image) => {
// toggleMediaSelected({ jobid: job.id, filename: image.filename });
// }}
const handleSelectAll = () => {
selectAllmediaForJob({ jobid });
};
const handleDeselectAll = () => {
deselectAllmediaForJob({ jobid });
};
return (
<Space wrap>
<Button onClick={handleSelectAll}>
{t("general.actions.selectall")}
</Button>
<Button onClick={handleDeselectAll}>
{t("general.actions.deselectall")}
</Button>
</Space>
);
}

View File

@@ -26,7 +26,6 @@ export function JobsExportAllButton({
disabled, disabled,
loadingCallback, loadingCallback,
completedCallback, completedCallback,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOBS); const [updateJob] = useMutation(UPDATE_JOBS);
@@ -40,7 +39,6 @@ export function JobsExportAllButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, { PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: jobIds, jobIds: jobIds,
elgen: true,
}); });
} else { } else {
let QbXmlResponse; let QbXmlResponse;
@@ -85,7 +83,6 @@ export function JobsExportAllButton({
return; return;
} }
} }
console.log("PartnerResponse", PartnerResponse); console.log("PartnerResponse", PartnerResponse);
const groupedData = _.groupBy( const groupedData = _.groupBy(
PartnerResponse.data, PartnerResponse.data,
@@ -109,70 +106,61 @@ export function JobsExportAllButton({
}); });
//Call is not awaited as it is not critical to finish before proceeding. //Call is not awaited as it is not critical to finish before proceeding.
}); });
await insertExportLog({
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { variables: {
//QBO Logs are handled server side. logs: [
await insertExportLog({ {
variables: { bodyshopid: bodyshop.id,
logs: [ jobid: key,
{ successful: false,
bodyshopid: bodyshop.id, message: JSON.stringify(
jobid: key, failedTransactions.map((ft) => ft.errorMessage)
successful: false, ),
message: JSON.stringify( useremail: currentUser.email,
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const jobUpdateResponse = await updateJob({
variables: {
jobIds: [key],
fields: {
status:
bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
}, },
}, ],
}); },
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
if (!jobUpdateResponse.errors) { const jobUpdateResponse = await updateJob({
notification.open({ variables: {
type: "success", jobIds: [key],
key: "jobsuccessexport", fields: {
message: t("jobs.successes.exported"), status: bodyshop.md_ro_statuses.default_exported || "Exported*",
}); date_exported: new Date(),
} else { },
notification["error"]({ },
message: t("jobs.errors.exporting", { });
error: JSON.stringify(jobUpdateResponse.error),
}), if (!jobUpdateResponse.errors) {
}); notification.open({
} type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
});
} }
} }
}) })
); );
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (!!completedCallback) completedCallback([]); if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);

View File

@@ -61,9 +61,6 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
jobId={jobId} jobId={jobId}
loading={loading} loading={loading}
data={data ? data.jobs_by_pk.notes : null} data={data ? data.jobs_by_pk.notes : null}
relatedRos={
data ? data.jobs_by_pk.vehicle && data.jobs_by_pk.vehicle.jobs : null
}
refetch={refetch} refetch={refetch}
deleteLoading={deleteLoading} deleteLoading={deleteLoading}
handleNoteDelete={handleNoteDelete} handleNoteDelete={handleNoteDelete}

View File

@@ -37,7 +37,6 @@ export function JobNotesComponent({
setNoteUpsertContext, setNoteUpsertContext,
deleteLoading, deleteLoading,
ro_number, ro_number,
relatedRos,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const Templates = TemplateList("job_special", { const Templates = TemplateList("job_special", {
@@ -150,7 +149,6 @@ export function JobNotesComponent({
actions: { refetch: refetch }, actions: { refetch: refetch },
context: { context: {
jobId: jobId, jobId: jobId,
relatedRos: relatedRos,
}, },
}); });
}} }}

View File

@@ -1,355 +0,0 @@
import {
ExclamationCircleFilled,
PauseCircleOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table } from "antd";
import queryString from "query-string";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsReadyList({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const readyStatuses = useMemo(() => {
if (bodyshop.md_ro_statuses.ready_statuses)
return bodyshop.md_ro_statuses.ready_statuses;
return bodyshop.md_ro_statuses.post_production_statuses.filter(
(s) =>
s !== bodyshop.md_ro_statuses.default_invoiced &&
s !== bodyshop.md_ro_statuses.default_exported
);
}, [bodyshop.md_ro_statuses]);
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: readyStatuses,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const { t } = useTranslation();
const history = useHistory();
const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />;
const jobs = data
? searchText === ""
? data.jobs
: data.jobs.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.comments || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
history.push({
search: queryString.stringify({
...searchParams,
selected: record.id,
}),
});
}
}
};
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.id}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.owner ? (
<Link
to={"/manage/owners/" + record.owner.id}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
</span>
);
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-ready")}
extra={
<Space wrap>
<span>({readyStatuses && readyStatuses.join(", ")})</span>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{ defaultPageSize: 50 }}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
},
};
}}
/>
</Card>
);
}
export default connect(mapStateToProps, null)(JobsReadyList);

View File

@@ -1,92 +1,51 @@
import { Checkbox, Col, Form, Input, Row, Space, Switch, Tag } from "antd"; import { Col, Form, Input, Row, Switch } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectNoteUpsert } from "../../redux/modals/modals.selectors";
import NotesPresetButton from "../notes-preset-button/notes-preset-button.component"; import NotesPresetButton from "../notes-preset-button/notes-preset-button.component";
const mapStateToProps = createStructuredSelector({ export default function NoteUpsertModalComponent({ form }) {
noteUpsertModal: selectNoteUpsert,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(NoteUpsertModalComponent);
export function NoteUpsertModalComponent({ form, noteUpsertModal }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { jobId, existingNote, relatedRos } = noteUpsertModal.context;
const filteredRelatedRos = relatedRos
? relatedRos.filter((j) => j.id !== jobId)
: [];
return ( return (
<> <Row gutter={[16, 16]}>
<Row gutter={[16, 16]}> <Col span={8}>
<Col span={8}> <Form.Item
<Form.Item label={t("notes.fields.critical")}
label={t("notes.fields.critical")} name="critical"
name="critical" valuePropName="checked"
valuePropName="checked" >
> <Switch />
<Switch /> </Form.Item>
</Form.Item> </Col>
</Col> <Col span={8}>
<Col span={8}> <Form.Item
<Form.Item label={t("notes.fields.private")}
label={t("notes.fields.private")} name="private"
name="private" valuePropName="checked"
valuePropName="checked" >
> <Switch />
<Switch /> </Form.Item>
</Form.Item> </Col>
</Col> <Col span={8}>
<Col span={8}> <NotesPresetButton form={form} />
<NotesPresetButton form={form} /> </Col>
</Col> <Col span={24}>
<Col span={24}> <Form.Item
<Form.Item label={t("notes.fields.text")}
label={t("notes.fields.text")} name="text"
name="text" rules={[
rules={[ {
{ required: true,
required: true, //message: t("general.validation.required"),
//message: t("general.validation.required"), },
}, ]}
]} >
> <Input.TextArea
<Input.TextArea rows={8}
rows={8} placeholder={t("notes.labels.newnoteplaceholder")}
placeholder={t("notes.labels.newnoteplaceholder")} />
/> </Form.Item>
</Form.Item> </Col>
</Col> </Row>
</Row>
<div>
<div>{!existingNote && t("notes.labels.addtorelatedro")}</div>
{!existingNote &&
filteredRelatedRos.map((j, idx) => (
<Space key={j.id} align="center">
<Form.Item
noStyle
name={["relatedros", j.id]}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
<Tag>
{`${j.ro_number || "N/A"}${j.clm_no ? ` | ${j.clm_no}` : ""}${
j.status ? ` | ${j.status}` : ""
}`}
</Tag>
</Space>
))}
</div>
</>
); );
} }

View File

@@ -4,14 +4,14 @@ import React, { useEffect } 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 { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_NOTE, UPDATE_NOTE } from "../../graphql/notes.queries"; import { INSERT_NEW_NOTE, UPDATE_NOTE } from "../../graphql/notes.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectNoteUpsert } from "../../redux/modals/modals.selectors"; import { selectNoteUpsert } from "../../redux/modals/modals.selectors";
import { selectCurrentUser } from "../../redux/user/user.selectors"; import { selectCurrentUser } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import NoteUpsertModalComponent from "./note-upsert-modal.component"; import NoteUpsertModalComponent from "./note-upsert-modal.component";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -48,9 +48,7 @@ export function NoteUpsertModalContainer({
} }
}, [existingNote, form, visible]); }, [existingNote, form, visible]);
const handleFinish = async (formValues) => { const handleFinish = (values) => {
const { relatedros, ...values } = formValues;
if (existingNote) { if (existingNote) {
logImEXEvent("job_note_update"); logImEXEvent("job_note_update");
@@ -72,44 +70,24 @@ export function NoteUpsertModalContainer({
toggleModalVisible(); toggleModalVisible();
} else { } else {
logImEXEvent("job_note_insert"); logImEXEvent("job_note_insert");
const AdditionalNoteInserts = relatedros
? Object.keys(relatedros).filter((key) => relatedros[key])
: [];
await insertNote({ insertNote({
variables: { variables: {
noteInput: [ noteInput: [
{ ...values, jobid: jobId, created_by: currentUser.email }, { ...values, jobid: jobId, created_by: currentUser.email },
], ],
}, },
}); }).then((r) => {
if (refetch) refetch();
if (AdditionalNoteInserts.length > 0) { form.resetFields();
//Insert the others. toggleModalVisible();
AdditionalNoteInserts.forEach(async (newJobId) => { notification["success"]({
await insertNote({ message: t("notes.successes.create"),
variables: { });
noteInput: [ insertAuditTrail({
{ ...values, jobid: newJobId, created_by: currentUser.email }, jobid: context.jobId,
], operation: AuditTrailMapping.jobnoteadded(),
},
});
insertAuditTrail({
jobid: newJobId,
operation: AuditTrailMapping.jobnoteadded(),
});
}); });
}
if (refetch) refetch();
form.resetFields();
toggleModalVisible();
notification["success"]({
message: t("notes.successes.create"),
});
insertAuditTrail({
jobid: context.jobId,
operation: AuditTrailMapping.jobnoteadded(),
}); });
} }
}; };

View File

@@ -22,17 +22,9 @@ export function NotesPresetButton({ bodyshop, form }) {
}; };
const menu = ( const menu = (
<Menu <Menu>
style={{
columnCount: Math.floor(bodyshop.md_notes_presets.length / 10) + 1,
}}
>
{bodyshop.md_notes_presets.map((i, idx) => ( {bodyshop.md_notes_presets.map((i, idx) => (
<Menu.Item <Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
onClick={() => handleSelect(i)}
key={idx}
style={{ breakInside: "avoid" }}
>
{i.label} {i.label}
</Menu.Item> </Menu.Item>
))} ))}

View File

@@ -1,66 +0,0 @@
import { useMutation } from "@apollo/client";
import { Checkbox, notification, Space, Spin } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { MUTATION_UPDATE_PO_CM_REECEIVED } from "../../graphql/parts-orders.queries";
export default function PartsOrderCmReceived({
checked,
orderLineId,
partsOrderId,
}) {
const [updateLine] = useMutation(MUTATION_UPDATE_PO_CM_REECEIVED);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleChange = async (e) => {
setLoading(true);
const result = await updateLine({
variables: {
partsLineId: orderLineId,
partsOrder: { cm_received: e.target.checked },
},
update(cache) {
cache.modify({
id: cache.identify({
id: partsOrderId,
__typename: "parts_orders",
}),
fields: {
parts_order_lines(ex, { readField }) {
console.log(ex);
return ex.map((lineref) => {
if (orderLineId.id !== readField("id", lineref)) {
lineref.cm_received = e.target.checked;
}
return lineref;
});
},
},
});
},
});
if (!!!result.errors) {
notification["success"]({
message: t("parts_orders.successes.line_updated"),
});
} else {
notification["error"]({
message: t("parts_orders.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
return (
<Space>
<Checkbox checked={checked} onChange={handleChange} />
{loading && <Spin size="small" />}
</Space>
);
}

View File

@@ -1,47 +0,0 @@
import React from "react";
import { Button, Popconfirm } from "antd";
import { DeleteFilled } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { DELETE_PARTS_ORDER_LINE } from "../../graphql/parts-orders.queries";
import { useMutation } from "@apollo/client";
export default function PartsOrderDeleteLine({
disabled,
partsLineId,
partsOrderId,
}) {
const { t } = useTranslation();
const [deletePartsOrderLine] = useMutation(DELETE_PARTS_ORDER_LINE);
return (
<Popconfirm
title={t("parts_orders.labels.confirmdelete")}
disabled={disabled}
onConfirm={async () => {
//Delete the parts return.!
await deletePartsOrderLine({
variables: { partsOrderLineId: partsLineId },
update(cache) {
cache.modify({
id: cache.identify({
__typename: "parts_orders",
id: partsOrderId,
}),
fields: {
parts_order_lines(cached, { readField }) {
return cached.filter((c) => {
return readField("id", c) !== partsLineId;
});
},
},
});
},
});
}}
>
<Button disabled={disabled}>
<DeleteFilled />
</Button>
</Popconfirm>
);
}

View File

@@ -29,8 +29,6 @@ import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import DataLabel from "../data-label/data-label.component"; import DataLabel from "../data-label/data-label.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component"; import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component"; import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container"; import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component"; import PrintWrapper from "../print-wrapper/print-wrapper.component";
@@ -79,7 +77,6 @@ export function PartsOrderListTableComponent({
}); });
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const selectedpartsorder = search.partsorderid; const selectedpartsorder = search.partsorderid;
const [searchText, setSearchText] = useState("");
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER); const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
@@ -349,23 +346,6 @@ export function PartsOrderListTableComponent({
dataIndex: "status", dataIndex: "status",
key: "status", key: "status",
}, },
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cm_received"),
dataIndex: "cm_received",
key: "cm_received",
render: (text, record) => (
<PartsOrderCmReceived
orderLineId={record.id}
checked={record.cm_received}
partsorderid={selectedPartsOrderRecord.id}
/>
),
},
]
: []),
{ {
title: t("parts_orders.fields.backordered_on"), title: t("parts_orders.fields.backordered_on"),
dataIndex: "backordered_on", dataIndex: "backordered_on",
@@ -392,21 +372,12 @@ export function PartsOrderListTableComponent({
dataIndex: "actions", dataIndex: "actions",
key: "actions", key: "actions",
render: (text, record) => ( render: (text, record) => (
<Space wrap> <PartsOrderLineBackorderButton
<PartsOrderDeleteLine disabled={jobRO}
disabled={jobRO} partsOrderStatus={record.status}
partsOrderStatus={record.status} partsLineId={record.id}
partsLineId={record.id} jobLineId={record.job_line_id}
partsOrderId={selectedpartsorder} />
jobLineId={record.job_line_id}
/>
<PartsOrderLineBackorderButton
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
</Space>
), ),
}, },
]; ];
@@ -432,21 +403,6 @@ export function PartsOrderListTableComponent({
); );
}; };
const filteredPartsOrders = parts_orders
? searchText === ""
? parts_orders
: parts_orders.filter(
(b) =>
(b.order_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.vendor.name || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
return ( return (
<Card <Card
title={t("parts_orders.labels.parts_orders")} title={t("parts_orders.labels.parts_orders")}
@@ -457,10 +413,8 @@ export function PartsOrderListTableComponent({
</Button> </Button>
<Input.Search <Input.Search
placeholder={t("general.labels.search")} placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => { onChange={(e) => {
e.preventDefault(); e.preventDefault();
setSearchText(e.target.value);
}} }}
/> />
</Space> </Space>
@@ -484,7 +438,7 @@ export function PartsOrderListTableComponent({
}} }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={filteredPartsOrders} dataSource={parts_orders}
onChange={handleTableChange} onChange={handleTableChange}
/> />
</Card> </Card>

View File

@@ -11,7 +11,6 @@ import {
Select, Select,
Menu, Menu,
Dropdown, Dropdown,
Checkbox,
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -115,15 +114,6 @@ export function PartsOrderModalComponent({
</Space> </Space>
</Tag> </Tag>
)} )}
{!isReturn && (
<Form.Item
name="removefrompartsqueue"
label={t("parts_orders.labels.removefrompartsqueue")}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
)}
</LayoutFormRow> </LayoutFormRow>
<Divider orientation="left"> <Divider orientation="left">
{t("parts_orders.labels.inthisorder")} {t("parts_orders.labels.inthisorder")}
@@ -290,7 +280,6 @@ export function PartsOrderModalComponent({
> >
<Input.TextArea rows={3} /> <Input.TextArea rows={3} />
</Form.Item> </Form.Item>
<Radio.Group <Radio.Group
defaultValue={sendType} defaultValue={sendType}
onChange={(e) => setSendType(e.target.value)} onChange={(e) => setSendType(e.target.value)}

View File

@@ -32,7 +32,6 @@ import PartsOrderModalComponent from "./parts-order-modal.component";
import axios from "axios"; import axios from "axios";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash"; import _ from "lodash";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -91,9 +90,8 @@ export function PartsOrderModalContainer({
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS); const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS); const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async ({ removefrompartsqueue, ...values }) => { const handleFinish = async (values) => {
logImEXEvent("parts_order_insert"); logImEXEvent("parts_order_insert");
setSaving(true); setSaving(true);
const insertResult = await insertPartOrder({ const insertResult = await insertPartOrder({
@@ -130,17 +128,6 @@ export function PartsOrderModalContainer({
}, },
}); });
if (!isReturn && removefrompartsqueue) {
await updateJob({
variables: {
jobId: jobId,
job: {
queued_for_parts: false,
},
},
});
}
insertAuditTrail({ insertAuditTrail({
jobid: jobId, jobid: jobId,
operation: isReturn operation: isReturn
@@ -318,7 +305,6 @@ export function PartsOrderModalContainer({
quantity: value.part_qty, quantity: value.part_qty,
job_line_id: isReturn ? value.joblineid : value.id, job_line_id: isReturn ? value.joblineid : value.id,
part_type: value.part_type, part_type: value.part_type,
...(isReturn && { cm_received: false }),
}); });
return acc; return acc;
}, []) }, [])

View File

@@ -27,7 +27,6 @@ export function PayableExportAll({
disabled, disabled,
loadingCallback, loadingCallback,
completedCallback, completedCallback,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS); const [updateBill] = useMutation(UPDATE_BILLS);
@@ -43,7 +42,6 @@ export function PayableExportAll({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, { PartnerResponse = await axios.post(`/qbo/payables`, {
bills: billids, bills: billids,
elgen: true,
}); });
} else { } else {
let QbXmlResponse; let QbXmlResponse;
@@ -106,62 +104,57 @@ export function PayableExportAll({
}), }),
}) })
); );
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({ await insertExportLog({
variables: { variables: {
billIdList: [key], logs: [
bill: { {
exported: true, bodyshopid: bodyshop.id,
exported_at: new Date(), billid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
}, },
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: [key],
bill: {
exported: true,
exported_at: new Date(),
}, },
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
}); });
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
});
}
} }
} }
})() })()
@@ -171,8 +164,6 @@ export function PayableExportAll({
await Promise.all(proms); await Promise.all(proms);
if (!!completedCallback) completedCallback([]); if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false); setLoading(false);
}; };

View File

@@ -26,7 +26,6 @@ export function PayableExportButton({
disabled, disabled,
loadingCallback, loadingCallback,
setSelectedBills, setSelectedBills,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS); const [updateBill] = useMutation(UPDATE_BILLS);
@@ -44,7 +43,6 @@ export function PayableExportButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, { PartnerResponse = await axios.post(`/qbo/payables`, {
bills: [billId], bills: [billId],
elgen: true,
}); });
} else { } else {
//Default is QBD //Default is QBD
@@ -102,72 +100,64 @@ export function PayableExportButton({
}), }),
}) })
); );
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { await insertExportLog({
//QBO Logs are handled server side. variables: {
await insertExportLog({ logs: [
variables: { {
logs: [ bodyshopid: bodyshop.id,
{ billid: billId,
bodyshopid: bodyshop.id, successful: false,
billid: billId, message: JSON.stringify(
successful: false, failedTransactions.map((ft) => ft.errorMessage)
message: JSON.stringify( ),
failedTransactions.map((ft) => ft.errorMessage) useremail: currentUser.email,
), },
useremail: currentUser.email, ],
}, },
], });
},
});
}
} }
if (successfulTransactions.length > 0) { if (successfulTransactions.length > 0) {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { await insertExportLog({
//QBO Logs are handled server side. variables: {
await insertExportLog({ logs: [
variables: { {
logs: [ bodyshopid: bodyshop.id,
{ billid: billId,
bodyshopid: bodyshop.id, successful: true,
billid: billId, useremail: currentUser.email,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
),
bill: {
exported: true,
exported_at: new Date(),
}, },
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
),
bill: {
exported: true,
exported_at: new Date(),
}, },
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
}); });
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
});
}
} }
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (setSelectedBills) { if (setSelectedBills) {
setSelectedBills((selectedBills) => { setSelectedBills((selectedBills) => {
return selectedBills.filter((i) => i !== billId); return selectedBills.filter((i) => i !== billId);

View File

@@ -26,7 +26,6 @@ export function PaymentExportButton({
disabled, disabled,
loadingCallback, loadingCallback,
setSelectedPayments, setSelectedPayments,
refetch,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updatePayment] = useMutation(UPDATE_PAYMENTS); const [updatePayment] = useMutation(UPDATE_PAYMENTS);
@@ -41,7 +40,6 @@ export function PaymentExportButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, { PartnerResponse = await axios.post(`/qbo/payments`, {
payments: [paymentId], payments: [paymentId],
elgen: true,
}); });
} else { } else {
//Default is QBD //Default is QBD
@@ -102,68 +100,63 @@ export function PaymentExportButton({
}), }),
}) })
); );
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({ await insertExportLog({
variables: { variables: {
paymentIdList: successfulTransactions.map( logs: [
(st) => {
st[ bodyshopid: bodyshop.id,
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo paymentid: paymentId,
? "paymentid" successful: false,
: "id" message: JSON.stringify(
] failedTransactions.map((ft) => ft.errorMessage)
), ),
payment: { useremail: currentUser.email,
exportedat: new Date(),
}, },
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
),
payment: {
exportedat: new Date(),
}, },
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
}); });
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
} }
if (setSelectedPayments) { if (setSelectedPayments) {
@@ -172,7 +165,7 @@ export function PaymentExportButton({
}); });
} }
} }
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };

View File

@@ -25,7 +25,6 @@ export function PaymentsExportAllButton({
disabled, disabled,
loadingCallback, loadingCallback,
completedCallback, completedCallback,
refetch
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updatePayments] = useMutation(UPDATE_PAYMENTS); const [updatePayments] = useMutation(UPDATE_PAYMENTS);
@@ -39,7 +38,6 @@ export function PaymentsExportAllButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, { PartnerResponse = await axios.post(`/qbo/payments`, {
payments: paymentIds, payments: paymentIds,
elgen: true,
}); });
} else { } else {
let QbXmlResponse; let QbXmlResponse;
@@ -94,61 +92,54 @@ export function PaymentsExportAllButton({
}), }),
}) })
); );
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) { await insertExportLog({
//QBO Logs are handled server side. variables: {
await insertExportLog({ logs: [
variables: { {
logs: [ bodyshopid: bodyshop.id,
{ paymentid: key,
bodyshopid: bodyshop.id, successful: false,
paymentid: key, message: JSON.stringify(
successful: false, failedTransactions.map((ft) => ft.errorMessage)
message: JSON.stringify( ),
failedTransactions.map((ft) => ft.errorMessage) useremail: currentUser.email,
),
useremail: currentUser.email,
},
],
},
});
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
payment: {
exportedat: new Date(),
}, },
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
payment: {
exportedat: new Date(),
}, },
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
}); });
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
} }
} }
})() })()
@@ -157,7 +148,6 @@ export function PaymentsExportAllButton({
await Promise.all(proms); await Promise.all(proms);
if (!!completedCallback) completedCallback([]); if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false); setLoading(false);
}; };

View File

@@ -1,7 +1,6 @@
import { import {
CalendarOutlined, CalendarOutlined,
EyeFilled, EyeFilled,
DownloadOutlined,
PauseCircleOutlined, PauseCircleOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Card, Col, Row, Space } from "antd"; import { Card, Col, Row, Space } from "antd";
@@ -15,7 +14,6 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ
import "./production-board-card.styles.scss"; import "./production-board-card.styles.scss";
import moment from "moment"; import moment from "moment";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
export default function ProductionBoardCard( export default function ProductionBoardCard(
technician, technician,
@@ -159,16 +157,6 @@ export default function ProductionBoardCard(
</Row> </Row>
</Col> </Col>
)} */} )} */}
{cardSettings && cardSettings.actual_in && card.actual_in && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<Space>
<DownloadOutlined />
<DateTimeFormatter format="MM/DD">
{card.actual_in}
</DateTimeFormatter>
</Space>
</Col>
)}
{cardSettings && {cardSettings &&
cardSettings.scheduled_completion && cardSettings.scheduled_completion &&
card.scheduled_completion && ( card.scheduled_completion && (
@@ -200,11 +188,6 @@ export default function ProductionBoardCard(
)} )}
</Col> </Col>
)} )}
{cardSettings && cardSettings.partsstatus && (
<Col span={24}>
<JobPartsQueueCount parts={card.joblines_status} />
</Col>
)}
</Row> </Row>
</Card> </Card>
); );

View File

@@ -97,13 +97,6 @@ export default function ProductionBoardKanbanCardSettings({
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.actual_in")}
name="actual_in"
>
<Switch />
</Form.Item>
</Col> </Col>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -138,13 +131,6 @@ export default function ProductionBoardKanbanCardSettings({
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.partsstatus")}
name="partsstatus"
>
<Switch />
</Form.Item>
<Form.Item <Form.Item
valuePropName="checked" valuePropName="checked"
label={t("production.labels.stickyheader")} label={t("production.labels.stickyheader")}

View File

@@ -51,10 +51,7 @@ export function ProductionBoardKanbanComponent({
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
const boardData = createBoardData( const boardData = createBoardData(
[ bodyshop.md_ro_statuses.production_statuses,
...bodyshop.md_ro_statuses.production_statuses,
...(bodyshop.md_ro_statuses.additional_board_statuses || []),
],
data, data,
filter filter
); );
@@ -64,7 +61,13 @@ export function ProductionBoardKanbanComponent({
}); });
setBoardLanes(boardData); setBoardLanes(boardData);
setIsMoving(false); setIsMoving(false);
}, [data, setBoardLanes, setIsMoving, bodyshop.md_ro_statuses, filter]); }, [
data,
setBoardLanes,
setIsMoving,
bodyshop.md_ro_statuses.production_statuses,
filter,
]);
const client = useApolloClient(); const client = useApolloClient();

View File

@@ -23,7 +23,6 @@ import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.co
import ProductionListColumnComment from "./production-list-columns.comment.component"; import ProductionListColumnComment from "./production-list-columns.comment.component";
import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component"; import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
const r = ({ technician, state, activeStatuses, bodyshop }) => { const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [ return [
@@ -491,14 +490,6 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
<ProductionListColumnPartsReceived record={record} /> <ProductionListColumnPartsReceived record={record} />
), ),
}, },
{
title: i18n.t("jobs.fields.partsstatus"),
dataIndex: "partsstatus",
key: "partsstatus",
render: (text, record) => (
<JobPartsQueueCount parts={record.joblines_status} record={record} />
),
},
]; ];
}; };
export default r; export default r;

View File

@@ -21,11 +21,9 @@ import OwnerNameDisplay from "../owner-name-display/owner-name-display.component
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, //currentUser: selectCurrentUser
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) => setPrintCenterContext: (context) =>
@@ -36,11 +34,7 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(ProductionListDetail); )(ProductionListDetail);
export function ProductionListDetail({ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
bodyshop,
jobs,
setPrintCenterContext,
}) {
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const history = useHistory(); const history = useHistory();
const { selected } = search; const { selected } = search;
@@ -65,7 +59,7 @@ export function ProductionListDetail({
<PageHeader <PageHeader
title={theJob.ro_number} title={theJob.ro_number}
extra={ extra={
<Space wrap> <Space>
<ProductionRemoveButton jobId={theJob.id} />{" "} <ProductionRemoveButton jobId={theJob.id} />{" "}
<Button <Button
onClick={() => { onClick={() => {
@@ -82,7 +76,6 @@ export function ProductionListDetail({
<PrinterFilled /> <PrinterFilled />
{t("jobs.actions.printCenter")} {t("jobs.actions.printCenter")}
</Button> </Button>
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
</Space> </Space>
} }
/> />
@@ -149,12 +142,11 @@ export function ProductionListDetail({
loading={loading} loading={loading}
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
{!bodyshop.uselocalmediaserver && (
<JobDetailCardsDocumentsComponent <JobDetailCardsDocumentsComponent
loading={loading} loading={loading}
data={data ? data.jobs_by_pk : null} data={data ? data.jobs_by_pk : null}
/> />
)}
</div> </div>
)} )}
</Drawer> </Drawer>

View File

@@ -8,11 +8,8 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment"; import moment from "moment";
import { useApolloClient, useQuery } from "@apollo/client"; import { useApolloClient } from "@apollo/client";
import { import { GET_BLOCKED_DAYS } from "../../graphql/scoreboard.queries";
GET_BLOCKED_DAYS,
QUERY_SCOREBOARD,
} from "../../graphql/scoreboard.queries";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -25,15 +22,10 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(ScoreboardDisplayComponent); )(ScoreboardDisplayComponent);
export function ScoreboardDisplayComponent({ bodyshop }) { export function ScoreboardDisplayComponent({
const scoreboardSubscription = useQuery(QUERY_SCOREBOARD, { bodyshop,
variables: { scoreboardSubscription,
start: moment().startOf("month"), }) {
end: moment().endOf("month"),
},
pollInterval: 60000,
});
const { data } = scoreboardSubscription; const { data } = scoreboardSubscription;
const client = useApolloClient(); const client = useApolloClient();
const scoreBoardlist = (data && data.scoreboard) || []; const scoreBoardlist = (data && data.scoreboard) || [];

View File

@@ -1,42 +1,13 @@
import React, { useState } from "react"; import React from "react";
import { Dropdown, Button, Table, Space, Card, Input } from "antd"; import { Dropdown, Button, Table, Space } from "antd";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component"; import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component"; import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
export default function ScoreboardJobsList({ scoreBoardlist }) { export default function ScoreboardJobsList({ scoreBoardlist }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [searchText, setSearchText] = useState("");
const jobs = scoreBoardlist
? searchText === ""
? scoreBoardlist
: scoreBoardlist.filter(
(sb) =>
(sb.job.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const columns = [ const columns = [
{ {
@@ -49,25 +20,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
</Link> </Link>
), ),
}, },
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => (
<span>{`${record.job.v_model_yr || ""} ${
record.job.v_make_desc || ""
} ${record.job.v_model_desc || ""}`}</span>
),
},
{ {
title: t("scoreboard.fields.date"), title: t("scoreboard.fields.date"),
dataIndex: "date", dataIndex: "date",
@@ -98,29 +51,17 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
]; ];
const overlay = ( const overlay = (
<Card <div style={{ width: "50vw", padding: "1rem" }}>
style={{ maxWidth: "90vw", padding: "1rem" }}
onClick={(e) => e.stopPropagation()}
extra={
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
onClick={(e) => e.stopPropagation()}
/>
}
>
<Table <Table
pagination={false} pagination={false}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={scoreBoardlist}
scroll={{ x: true, y: "15rem" }}
style={{ padding: "1rem" }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
/> />
</Card> </div>
); );
return ( return (

View File

@@ -25,6 +25,10 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
const values = useMemo(() => { const values = useMemo(() => {
const dateHash = _.groupBy(scoreBoardlist, "date"); const dateHash = _.groupBy(scoreBoardlist, "date");
console.log(
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 31 ~ values ~ dateHash",
dateHash
);
let ret = { let ret = {
todayBody: 0, todayBody: 0,
@@ -67,6 +71,10 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
return ret; return ret;
}, [scoreBoardlist]); }, [scoreBoardlist]);
console.log(
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 51 ~ values ~ values",
values
);
return ( return (
<Card <Card

View File

@@ -328,12 +328,6 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("employees.fields.external_id")}
name="external_id"
>
<Input />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Form.List name={["rates"]}> <Form.List name={["rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {

View File

@@ -352,27 +352,10 @@ export default function ShopInfoGeneral({ form }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item
name={["accountingconfig", "ReceivableCustomField1"]}
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
>
{ReceivableCustomFieldSelect}
</Form.Item>
<Form.Item
name={["accountingconfig", "ReceivableCustomField2"]}
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
>
{ReceivableCustomFieldSelect}
</Form.Item>
<Form.Item
name={["accountingconfig", "ReceivableCustomField3"]}
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
>
{ReceivableCustomFieldSelect}
</Form.Item>
<Form.Item <Form.Item
name={["md_classes"]} name={["md_classes"]}
label={t("bodyshop.fields.md_classes")} label={t("bodyshop.fields.md_classes")}
dependencies={["enforce_class"]}
rules={[ rules={[
({ getFieldValue }) => { ({ getFieldValue }) => {
return { return {
@@ -601,31 +584,6 @@ export default function ShopInfoGeneral({ form }) {
> >
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item
name={["uselocalmediaserver"]}
label={t("bodyshop.fields.uselocalmediaserver")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["localmediaserverhttp"]}
label={t("bodyshop.fields.localmediaserverhttp")}
>
<Input />
</Form.Item>
<Form.Item
name={["localmediaservernetwork"]}
label={t("bodyshop.fields.localmediaservernetwork")}
>
<Input />
</Form.Item>
<Form.Item
name={["localmediatoken"]}
label={t("bodyshop.fields.localmediatoken")}
>
<Input />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}> <LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}> <Form.List name={["md_messaging_presets"]}>
@@ -724,7 +682,7 @@ export default function ShopInfoGeneral({ form }) {
}, },
]} ]}
> >
<Input.TextArea rows={3} /> <Input />
</Form.Item> </Form.Item>
<Space wrap> <Space wrap>
<DeleteFilled <DeleteFilled
@@ -1435,68 +1393,6 @@ export default function ShopInfoGeneral({ form }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.md_to_emails")}>
<Form.List name={["md_to_emails"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("general.labels.label")}
key={`${index}label`}
name={[field.name, "label"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.md_to_emails_emails")}
key={`${index}emails`}
name={[field.name, "emails"]}
>
<Select mode="tags" tokenSeparators={[",", ";"]} />
</Form.Item>
<Space>
<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("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
</div> </div>
); );
} }
const ReceivableCustomFieldSelect = (
<Select>
<Select.Option value="v_vin">VIN</Select.Option>
<Select.Option value="clm_no">Claim No.</Select.Option>
<Select.Option value="ded_amt">Deductible Amount</Select.Option>
</Select>
);

View File

@@ -165,18 +165,6 @@ export default function ShopInfoRbacComponent({ form }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-ready")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-ready"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.admin")} label={t("bodyshop.fields.rbac.jobs.admin")}
rules={[ rules={[

View File

@@ -134,44 +134,6 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
))} ))}
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item
name={["md_ro_statuses", "ready_statuses"]}
label={t("bodyshop.fields.statuses.ready_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "additional_board_statuses"]}
label={t("bodyshop.fields.statuses.additional_board_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<LayoutFormRow noDivider> <LayoutFormRow noDivider>
<Form.Item <Form.Item
label={t("bodyshop.fields.statuses.default_scheduled")} label={t("bodyshop.fields.statuses.default_scheduled")}

View File

@@ -6,20 +6,15 @@ import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries"; import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import JobLinesContainer from "../job-detail-lines/job-lines.container"; import JobLinesContainer from "../job-detail-lines/job-lines.container";
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component"; import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
import JobsDocumentsGalleryContainer from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobsDocumentsGalleryContainer from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import JobNotesContainer from "../jobs-notes/jobs-notes.container"; import JobNotesContainer from "../jobs-notes/jobs-notes.container";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) => setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })), dispatch(setModalContext({ context: context, modal: "printCenter" })),
@@ -34,7 +29,7 @@ const mapDispatchToProps = (dispatch) => ({
// }, // },
// }; // };
export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) { export function JobDetailCards({ setPrintCenterContext }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
@@ -115,13 +110,7 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) {
/> />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane key="documents" tab={t("jobs.labels.documents")}> <Tabs.TabPane key="documents" tab={t("jobs.labels.documents")}>
{bodyshop.uselocalmediaserver ? ( <JobsDocumentsGalleryContainer jobId={searchParams.selected} />
<JobsDocumentsLocalGallery
job={data ? data.jobs_by_pk : null}
/>
) : (
<JobsDocumentsGalleryContainer jobId={searchParams.selected} />
)}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane key="notes" tab={t("jobs.labels.notes")}> <Tabs.TabPane key="notes" tab={t("jobs.labels.notes")}>
<JobNotesContainer jobId={searchParams.selected} /> <JobNotesContainer jobId={searchParams.selected} />
@@ -132,7 +121,4 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) {
</Drawer> </Drawer>
); );
} }
export default connect( export default connect(null, mapDispatchToProps)(JobDetailCards);
mapStateToProps,
mapDispatchToProps
)(TechLookupJobsDrawer);

View File

@@ -237,11 +237,7 @@ export function TimeTicketModalComponent({
return Promise.reject( return Promise.reject(
t("timetickets.validation.clockoffwithoutclockon") t("timetickets.validation.clockoffwithoutclockon")
); );
if ( if (value && !value.isSameOrAfter(clockon))
value &&
value.isSameOrAfter &&
!value.isSameOrAfter(clockon)
)
return Promise.reject( return Promise.reject(
t("timetickets.validation.clockoffmustbeafterclockon") t("timetickets.validation.clockoffmustbeafterclockon")
); );

View File

@@ -1,42 +0,0 @@
import { Button } from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import { useLocation } from "react-router-dom";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next";
import moment from "moment";
const AttendanceCsv = TemplateList("special").attendance_detail_csv;
export default function TimeTicketsAttendanceTable() {
const searchParams = queryString.parse(useLocation().search);
const { start, end } = searchParams;
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await GenerateDocument(
{
name: AttendanceCsv.key,
variables: {
start: start
? start
: moment().startOf("week").subtract(7, "days").format("YYYY-MM-DD"),
end: end ? end : moment().endOf("week").format("YYYY-MM-DD"),
},
},
{},
"text"
);
setLoading(false);
};
return (
<Button loading={loading} onClick={handleClick}>
{t("printcenter.special.attendance_detail_csv")}
</Button>
);
}

View File

@@ -139,9 +139,6 @@ export default function VehicleDetailFormComponent({ form, loading }) {
<Input /> <Input />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<Form.Item label={t("vehicles.fields.notes")} name="notes">
<Input.TextArea rows={4} />
</Form.Item>
</div> </div>
); );
} }

View File

@@ -86,7 +86,6 @@ export const QUERY_BILLS_BY_JOBID = gql`
job_line_id job_line_id
part_type part_type
cost cost
cm_received
jobline { jobline {
id id
part_type part_type
@@ -125,7 +124,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
applicable_taxes applicable_taxes
deductedfromlbr deductedfromlbr
lbr_adjustment lbr_adjustment
jobline { jobline{
oem_partno oem_partno
part_type part_type
} }
@@ -165,7 +164,7 @@ export const QUERY_BILL_BY_PK = gql`
cost_center cost_center
quantity quantity
joblineid joblineid
jobline { jobline{
oem_partno oem_partno
part_type part_type
} }

View File

@@ -106,11 +106,6 @@ export const QUERY_BODYSHOP = gql`
last_name_first last_name_first
md_parts_order_comment md_parts_order_comment
bill_allow_post_to_closed bill_allow_post_to_closed
md_to_emails
uselocalmediaserver
localmediaserverhttp
localmediaservernetwork
localmediatoken
employees { employees {
user_email user_email
id id
@@ -119,7 +114,6 @@ export const QUERY_BODYSHOP = gql`
last_name last_name
employee_number employee_number
rates rates
external_id
} }
} }
} }
@@ -215,11 +209,6 @@ export const UPDATE_SHOP = gql`
last_name_first last_name_first
md_parts_order_comment md_parts_order_comment
bill_allow_post_to_closed bill_allow_post_to_closed
md_to_emails
uselocalmediaserver
localmediaserverhttp
localmediaservernetwork
localmediatoken
employees { employees {
id id
first_name first_name
@@ -228,7 +217,6 @@ export const UPDATE_SHOP = gql`
employee_number employee_number
rates rates
user_email user_email
external_id
} }
} }
} }

View File

@@ -86,7 +86,6 @@ export const QUERY_ALL_CC = gql`
limit: 1 limit: 1
) { ) {
id id
scheduledreturn
job { job {
id id
ro_number ro_number

View File

@@ -25,7 +25,6 @@ export const QUERY_EMPLOYEE_BY_ID = gql`
rates rates
pin pin
user_email user_email
external_id
employee_vacations(order_by: { start: desc }) { employee_vacations(order_by: { start: desc }) {
id id
start start

View File

@@ -50,13 +50,25 @@ export const QUERY_PARTS_QUEUE = gql`
$limit: Int $limit: Int
$order: [jobs_order_by!] $order: [jobs_order_by!]
) { ) {
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) { jobs_aggregate(
where: {
_and: [
{ status: { _in: $statuses } }
{ queued_for_parts: { _eq: true } }
]
}
) {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
} }
} }
jobs( jobs(
where: { _and: [{ status: { _in: $statuses } }] } where: {
_and: [
{ status: { _in: $statuses } }
{ queued_for_parts: { _eq: true } }
]
}
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: $order
@@ -75,20 +87,18 @@ export const QUERY_PARTS_QUEUE = gql`
v_make_desc v_make_desc
v_color v_color
vehicleid vehicleid
scheduled_in actual_completion
actual_delivery
actual_in
id id
clm_no clm_no
clm_total
owner_owing
ro_number ro_number
status status
updated_at updated_at
vehicleid vehicleid
ownerid ownerid
queued_for_parts
joblines_status {
count
part_type
status
}
} }
} }
`; `;
@@ -530,8 +540,6 @@ export const GET_JOB_BY_PK = gql`
v_model_desc v_model_desc
v_make_desc v_make_desc
v_color v_color
notes
v_paint_codes
jobs { jobs {
id id
ro_number ro_number
@@ -843,11 +851,6 @@ export const QUERY_JOB_CARD_DETAILS = gql`
count count
status status
} }
joblines {
id
mod_lbr_ty
mod_lb_hrs
}
owner { owner {
id id
allow_text_message allow_text_message
@@ -868,7 +871,6 @@ export const QUERY_JOB_CARD_DETAILS = gql`
v_model_desc v_model_desc
v_color v_color
plate_no plate_no
notes
jobs { jobs {
id id
clm_no clm_no
@@ -1048,7 +1050,6 @@ export const UPDATE_JOB = gql`
production_vars production_vars
lbr_adjustments lbr_adjustments
suspended suspended
queued_for_parts
} }
} }
} }

View File

@@ -15,14 +15,6 @@ export const QUERY_NOTES_BY_JOB_PK = gql`
jobs_by_pk(id: $id) { jobs_by_pk(id: $id) {
id id
ro_number ro_number
vehicle{
jobs{
id
ro_number
status
clm_no
}
}
notes { notes {
created_at created_at
created_by created_by

View File

@@ -72,7 +72,7 @@ export const QUERY_PARTS_ORDER_OEC = gql`
part_type part_type
} }
job { job {
bodyshop { bodyshop{
shopname shopname
bill_tax_rates bill_tax_rates
} }
@@ -292,30 +292,6 @@ export const DELETE_PARTS_ORDER = gql`
} }
`; `;
export const DELETE_PARTS_ORDER_LINE = gql`
mutation DELETE_PARTS_ORDER_LINE($partsOrderLineId: uuid!) {
delete_parts_order_lines_by_pk(id: $partsOrderLineId) {
id
}
}
`;
export const MUTATION_UPDATE_PO_CM_REECEIVED = gql`
mutation MUTATION_UPDATE_PO_CM_REECEIVED(
$partsLineId: uuid!
$partsOrder: parts_order_lines_set_input
) {
update_parts_order_lines(
where: { id: { _eq: $partsLineId } }
_set: $partsOrder
) {
returning {
id
cm_received
}
}
}
`;
export const MUTATION_UPDATE_BO_ETA = gql` export const MUTATION_UPDATE_BO_ETA = gql`
mutation MUTATION_UPDATE_BO_ETA( mutation MUTATION_UPDATE_BO_ETA(
$partsLineId: uuid! $partsLineId: uuid!
@@ -363,36 +339,3 @@ export const MUTATION_BACKORDER_PART_LINE = gql`
} }
} }
`; `;
export const QUERY_UNRECEIVED_LINES = gql`
query QUERY_UNRECEIVED_LINES($jobId: uuid!, $vendorId: uuid!) {
parts_order_lines(
where: {
parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId } }
cm_received: { _neq: true }
}
) {
cm_received
id
line_desc
quantity
act_price
cost
oem_partno
}
}
`;
export const MUTATION_MARK_RETURN_RECEIVED = gql`
mutation MUTATION_MARK_RETURN_RECEIVED($partsLineIds: [uuid!]!) {
update_parts_order_lines(
where: { id: { _in: $partsLineIds } }
_set: { cm_received: true }
) {
returning {
id
cm_received
}
}
}
`;

View File

@@ -1,7 +1,7 @@
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
export const QUERY_SCOREBOARD = gql` export const SUBSCRIPTION_SCOREBOARD = gql`
query QUERY_SCOREBOARD($start: date!, $end: date!) { subscription SUBSCRIPTION_SCOREBOARD($start: date!, $end: date!) {
scoreboard( scoreboard(
where: { _and: { date: { _gte: $start, _lte: $end } } } where: { _and: { date: { _gte: $start, _lte: $end } } }
order_by: { date: asc } order_by: { date: asc }
@@ -13,12 +13,6 @@ export const QUERY_SCOREBOARD = gql`
job { job {
id id
ro_number ro_number
ownr_fn
ownr_ln
ownr_co_nm
v_make_desc
v_model_desc
v_model_yr
} }
} }
} }

View File

@@ -27,7 +27,6 @@ export const QUERY_VEHICLE_BY_ID = gql`
v_bstyle v_bstyle
updated_at updated_at
trim_color trim_color
notes
jobs { jobs {
id id
ro_number ro_number

View File

@@ -3,7 +3,7 @@ import * as Sentry from "@sentry/react";
import "antd/dist/antd.less"; import "antd/dist/antd.less";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import { createRoot } from "react-dom/client";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import { PersistGate } from "redux-persist/integration/react"; import { PersistGate } from "redux-persist/integration/react";
@@ -39,7 +39,9 @@ if (process.env.NODE_ENV !== "development") {
}); });
} }
ReactDOM.render( const container = document.getElementById("root");
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(
<Provider store={store}> <Provider store={store}>
<BrowserRouter> <BrowserRouter>
<PersistGate <PersistGate
@@ -49,48 +51,7 @@ ReactDOM.render(
<AppContainer /> <AppContainer />
</PersistGate> </PersistGate>
</BrowserRouter> </BrowserRouter>
</Provider>, </Provider>
document.getElementById("root")
); );
// const onServiceWorkerUpdate = (registration) => {
// console.log("onServiceWorkerUpdate", registration);
// const btn = (
// <Space flex>
// <Button
// onClick={async () => {
// window.open("https://imex-online.noticeable.news/", "_blank");
// }}
// >
// {i18n.t("general.actions.viewreleasenotes")}
// </Button>
// <Button
// type="primary"
// onClick={async () => {
// if (registration && registration.waiting) {
// await registration.unregister();
// // Makes Workbox call skipWaiting()
// registration.waiting.postMessage({ type: "SKIP_WAITING" });
// // Once the service worker is unregistered, we can reload the page to let
// // the browser download a fresh copy of our app (invalidating the cache)
// window.location.reload();
// }
// }}
// >
// {i18n.t("general.actions.refresh")}
// </Button>
// </Space>
// );
// notification.open({
// icon: <AlertOutlined />,
// message: i18n.t("general.messages.newversiontitle"),
// description: i18n.t("general.messages.newversionmessage"),
// duration: 0,
// btn,
// key: "updateavailable",
// });
// };
// serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
reportWebVitals(); reportWebVitals();

View File

@@ -1,9 +1,9 @@
import React from "react"; import React from 'react';
import { Button, Space } from "antd"; import { Button } from 'antd';
import { DownOutlined } from "@ant-design/icons"; import { DownOutlined } from '@ant-design/icons';
import QueueAnim from "rc-queue-anim"; import QueueAnim from 'rc-queue-anim';
import TweenOne from "rc-tween-one"; import TweenOne from 'rc-tween-one';
import { isImg } from "./utils"; import { isImg } from './utils';
class Banner extends React.PureComponent { class Banner extends React.PureComponent {
render() { render() {
@@ -15,12 +15,12 @@ class Banner extends React.PureComponent {
<div {...currentProps} {...dataSource.wrapper}> <div {...currentProps} {...dataSource.wrapper}>
<QueueAnim <QueueAnim
key="QueueAnim" key="QueueAnim"
type={["bottom", "top"]} type={['bottom', 'top']}
delay={200} delay={200}
{...dataSource.textWrapper} {...dataSource.textWrapper}
> >
<div key="title" {...dataSource.title}> <div key="title" {...dataSource.title}>
{typeof dataSource.title.children === "string" && {typeof dataSource.title.children === 'string' &&
dataSource.title.children.match(isImg) ? ( dataSource.title.children.match(isImg) ? (
<img src={dataSource.title.children} width="100%" alt="img" /> <img src={dataSource.title.children} width="100%" alt="img" />
) : ( ) : (
@@ -30,18 +30,13 @@ class Banner extends React.PureComponent {
<div key="content" {...dataSource.content}> <div key="content" {...dataSource.content}>
{dataSource.content.children} {dataSource.content.children}
</div> </div>
<Space wrap> <Button ghost key="button" {...dataSource.button}>
<Button ghost key="button" {...dataSource.button}> {dataSource.button.children}
{dataSource.button.children} </Button>
</Button>
<Button type="primary" key="button2" {...dataSource.button2}>
{dataSource.button2.children}
</Button>
</Space>
</QueueAnim> </QueueAnim>
<TweenOne <TweenOne
animation={{ animation={{
y: "-=20", y: '-=20',
yoyo: true, yoyo: true,
repeat: -1, repeat: -1,
duration: 1000, duration: 1000,

View File

@@ -74,26 +74,26 @@ export const Nav00DataSource = {
// }, // },
// ], // ],
// }, // },
// { {
// name: "item1", name: "item1",
// className: "header0-item", className: "header0-item",
// children: { children: {
// href: "#", href: "#",
// children: [ children: [
// { children: i18n.t("landing.labels.features"), name: "text" }, { children: i18n.t("landing.labels.features"), name: "text" },
// ], ],
// }, },
// }, },
// { {
// name: "item2", name: "item2",
// className: "header0-item", className: "header0-item",
// children: { children: {
// href: "#", href: "#",
// children: [ children: [
// { children: i18n.t("landing.labels.pricing"), name: "text" }, { children: i18n.t("landing.labels.pricing"), name: "text" },
// ], ],
// }, },
// }, },
{ {
name: "item3", name: "item3",
className: "header0-item", className: "header0-item",
@@ -124,12 +124,7 @@ export const Banner00DataSource = {
button: { button: {
className: "banner0-button", className: "banner0-button",
children: i18n.t("landing.hero.button"), children: i18n.t("landing.hero.button"),
href: "https://imexsystems.ca/products/imex-online", href: "https://imexsystems.ca",
},
button2: {
className: "banner0-button2",
children: i18n.t("landing.labels.managemyshop"),
href: "/signin",
}, },
}; };
export const Content40DataSource = { export const Content40DataSource = {
@@ -1038,22 +1033,22 @@ export const Footer10DataSource = {
childWrapper: { childWrapper: {
children: [ children: [
{ {
href: "https://imexsystems.ca/privacy", href: "/privacy",
name: "link0", name: "link0",
children: i18n.t("landing.footer.company.privacypolicy"), children: i18n.t("landing.footer.company.privacypolicy"),
}, },
// { {
// href: "/about", href: "/about",
// name: "link1", name: "link1",
// children: i18n.t("landing.footer.company.about"), children: i18n.t("landing.footer.company.about"),
// }, },
{ {
href: "/disclaimer", href: "/disclaimer",
name: "link2", name: "link2",
children: i18n.t("landing.footer.company.disclaimers"), children: i18n.t("landing.footer.company.disclaimers"),
}, },
{ {
href: "https://imexsystems.ca/schedule-a-demo/", href: "https://thinkimex.com",
name: "link3", name: "link3",
children: i18n.t("landing.footer.company.contact"), children: i18n.t("landing.footer.company.contact"),
}, },
@@ -1068,7 +1063,7 @@ export const Footer10DataSource = {
className: "copyright", className: "copyright",
children: ( children: (
<span> <span>
©2022 <a href="http://imexsystems.ca">ImEX Systems</a> used under ©2021 <a href="http://imexsystems.ca">ImEX Systems</a> used under
license. license.
</span> </span>
), ),

View File

@@ -4,27 +4,27 @@ import { enquireScreen } from "enquire-js";
import React from "react"; import React from "react";
import Banner0 from "./Banner0"; import Banner0 from "./Banner0";
// import Content4 from "./Content4"; // import Content4 from "./Content4";
//import Content0 from "./Content0"; import Content0 from "./Content0";
//import Content1 from "./Content1"; import Content1 from "./Content1";
import { import {
Banner00DataSource, Banner00DataSource,
// Content40DataSource, // Content40DataSource,
//Content00DataSource, Content00DataSource,
//Content10DataSource, Content10DataSource,
// Pricing11DataSource, // Pricing11DataSource,
// Content30DataSource, // Content30DataSource,
// Content120DataSource, // Content120DataSource,
Footer10DataSource, Footer10DataSource,
// Nav00DataSource, Nav00DataSource,
//Pricing20DataSource, Pricing20DataSource,
} from "./data.source"; } from "./data.source";
// import Pricing1 from "./Pricing1"; // import Pricing1 from "./Pricing1";
// import Content3 from "./Content3"; // import Content3 from "./Content3";
// import Content12 from "./Content12"; // import Content12 from "./Content12";
import Footer1 from "./Footer1"; import Footer1 from "./Footer1";
import "./less/antMotionStyle.less"; import "./less/antMotionStyle.less";
// import Nav0 from "./Nav0"; import Nav0 from "./Nav0";
//import Pricing2 from "./Pricing2"; import Pricing2 from "./Pricing2";
let isMobile; let isMobile;
enquireScreen((b) => { enquireScreen((b) => {
@@ -62,64 +62,64 @@ export default class Home extends React.Component {
render() { render() {
const children = [ const children = [
// <Nav0 <Nav0
// id="Nav0_0" id="Nav0_0"
// key="Nav0_0" key="Nav0_0"
// dataSource={Nav00DataSource} dataSource={Nav00DataSource}
// isMobile={this.state.isMobile} isMobile={this.state.isMobile}
// />, />,
<Banner0 <Banner0
id="Banner0_0" id="Banner0_0"
key="Banner0_0" key="Banner0_0"
dataSource={Banner00DataSource} dataSource={Banner00DataSource}
isMobile={this.state.isMobile} isMobile={this.state.isMobile}
/>, />,
// ...(process.env.NODE_ENV !== "production" ...(process.env.NODE_ENV !== "production"
// ? [ ? [
// // <Content4 // <Content4
// // id="Content4_0" // id="Content4_0"
// // key="Content4_0" // key="Content4_0"
// // dataSource={Content40DataSource} // dataSource={Content40DataSource}
// // isMobile={this.state.isMobile} // isMobile={this.state.isMobile}
// // />, // />,
// <Content1 <Content1
// id="Content1_0" id="Content1_0"
// key="Content1_0" key="Content1_0"
// dataSource={Content10DataSource} dataSource={Content10DataSource}
// isMobile={this.state.isMobile} isMobile={this.state.isMobile}
// />, />,
// <Content0 <Content0
// id="Content0_0" id="Content0_0"
// key="Content0_0" key="Content0_0"
// dataSource={Content00DataSource} dataSource={Content00DataSource}
// isMobile={this.state.isMobile} isMobile={this.state.isMobile}
// />, />,
// <Pricing2 <Pricing2
// id="Pricing2_0" id="Pricing2_0"
// key="Pricing2_0" key="Pricing2_0"
// dataSource={Pricing20DataSource} dataSource={Pricing20DataSource}
// isMobile={this.state.isMobile} isMobile={this.state.isMobile}
// />, />,
// // <Pricing1 // <Pricing1
// // id="Pricing1_1" // id="Pricing1_1"
// // key="Pricing1_1" // key="Pricing1_1"
// // dataSource={Pricing11DataSource} // dataSource={Pricing11DataSource}
// // isMobile={this.state.isMobile} // isMobile={this.state.isMobile}
// // />, // />,
// // <Content3 // <Content3
// // id="Content3_0" // id="Content3_0"
// // key="Content3_0" // key="Content3_0"
// // dataSource={Content30DataSource} // dataSource={Content30DataSource}
// // isMobile={this.state.isMobile} // isMobile={this.state.isMobile}
// // />, // />,
// // <Content12 // <Content12
// // id="Content12_0" // id="Content12_0"
// // key="Content12_0" // key="Content12_0"
// // dataSource={Content120DataSource} // dataSource={Content120DataSource}
// // isMobile={this.state.isMobile} // isMobile={this.state.isMobile}
// // />, // />,
// ] ]
// : []), : []),
<Footer1 <Footer1
id="Footer1_0" id="Footer1_0"
key="Footer1_0" key="Footer1_0"

View File

@@ -6,7 +6,7 @@
position: relative; position: relative;
text-align: center; text-align: center;
border-color: #666; border-color: #666;
background-image: url("../../assets/banner4.jpg"); background-image: url("../../assets/banner1.jpeg");
background-size: cover; background-size: cover;
background-attachment: fixed; background-attachment: fixed;
background-position: center; background-position: center;
@@ -60,28 +60,6 @@
width: auto; width: auto;
} }
} }
& &-button2 {
border: 1px solid #fff;
color: #fff;
// background: transparent;
box-shadow: 0 0 0 transparent;
font-size: 16px;
height: 40px;
transition: background 0.45s @ease-out, box-shadow 0.45s @ease-out;
&:hover {
color: #fff;
border-color: #fff;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(50, 250, 255, 0.75);
}
&:focus {
color: #fff;
border-color: #fff;
}
&.queue-anim-leaving {
width: auto;
}
}
& &-icon { & &-icon {
bottom: 20px; bottom: 20px;
font-size: 24px; font-size: 24px;

View File

@@ -45,7 +45,7 @@ export function AccountingPayablesContainer({
checkPartnerStatus(bodyshop, true); checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]); }, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data, refetch } = useQuery(QUERY_BILLS_FOR_EXPORT, { const { loading, error, data } = useQuery(QUERY_BILLS_FOR_EXPORT, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
@@ -73,7 +73,6 @@ export function AccountingPayablesContainer({
<AccountingPayablesTable <AccountingPayablesTable
loadaing={loading} loadaing={loading}
bills={data ? data.bills : []} bills={data ? data.bills : []}
refetch={refetch}
/> />
</RbacWrapper> </RbacWrapper>
</div> </div>

View File

@@ -44,13 +44,10 @@ export function AccountingPaymentsContainer({
checkPartnerStatus(bodyshop, true); checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]); }, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data, refetch } = useQuery( const { loading, error, data } = useQuery(QUERY_PAYMENTS_FOR_EXPORT, {
QUERY_PAYMENTS_FOR_EXPORT, fetchPolicy: "network-only",
{ nextFetchPolicy: "network-only",
fetchPolicy: "network-only", });
nextFetchPolicy: "network-only",
}
);
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
const noPath = const noPath =
@@ -73,7 +70,6 @@ export function AccountingPaymentsContainer({
<AccountingPaymentsTable <AccountingPaymentsTable
loadaing={loading} loadaing={loading}
payments={data ? data.payments : []} payments={data ? data.payments : []}
refetch={refetch}
/> />
</RbacWrapper> </RbacWrapper>
</div> </div>

View File

@@ -44,7 +44,7 @@ export function AccountingReceivablesContainer({
checkPartnerStatus(bodyshop, true); checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]); }, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data, refetch } = useQuery(QUERY_JOBS_FOR_EXPORT, { const { loading, error, data } = useQuery(QUERY_JOBS_FOR_EXPORT, {
variables: { variables: {
invoicedStatus: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*", invoicedStatus: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*",
}, },
@@ -75,7 +75,6 @@ export function AccountingReceivablesContainer({
<AccountingReceivablesTable <AccountingReceivablesTable
loadaing={loading} loadaing={loading}
jobs={data ? data.jobs : []} jobs={data ? data.jobs : []}
refetch={refetch}
/> />
</RbacWrapper> </RbacWrapper>
</div> </div>

View File

@@ -6,7 +6,6 @@ import Icon, {
PrinterFilled, PrinterFilled,
ToolFilled, ToolFilled,
HistoryOutlined, HistoryOutlined,
SyncOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
Button, Button,
@@ -50,7 +49,6 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component"; import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -63,7 +61,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(insertAuditTrail({ jobid, operation })), dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function JobsDetailPage({ export function JobsDetailPage({
bodyshop,
setPrintCenterContext, setPrintCenterContext,
jobRO, jobRO,
job, job,
@@ -181,15 +178,6 @@ export function JobsDetailPage({
const menuExtra = ( const menuExtra = (
<Space wrap> <Space wrap>
<Button
onClick={() => {
refetch();
}}
key="refresh"
>
<SyncOutlined />
{t("general.labels.refresh")}
</Button>
<JobsChangeStatus job={job} /> <JobsChangeStatus job={job} />
<JobSyncButton job={job} /> <JobSyncButton job={job} />
<Button <Button
@@ -346,11 +334,7 @@ export function JobsDetailPage({
} }
key="documents" key="documents"
> >
{bodyshop.uselocalmediaserver ? ( <JobsDocumentsGalleryContainer jobId={job.id} />
<JobsDocumentsLocalGallery job={job} />
) : (
<JobsDocumentsGalleryContainer jobId={job.id} />
)}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane <Tabs.TabPane
tab={ tab={

View File

@@ -1,36 +0,0 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
import JobsReadyList from "../../components/jobs-ready-list/jobs-ready-list.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export function JobsReadyPage({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.readyjobs");
setSelectedHeader("readyjobs");
setBreadcrumbs([
{ link: "/manage/jobs", label: t("titles.bc.jobs-ready") },
]);
}, [t, setBreadcrumbs, setSelectedHeader]);
return (
<RbacWrapper action="jobs:list-ready">
<JobsReadyList />
<JobDetailCards />
</RbacWrapper>
);
}
export default connect(null, mapDispatchToProps)(JobsReadyPage);

View File

@@ -127,7 +127,6 @@ const AccountingPayments = lazy(() =>
import("../accounting-payments/accounting-payments.container") import("../accounting-payments/accounting-payments.container")
); );
const AllJobs = lazy(() => import("../jobs-all/jobs-all.container")); const AllJobs = lazy(() => import("../jobs-all/jobs-all.container"));
const ReadyJobs = lazy(() => import("../jobs-ready/jobs-ready.page"));
const JobsClose = lazy(() => import("../jobs-close/jobs-close.container")); const JobsClose = lazy(() => import("../jobs-close/jobs-close.container"));
const JobsAdmin = lazy(() => import("../jobs-admin/jobs-admin.page")); const JobsAdmin = lazy(() => import("../jobs-admin/jobs-admin.page"));
const TempDocs = lazy(() => const TempDocs = lazy(() =>
@@ -241,7 +240,6 @@ export function Manage({ match, conflict, bodyshop }) {
component={JobsAdmin} component={JobsAdmin}
/> />
<Route exact path={`${match.path}/jobs/all`} component={AllJobs} /> <Route exact path={`${match.path}/jobs/all`} component={AllJobs} />
<Route exact path={`${match.path}/jobs/ready`} component={ReadyJobs} />
<Route <Route
exact exact
path={`${match.path}/jobs/new`} path={`${match.path}/jobs/new`}

View File

@@ -1,22 +1,23 @@
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { Button, Card, Input, Space, Table } from "antd"; import { Button, Card, Input, Space, Table } from "antd";
import _ from "lodash";
import queryString from "query-string";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component"; import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries"; import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper"; import { onlyUnique } from "../../utils/arrayHelper";
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort, dateSort } from "../../utils/sorters"; import { TimeAgoFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
import _ from "lodash";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -24,25 +25,20 @@ const mapStateToProps = createStructuredSelector({
export function PartsQueuePageComponent({ bodyshop }) { export function PartsQueuePageComponent({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { const { page, sortcolumn, sortorder, statusFilters } = searchParams;
//page,
sortcolumn,
sortorder,
statusFilters,
} = searchParams;
const history = useHistory(); const history = useHistory();
const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, { const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
// offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * 25 : 0,
// limit: 25, limit: 25,
statuses: (statusFilters && JSON.parse(statusFilters)) || statuses: (statusFilters && JSON.parse(statusFilters)) ||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: [ order: [
{ {
[sortcolumn || "ro_number"]: sortorder [sortcolumn || "updated_at"]: sortorder
? sortorder === "descend" ? sortorder === "descend"
? "desc" ? "desc"
: "asc" : "asc"
@@ -89,7 +85,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
: []; : [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
// searchParams.page = pagination.current; searchParams.page = pagination.current;
searchParams.sortcolumn = sorter.columnKey; searchParams.sortcolumn = sorter.columnKey;
searchParams.sortorder = sorter.order; searchParams.sortorder = sorter.order;
if (filters.status) { if (filters.status) {
@@ -118,10 +114,10 @@ export function PartsQueuePageComponent({ bodyshop }) {
}, },
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "ownr_ln", dataIndex: "owner",
key: "ownr_ln", key: "owner",
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), // sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder: sortcolumn === "ownr_ln" && sortorder, // sortOrder: sortcolumn === "owner" && sortorder,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
<Link to={"/manage/owners/" + record.ownerid}> <Link to={"/manage/owners/" + record.ownerid}>
@@ -158,17 +154,6 @@ export function PartsQueuePageComponent({ bodyshop }) {
}, },
}, },
{
title: t("jobs.fields.scheduled_in"),
dataIndex: "scheduled_in",
key: "scheduled_in",
ellipsis: true,
sorter: (a, b) => dateSort(a.scheduled_in, b.scheduled_in),
sortOrder: sortcolumn === "scheduled_in" && sortorder,
render: (text, record) => (
<DateTimeFormatter>{record.scheduled_in}</DateTimeFormatter>
),
},
{ {
title: t("jobs.fields.vehicle"), title: t("jobs.fields.vehicle"),
dataIndex: "vehicle", dataIndex: "vehicle",
@@ -188,16 +173,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
); );
}, },
}, },
// { {
// title: t("vehicles.fields.plate_no"), title: t("vehicles.fields.plate_no"),
// dataIndex: "plate_no", dataIndex: "plate_no",
// key: "plate_no", key: "plate_no",
// sorter: (a, b) => alphaSort(a.plate_no, b.plate_no), sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
// sortOrder: sortcolumn === "plate_no" && sortorder, sortOrder: sortcolumn === "plate_no" && sortorder,
// render: (text, record) => { render: (text, record) => {
// return record.plate_no ? record.plate_no : ""; return record.plate_no ? record.plate_no : "";
// }, },
// }, },
{ {
title: t("jobs.fields.clm_no"), title: t("jobs.fields.clm_no"),
dataIndex: "clm_no", dataIndex: "clm_no",
@@ -205,50 +190,42 @@ export function PartsQueuePageComponent({ bodyshop }) {
ellipsis: true, ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no), sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder, sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => {
return record.clm_no ? (
<span>{record.clm_no}</span>
) : (
t("general.labels.unknown")
);
},
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => {
return record.clm_total ? (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
) : (
t("general.labels.unknown")
);
},
}, },
// {
// title: t("jobs.fields.clm_total"),
// dataIndex: "clm_total",
// key: "clm_total",
// sorter: (a, b) => a.clm_total - b.clm_total,
// sortOrder: sortcolumn === "clm_total" && sortorder,
// render: (text, record) => {
// return record.clm_total ? (
// <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
// ) : (
// t("general.labels.unknown")
// );
// },
// },
{ {
title: t("jobs.fields.updated_at"), title: t("jobs.fields.updated_at"),
dataIndex: "updated_at", dataIndex: "updated_at",
key: "updated_at", key: "updated_at",
sorter: (a, b) => dateSort(a.updated_at, b.updated_at),
sortOrder: sortcolumn === "updated_at" && sortorder,
render: (text, record) => ( render: (text, record) => (
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter> <TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
), ),
}, },
{ {
title: t("jobs.fields.partsstatus"), title: t("general.labels.actions"),
dataIndex: "partsstatus", dataIndex: "actions",
key: "partsstatus", key: "actions",
render: (text, record) => ( render: (text, record) => (
<JobPartsQueueCount parts={record.joblines_status} /> <JobRemoveFromPartsQueue jobId={record.id} refetch={refetch} />
),
},
{
title: t("jobs.fields.queued_for_parts"),
dataIndex: "queued_for_parts",
key: "queued_for_parts",
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
render: (text, record) => (
<JobRemoveFromPartsQueue
checked={record.queued_for_parts}
jobId={record.id}
/>
), ),
}, },
]; ];
@@ -276,9 +253,9 @@ export function PartsQueuePageComponent({ bodyshop }) {
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 50, pageSize: 25,
// current: parseInt(page || 1), current: parseInt(page || 1),
// total: data && data.jobs_aggregate.aggregate.count, total: data && data.jobs_aggregate.aggregate.count,
}} }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"

View File

@@ -160,7 +160,6 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
<Button <Button
onClick={() => { onClick={() => {
delete searchParams.search; delete searchParams.search;
searchParams.page = 1;
history.push({ search: queryString.stringify(searchParams) }); history.push({ search: queryString.stringify(searchParams) });
}} }}
> >
@@ -178,7 +177,6 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
placeholder={searchParams.search || t("general.labels.search")} placeholder={searchParams.search || t("general.labels.search")}
onSearch={(value) => { onSearch={(value) => {
searchParams.search = value; searchParams.search = value;
searchParams.page = 1;
history.push({ search: queryString.stringify(searchParams) }); history.push({ search: queryString.stringify(searchParams) });
}} }}
/> />

View File

@@ -2,14 +2,17 @@ import React, { useEffect } 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 FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import { useSubscription } from "@apollo/client";
import { SUBSCRIPTION_SCOREBOARD } from "../../graphql/scoreboard.queries";
import moment from "moment";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -23,6 +26,13 @@ const mapDispatchToProps = (dispatch) => ({
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) { export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation(); const { t } = useTranslation();
const scoreboardSubscription = useSubscription(SUBSCRIPTION_SCOREBOARD, {
variables: {
start: moment().startOf("month"),
end: moment().endOf("month"),
},
});
useEffect(() => { useEffect(() => {
document.title = t("titles.scoreboard"); document.title = t("titles.scoreboard");
setSelectedHeader("scoreboard"); setSelectedHeader("scoreboard");
@@ -37,7 +47,7 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
return ( return (
<FeatureWrapper featureName="scoreboard"> <FeatureWrapper featureName="scoreboard">
<RbacWrapper action="scoreboard:view"> <RbacWrapper action="scoreboard:view">
<ScoreboardDisplay /> <ScoreboardDisplay scoreboardSubscription={scoreboardSubscription} />
</RbacWrapper> </RbacWrapper>
</FeatureWrapper> </FeatureWrapper>
); );

View File

@@ -5,34 +5,15 @@ import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component"; import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries"; import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries";
import { connect } from "react-redux"; export default function TemporaryDocsComponent() {
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TemporaryDocsComponent);
export function TemporaryDocsComponent({ bodyshop }) {
const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, { const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
skip: bodyshop.uselocalmediaserver,
}); });
if (loading) return <LoadingSpinner />; if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
if (bodyshop.uselocalmediaserver) {
return <JobsDocumentsLocalGallery job={{ id: "temporary" }} />;
}
return ( return (
<JobsDocumentsComponent <JobsDocumentsComponent
data={data ? data.documents : []} data={data ? data.documents : []}

View File

@@ -14,7 +14,6 @@ import TimeTicketList from "../../components/time-ticket-list/time-ticket-list.c
import TimeTicketsPayrollTable from "../../components/time-tickets-payroll-table/time-tickets-payroll-table.component"; import TimeTicketsPayrollTable from "../../components/time-tickets-payroll-table/time-tickets-payroll-table.component";
import TimeTicketsSummaryEmployees from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component"; import TimeTicketsSummaryEmployees from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component";
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries"; import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
import TimeTicketsAttendanceTable from "../../components/time-tickets-attendance-table/time-tickets-attendance-table.component";
import { import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
@@ -72,7 +71,6 @@ export function TimeTicketsContainer({
timetickets={data ? data.timetickets : []} timetickets={data ? data.timetickets : []}
extra={ extra={
<Space wrap> <Space wrap>
<TimeTicketsAttendanceTable />
<TimeTicketsPayrollTable /> <TimeTicketsPayrollTable />
<TimeTicketsDatesSelector /> <TimeTicketsDatesSelector />
</Space> </Space>

Some files were not shown because too many files have changed in this diff Show More