@@ -4,6 +4,7 @@
|
||||
..\*Include the statuses file in the format of:
|
||||
|
||||
```json
|
||||
{
|
||||
"statuses": [
|
||||
"Open",
|
||||
"Scheduled",
|
||||
@@ -21,14 +22,29 @@
|
||||
"Invoiced",
|
||||
"Exported"
|
||||
],
|
||||
"default_imported": "Open",
|
||||
"default_scheduled": "Scheduled",
|
||||
"open_statuses": [
|
||||
"Open",
|
||||
"Scheduled",
|
||||
"Arrived",
|
||||
"Repair Plan",
|
||||
"Parts",
|
||||
"Body",
|
||||
"Prep",
|
||||
"Paint",
|
||||
"Reassembly",
|
||||
"Sublet",
|
||||
"Detail",
|
||||
"Completed"
|
||||
],
|
||||
"default_arrived": "Arrived",
|
||||
"default_exported": "Exported",
|
||||
"default_imported": "Open",
|
||||
"default_invoiced": "Invoiced",
|
||||
"default_completed": "Completed",
|
||||
"default_delivered": "Delivered",
|
||||
"default_invoiced": "Invoiced",
|
||||
"default_exported": "Exported"
|
||||
"default_scheduled": "Scheduled"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
--\* Set the region for the shop.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.6.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.6.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -1283,6 +1283,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>loadingshop</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>loggingin</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>na</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1346,6 +1388,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>search</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>unknown</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -1466,6 +1529,27 @@
|
||||
<folder_node>
|
||||
<name>validation</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>invalidemail</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>required</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4823,6 +4907,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>shop_config</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>shop_vendors</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>vehicles</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -5769,6 +5895,167 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>parts</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>actions</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>order</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>parts_orders</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>creating</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>
|
||||
<name>fields</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deliver_by</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>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>email</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>print</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>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>created</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>profile</name>
|
||||
<children>
|
||||
@@ -6013,6 +6300,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>shop_vendors</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>vehicledetail</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -6701,6 +7009,519 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>vendors</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>actions</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>new</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>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deleting</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>fields</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>city</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>cost_center</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>country</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>discount</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>display_name</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>due_date</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>email</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>favorite</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>name</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>prompt_discount</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>state</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>street1</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>street2</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>taxid</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>terms</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>zip</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>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>noneselected</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>search</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>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>deleted</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>saved</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>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"private": true,
|
||||
"proxy": "https://localhost:5000",
|
||||
"dependencies": {
|
||||
"@ckeditor/ckeditor5-build-classic": "^16.0.0",
|
||||
"@ckeditor/ckeditor5-react": "^2.1.0",
|
||||
"antd": "^3.26.8",
|
||||
"apollo-boost": "^0.4.4",
|
||||
"apollo-link-context": "^1.0.19",
|
||||
@@ -23,6 +25,7 @@
|
||||
"react-big-calendar": "^0.23.0",
|
||||
"react-chartjs-2": "^2.9.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-html-email": "^3.0.0",
|
||||
"react-i18next": "^11.3.1",
|
||||
"react-icons": "^3.9.0",
|
||||
"react-image-file-resizer": "^0.2.1",
|
||||
@@ -37,7 +40,8 @@
|
||||
"redux-saga": "^1.1.3",
|
||||
"reselect": "^4.0.0",
|
||||
"styled-components": "^5.0.1",
|
||||
"subscriptions-transport-ws": "^0.9.16"
|
||||
"subscriptions-transport-ws": "^0.9.16",
|
||||
"twilio": "^3.39.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import i18next from "i18next";
|
||||
import React, { lazy, Suspense, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -33,7 +34,7 @@ export default connect(
|
||||
checkUserSession();
|
||||
return () => {};
|
||||
}, [checkUserSession]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
if (currentUser && currentUser.language)
|
||||
i18next.changeLanguage(currentUser.language, (err, t) => {
|
||||
if (err)
|
||||
@@ -41,8 +42,7 @@ export default connect(
|
||||
});
|
||||
|
||||
if (currentUser.authorized === null) {
|
||||
//TODO: Translate this.
|
||||
return <LoadingSpinner message="Waiting for Current Auth to persist." />;
|
||||
return <LoadingSpinner message={t("general.labels.loggingin")} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -50,15 +50,15 @@ export default connect(
|
||||
<Switch>
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<LoadingSpinner />}>
|
||||
<Route exact path="/" component={LandingPage} />
|
||||
<Route exact path="/unauthorized" component={Unauthorized} />
|
||||
<Route exact path='/' component={LandingPage} />
|
||||
<Route exact path='/unauthorized' component={Unauthorized} />
|
||||
|
||||
<Route exact path="/signin" component={SignInPage} />
|
||||
<Route exact path='/signin' component={SignInPage} />
|
||||
|
||||
<PrivateRoute
|
||||
//isAuthorized={HookCurrentUser.data.currentUser ? true : false}
|
||||
isAuthorized={currentUser.authorized}
|
||||
path="/manage"
|
||||
path='/manage'
|
||||
component={ManagePage}
|
||||
/>
|
||||
</Suspense>
|
||||
|
||||
@@ -2,10 +2,12 @@ import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import Alert from "./alert.component";
|
||||
import { MockedProvider } from "@apollo/react-testing";
|
||||
import { shallow } from "enzyme";
|
||||
import { shallow, mount } from "enzyme";
|
||||
|
||||
const div = document.createElement("div");
|
||||
|
||||
it("renders without crashing", () => {
|
||||
shallow(<Alert type="error" />);
|
||||
const wrapper = mount(<Alert type="error" message="Test Error" />);
|
||||
console.log("wrapper", wrapper);
|
||||
// expect(wrapper.children()).to.have.lengthOf(1);
|
||||
});
|
||||
|
||||
@@ -5,7 +5,11 @@ import { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { notification } from "antd";
|
||||
|
||||
export default function AllocationsAssignmentContainer({ jobLineId, hours }) {
|
||||
export default function AllocationsAssignmentContainer({
|
||||
jobLineId,
|
||||
hours,
|
||||
refetch
|
||||
}) {
|
||||
const visibilityState = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const [assignment, setAssignment] = useState({
|
||||
@@ -20,9 +24,9 @@ export default function AllocationsAssignmentContainer({ jobLineId, hours }) {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
visibilityState[1](false);
|
||||
//refetch().then(r => form.resetFields());
|
||||
if (refetch) refetch();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import AllocationsAssignmentContainer from "./allocations-assignment.container";
|
||||
|
||||
describe("LineAllocationsContainer", () => {
|
||||
let mockRefetch;
|
||||
let jobLineId;
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
mockRefetch = jest.fn;
|
||||
jobLineId = "b76e44a8-943f-4c67-b8f4-38d14db8b4b8";
|
||||
const mockProps = {
|
||||
refetch: mockRefetch,
|
||||
jobLineId,
|
||||
hours: 5
|
||||
};
|
||||
|
||||
shallow(<AllocationsAssignmentContainer {...mockProps} />);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,53 @@
|
||||
import React from "react";
|
||||
|
||||
import { Card } from "antd";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import "./chat-window.styles.scss"; //https://bootsnipp.com/snippets/exR5v
|
||||
import twilio from "twilio";
|
||||
// const client = require("twilio")(
|
||||
// "ACf1b1aaf0e04740828b49b6e58467d787",
|
||||
// "0bea5e29a6d77593183ab1caa01d23de"
|
||||
// );
|
||||
const client = twilio(
|
||||
"ACf1b1aaf0e04740828b49b6e58467d787",
|
||||
"0bea5e29a6d77593183ab1caa01d23de"
|
||||
);
|
||||
export default function ChatWindowComponent({ toggleChatVisible }) {
|
||||
const [conversations, setConversations] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
client.messages.list({ limit: 20 }, (error, items) => {
|
||||
setConversations(
|
||||
items.reduce((acc, value) => {
|
||||
acc.push({
|
||||
sid: value.sid,
|
||||
direction: value.direction,
|
||||
body: value.body
|
||||
});
|
||||
return acc;
|
||||
}, [])
|
||||
);
|
||||
});
|
||||
return () => {};
|
||||
}, [setConversations]);
|
||||
|
||||
console.log(conversations);
|
||||
return (
|
||||
<div style={{ height: "300px" }}>
|
||||
<button onClick={() => toggleChatVisible()}>hide</button> This is a chat
|
||||
window!
|
||||
</div>
|
||||
<Card style={{ width: "400px" }}>
|
||||
<div>
|
||||
<button onClick={() => toggleChatVisible()}>X</button>
|
||||
<div className='messages' style={{ height: "400px" }}>
|
||||
<ul>
|
||||
{conversations.map(item => (
|
||||
<li
|
||||
key={item.sid}
|
||||
className={`${
|
||||
item.direction === "inbound" ? "replies" : "sent"
|
||||
}`}>
|
||||
<p> {item.body}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Affix, Button, Badge } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -17,8 +18,20 @@ export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(function ChatWindowContainer({ chatVisible, toggleChatVisible }) {
|
||||
if (chatVisible)
|
||||
return <ChatWindowComponent toggleChatVisible={toggleChatVisible} />;
|
||||
|
||||
return <div onClick={() => toggleChatVisible()}>Chat</div>;
|
||||
return (
|
||||
<Affix offsetBottom={25}>
|
||||
{chatVisible ? (
|
||||
<ChatWindowComponent toggleChatVisible={toggleChatVisible} />
|
||||
) : (
|
||||
<Badge count={5}>
|
||||
<Button
|
||||
type='primary'
|
||||
shape='circle'
|
||||
icon='message'
|
||||
onClick={() => toggleChatVisible()}
|
||||
/>
|
||||
</Badge>
|
||||
)}
|
||||
</Affix>
|
||||
);
|
||||
});
|
||||
|
||||
127
client/src/components/chat-window/chat-window.styles.scss
Normal file
127
client/src/components/chat-window/chat-window.styles.scss
Normal file
@@ -0,0 +1,127 @@
|
||||
.messages {
|
||||
height: auto;
|
||||
min-height: calc(100% - 93px);
|
||||
max-height: calc(100% - 93px);
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
@media screen and (max-width: 735px) {
|
||||
.messages {
|
||||
max-height: calc(100% - 105px);
|
||||
}
|
||||
}
|
||||
.messages::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: transparent;
|
||||
}
|
||||
.messages::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.messages ul li {
|
||||
display: inline-block;
|
||||
clear: both;
|
||||
float: left;
|
||||
margin: 5px 15px 5px 15px;
|
||||
width: calc(100% - 25px);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.messages ul li:nth-last-child(1) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.messages ul li.sent img {
|
||||
margin: 6px 8px 0 0;
|
||||
}
|
||||
.messages ul li.sent p {
|
||||
background: #435f7a;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.messages ul li.replies img {
|
||||
float: right;
|
||||
margin: 6px 0 0 8px;
|
||||
}
|
||||
.messages ul li.replies p {
|
||||
background: #f5f5f5;
|
||||
float: right;
|
||||
}
|
||||
.messages ul li img {
|
||||
width: 22px;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
}
|
||||
.messages ul li p {
|
||||
display: inline-block;
|
||||
padding: 10px 15px;
|
||||
border-radius: 20px;
|
||||
max-width: 205px;
|
||||
line-height: 130%;
|
||||
}
|
||||
@media screen and (min-width: 735px) {
|
||||
.messages ul li p {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
.message-input {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
}
|
||||
.message-input .wrap {
|
||||
position: relative;
|
||||
}
|
||||
.message-input .wrap input {
|
||||
font-family: "proxima-nova", "Source Sans Pro", sans-serif;
|
||||
float: left;
|
||||
border: none;
|
||||
width: calc(100% - 90px);
|
||||
padding: 11px 32px 10px 8px;
|
||||
font-size: 0.8em;
|
||||
color: #32465a;
|
||||
}
|
||||
@media screen and (max-width: 735px) {
|
||||
.message-input .wrap input {
|
||||
padding: 15px 32px 16px 8px;
|
||||
}
|
||||
}
|
||||
.message-input .wrap input:focus {
|
||||
outline: none;
|
||||
}
|
||||
.message-input .wrap .attachment {
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
z-index: 4;
|
||||
margin-top: 10px;
|
||||
font-size: 1.1em;
|
||||
color: #435f7a;
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
}
|
||||
@media screen and (max-width: 735px) {
|
||||
.message-input .wrap .attachment {
|
||||
margin-top: 17px;
|
||||
right: 65px;
|
||||
}
|
||||
}
|
||||
.message-input .wrap .attachment:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.message-input .wrap button {
|
||||
float: right;
|
||||
border: none;
|
||||
width: 50px;
|
||||
padding: 12px 0;
|
||||
cursor: pointer;
|
||||
background: #32465a;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
@media screen and (max-width: 735px) {
|
||||
.message-input .wrap button {
|
||||
padding: 16px 0;
|
||||
}
|
||||
}
|
||||
.message-input .wrap button:hover {
|
||||
background: #435f7a;
|
||||
}
|
||||
.message-input .wrap button:focus {
|
||||
outline: none;
|
||||
}
|
||||
@@ -16,102 +16,99 @@ export default ({
|
||||
const { t } = useTranslation();
|
||||
//TODO Add
|
||||
return (
|
||||
<Row type="flex" justify="space-around" align="middle">
|
||||
<Row type='flex' justify='space-around' align='middle'>
|
||||
{logo ? (
|
||||
<Col span={4}>
|
||||
<img alt="Shop Logo" src={logo} style={{ height: "40px" }} />
|
||||
<img alt='Shop Logo' src={logo} style={{ height: "40px" }} />
|
||||
</Col>
|
||||
) : null}
|
||||
<Col span={14}>
|
||||
<Menu
|
||||
theme="dark"
|
||||
className="header"
|
||||
theme='dark'
|
||||
className='header'
|
||||
selectedKeys={selectedNavItem}
|
||||
mode="horizontal"
|
||||
onClick={handleMenuClick}
|
||||
>
|
||||
<Menu.Item key="home">
|
||||
<Link to="/manage">
|
||||
<Icon type="home" />
|
||||
mode='horizontal'
|
||||
onClick={handleMenuClick}>
|
||||
<Menu.Item key='home'>
|
||||
<Link to='/manage'>
|
||||
<Icon type='home' />
|
||||
{t("menus.header.home")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu title={t("menus.header.jobs")}>
|
||||
<Menu.Item key="schedule">
|
||||
<Link to="/manage/schedule">
|
||||
<Icon type="calendar" />
|
||||
<Menu.Item key='schedule'>
|
||||
<Link to='/manage/schedule'>
|
||||
<Icon type='calendar' />
|
||||
{t("menus.header.schedule")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="activejobs">
|
||||
<Link to="/manage/jobs">
|
||||
<Icon type="home" />
|
||||
{t("menus.header.activejobs")}
|
||||
</Link>
|
||||
<Menu.Item key='activejobs'>
|
||||
<Link to='/manage/jobs'>{t("menus.header.activejobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="availablejobs">
|
||||
<Link to="/manage/available">
|
||||
<Icon type="home" />
|
||||
<Menu.Item key='availablejobs'>
|
||||
<Link to='/manage/available'>
|
||||
{t("menus.header.availablejobs")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
<Menu.SubMenu title={t("menus.header.customers")}>
|
||||
<Menu.Item key="owners">
|
||||
<Link to="/manage/owners">
|
||||
<Icon type="team" />
|
||||
<Menu.Item key='owners'>
|
||||
<Link to='/manage/owners'>
|
||||
<Icon type='team' />
|
||||
{t("menus.header.owners")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="vehicles">
|
||||
<Link to="/manage/vehicles">
|
||||
<Icon type="car" />
|
||||
<Menu.Item key='vehicles'>
|
||||
<Link to='/manage/vehicles'>
|
||||
<Icon type='car' />
|
||||
{t("menus.header.vehicles")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
<Menu.Item key="shop">
|
||||
<Link to="/manage/shop">
|
||||
{t("menus.header.shop")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu title={t("menus.header.shop")}>
|
||||
<Menu.Item key='shop'>
|
||||
<Link to='/manage/shop'>{t("menus.header.shop_config")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key='shop-vendors'>
|
||||
<Link to='/manage/shop/vendors'>
|
||||
{t("menus.header.shop_vendors")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<div>
|
||||
<Avatar
|
||||
size="medium"
|
||||
alt="Avatar"
|
||||
size='medium'
|
||||
alt='Avatar'
|
||||
src={currentUser.photoURL ? currentUser.photoURL : UserImage}
|
||||
style={{ margin: "10px" }}
|
||||
/>
|
||||
{currentUser.displayName || t("general.labels.unknown")}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
}>
|
||||
<Menu.Item onClick={signOutStart()}>
|
||||
{t("user.actions.signout")}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
||||
<Link to='/manage/profile'>{t("menus.currentuser.profile")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.SubMenu
|
||||
title={
|
||||
<span>
|
||||
<Icon type="global" />
|
||||
<Icon type='global' />
|
||||
<span>{t("menus.currentuser.languageselector")}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item actiontype="lang-select" key="en_us">
|
||||
}>
|
||||
<Menu.Item actiontype='lang-select' key='en_us'>
|
||||
{t("general.languages.english")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="fr">
|
||||
<Menu.Item actiontype='lang-select' key='fr'>
|
||||
{t("general.languages.french")}
|
||||
</Menu.Item>
|
||||
<Menu.Item actiontype="lang-select" key="es">
|
||||
<Menu.Item actiontype='lang-select' key='es'>
|
||||
{t("general.languages.spanish")}
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
|
||||
@@ -17,6 +17,7 @@ import JobDetailCardsNotesComponent from "./job-detail-cards.notes.component";
|
||||
import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
|
||||
import "./job-detail-cards.styles.scss";
|
||||
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
|
||||
import ScheduleJobModalContainer from "../schedule-job-modal/schedule-job-modal.container";
|
||||
|
||||
export default function JobDetailCards({ selectedJob }) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_JOB_CARD_DETAILS, {
|
||||
@@ -25,29 +26,35 @@ export default function JobDetailCards({ selectedJob }) {
|
||||
skip: !selectedJob
|
||||
});
|
||||
const [noteModalVisible, setNoteModalVisible] = useState(false);
|
||||
const scheduleModalState = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!selectedJob) {
|
||||
return <div>{t("jobs.errors.nojobselected")}</div>;
|
||||
}
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
|
||||
return (
|
||||
<div className="job-cards-container">
|
||||
<div className='job-cards-container'>
|
||||
<NoteUpsertModal
|
||||
jobId={data.jobs_by_pk.id}
|
||||
visible={noteModalVisible}
|
||||
changeVisibility={setNoteModalVisible}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<ScheduleJobModalContainer
|
||||
scheduleModalState={scheduleModalState}
|
||||
jobId={data.jobs_by_pk.id}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<PageHeader
|
||||
ghost={false}
|
||||
onBack={() => window.history.back()}
|
||||
tags={
|
||||
<span key="job-status">
|
||||
<span key='job-status'>
|
||||
{data.jobs_by_pk.status ? (
|
||||
<Tag color="blue">{data.jobs_by_pk.status}</Tag>
|
||||
<Tag color='blue'>{data.jobs_by_pk.status}</Tag>
|
||||
) : null}
|
||||
</span>
|
||||
}
|
||||
@@ -65,35 +72,40 @@ export default function JobDetailCards({ selectedJob }) {
|
||||
)
|
||||
}
|
||||
extra={[
|
||||
<Button
|
||||
key='schedule'
|
||||
//TODO Enabled logic based on status.
|
||||
onClick={() => {
|
||||
scheduleModalState[1](true);
|
||||
}}>
|
||||
{t("jobs.actions.schedule")}
|
||||
</Button>,
|
||||
<Link
|
||||
key="documents"
|
||||
to={`/manage/jobs/${data.jobs_by_pk.id}#documents`}
|
||||
>
|
||||
key='documents'
|
||||
to={`/manage/jobs/${data.jobs_by_pk.id}#documents`}>
|
||||
<Button>
|
||||
<Icon type="file-image" />
|
||||
<Icon type='file-image' />
|
||||
{t("jobs.actions.addDocuments")}
|
||||
</Button>
|
||||
</Link>,
|
||||
<Button key="printing">
|
||||
<Icon type="printer" />
|
||||
<Button key='printing'>
|
||||
<Icon type='printer' />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>,
|
||||
<Button
|
||||
key="notes"
|
||||
actiontype="addNote"
|
||||
key='notes'
|
||||
actiontype='addNote'
|
||||
onClick={() => {
|
||||
setNoteModalVisible(!noteModalVisible);
|
||||
}}
|
||||
>
|
||||
<Icon type="edit" />
|
||||
}}>
|
||||
<Icon type='edit' />
|
||||
{t("jobs.actions.addNote")}
|
||||
</Button>,
|
||||
<Button key="postinvoices">
|
||||
<Icon type="shopping-cart" />
|
||||
<Button key='postinvoices'>
|
||||
<Icon type='shopping-cart' />
|
||||
{t("jobs.actions.postInvoices")}
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
]}>
|
||||
{
|
||||
// loading ? (
|
||||
// <LoadingSkeleton />
|
||||
@@ -114,7 +126,7 @@ export default function JobDetailCards({ selectedJob }) {
|
||||
// )
|
||||
}
|
||||
|
||||
<section className="job-cards">
|
||||
<section className='job-cards'>
|
||||
<JobDetailCardsCustomerComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
|
||||
@@ -14,7 +14,10 @@ export default function JobDetailCardsCustomerComponent({ loading, data }) {
|
||||
extraLink={data && data.owner ? `/manage/owners/${data.owner.id}` : null}>
|
||||
{data ? (
|
||||
<span>
|
||||
<div>{`${data.ownr_fn || ""} ${data.ownr_ln || ""}`}</div>
|
||||
<div>
|
||||
<Link to={`/manage/owners/${data.owner.id}`}>{`${data.ownr_fn ||
|
||||
""} ${data.ownr_ln || ""}`}</Link>
|
||||
</div>
|
||||
<div>
|
||||
{t("jobs.fields.phoneshort")}:
|
||||
<PhoneFormatter>{`${data.ownr_ph1 ||
|
||||
|
||||
@@ -18,13 +18,12 @@ export default function JobDetailCardsDocumentsComponent({ loading, data }) {
|
||||
<CardTemplate
|
||||
loading={loading}
|
||||
title={t("jobs.labels.cards.documents")}
|
||||
extraLink={`/manage/jobs/${data.id}#documents`}>
|
||||
{data.documents.count > 0 ? (
|
||||
extraLink={`/manage/jobs/${data.id}#documents`}
|
||||
>
|
||||
{data.documents.length > 0 ? (
|
||||
<Carousel autoplay>
|
||||
{data.documents.map(item => (
|
||||
<div key={item.id}>
|
||||
<img src={item.thumb_url} alt={item.name} />
|
||||
</div>
|
||||
<img key={item.id} src={item.thumb_url} alt={item.name} />
|
||||
))}
|
||||
</Carousel>
|
||||
) : (
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
.ant-carousel .slick-slide {
|
||||
text-align: center;
|
||||
height: 160px;
|
||||
line-height: 160px;
|
||||
background: #364d79;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ant-carousel .slick-slide h3 {
|
||||
color: #fff;
|
||||
}
|
||||
text-align: center;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
line-height: 50px;
|
||||
background: #364d79;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ant-carousel .slick-slide h3 {
|
||||
color: #ccddaa;
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from "react";
|
||||
import { Form, Input, InputNumber } from "antd";
|
||||
import JobDetailFormContext from "../../pages/jobs-detail/jobs-detail.page.context";
|
||||
|
||||
export default class EditableCell extends React.Component {
|
||||
getInput = () => {
|
||||
if (this.props.inputType === "number") {
|
||||
return <InputNumber />;
|
||||
}
|
||||
return <Input />;
|
||||
};
|
||||
|
||||
renderCell = ({ getFieldDecorator }) => {
|
||||
const {
|
||||
editing,
|
||||
dataIndex,
|
||||
title,
|
||||
inputType,
|
||||
record,
|
||||
index,
|
||||
children,
|
||||
...restProps
|
||||
} = this.props;
|
||||
return (
|
||||
<td {...restProps}>
|
||||
{editing ? (
|
||||
<Form.Item style={{ margin: 0 }}>
|
||||
{getFieldDecorator(dataIndex, {
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: `Please Input ${title}!`
|
||||
}
|
||||
],
|
||||
initialValue: record[dataIndex]
|
||||
})(this.getInput())}
|
||||
</Form.Item>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<JobDetailFormContext.Consumer>
|
||||
{this.renderCell}
|
||||
</JobDetailFormContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,27 @@
|
||||
import { Input, Table } from "antd";
|
||||
import { Button, Input, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
//import EditableCell from "./job-lines-cell.component";
|
||||
import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
|
||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||
|
||||
export default function JobLinesComponent({
|
||||
loading,
|
||||
refetch,
|
||||
jobLines,
|
||||
form,
|
||||
handleSubmit,
|
||||
setSearchText
|
||||
setSearchText,
|
||||
selectedLines,
|
||||
setSelectedLines,
|
||||
partsOrderModalVisible,
|
||||
jobId
|
||||
}) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {}
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const setPartsModalVisible = partsOrderModalVisible[1];
|
||||
const columns = [
|
||||
{
|
||||
title: t("joblines.fields.unq_seq"),
|
||||
@@ -119,17 +123,29 @@ export default function JobLinesComponent({
|
||||
{record.allocations && record.allocations.length > 0
|
||||
? record.allocations.map(item => (
|
||||
<div
|
||||
key={item.id}
|
||||
>{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}</div>
|
||||
key={
|
||||
item.id
|
||||
}>{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}</div>
|
||||
))
|
||||
: null}
|
||||
<AllocationsAssignmentContainer
|
||||
key={record.id}
|
||||
refetch={refetch}
|
||||
jobLineId={record.id}
|
||||
hours={record.mod_lb_hrs}
|
||||
/>
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("general.labels.actions"),
|
||||
dataIndex: "actions",
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<span>
|
||||
<Button>{t("general.actions.edit")}</Button>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
@@ -137,10 +153,6 @@ export default function JobLinesComponent({
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
// const handleChange = event => {
|
||||
// const { value } = event.target;
|
||||
// setState({ ...state, filterinfo: { text: [value] } });
|
||||
// };
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 12 },
|
||||
@@ -153,25 +165,46 @@ export default function JobLinesComponent({
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
title={() => {
|
||||
return (
|
||||
<Input.Search
|
||||
placeholder="Search..."
|
||||
onChange={e => {
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
{...formItemLayout}
|
||||
size="small"
|
||||
pagination={{ position: "bottom", defaultPageSize: 50 }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
rowKey="id"
|
||||
dataSource={jobLines}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
<div>
|
||||
<PartsOrderModalContainer
|
||||
partsOrderModalVisible={partsOrderModalVisible}
|
||||
linesToOrder={selectedLines}
|
||||
jobId={jobId}
|
||||
/>
|
||||
|
||||
<Table
|
||||
title={() => {
|
||||
return (
|
||||
<div>
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onChange={e => {
|
||||
e.preventDefault();
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={selectedLines.length > 0 ? false : true}
|
||||
onClick={() => setPartsModalVisible(true)}>
|
||||
{t("parts.actions.order")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
{...formItemLayout}
|
||||
loading={loading}
|
||||
size='small'
|
||||
pagination={{ position: "bottom", defaultPageSize: 50 }}
|
||||
rowSelection={{
|
||||
// selectedRowKeys: selectedLines,
|
||||
onSelect: (record, selected, selectedRows, nativeEvent) =>
|
||||
setSelectedLines(selectedRows)
|
||||
}}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
rowKey='id'
|
||||
dataSource={jobLines}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,52 +1,27 @@
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import { notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { GET_JOB_LINES_BY_PK } from "../../graphql/jobs-lines.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import JobLinesComponent from "./job-lines.component";
|
||||
|
||||
//export default Form.create({ name: "JobsDetailJobLines" })(
|
||||
|
||||
export default function JobLinesContainer({ jobId, form }) {
|
||||
const { loading, error, data } = useQuery(GET_JOB_LINES_BY_PK, {
|
||||
export default function JobLinesContainer({ jobId }) {
|
||||
const { loading, error, data, refetch } = useQuery(GET_JOB_LINES_BY_PK, {
|
||||
variables: { id: jobId },
|
||||
fetchPolicy: "network-only"
|
||||
});
|
||||
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
|
||||
form.validateFieldsAndScroll((err, values) => {
|
||||
if (err) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.validationtitle"),
|
||||
description: t("jobs.errors.validation")
|
||||
});
|
||||
}
|
||||
if (!err) {
|
||||
console.log("Save the est lines!", values);
|
||||
// mutationUpdateJob({
|
||||
// variables: { jobId: data.jobs_by_pk.id, job: values }
|
||||
// }).then(r => {
|
||||
// notification["success"]({
|
||||
// message: t("jobs.successes.savetitle")
|
||||
// });
|
||||
// //TODO: Better way to reset the field decorators?
|
||||
// refetch().then(r => form.resetFields());
|
||||
// });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
const [selectedLines, setSelectedLines] = useState([]);
|
||||
const partsOrderModalVisible = useState(false);
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
|
||||
return (
|
||||
<JobLinesComponent
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
jobLines={
|
||||
data && data.joblines
|
||||
? searchText
|
||||
@@ -78,9 +53,11 @@ export default function JobLinesContainer({ jobId, form }) {
|
||||
: data.joblines
|
||||
: null
|
||||
}
|
||||
handleSubmit={handleSubmit}
|
||||
form={form}
|
||||
setSearchText={setSearchText}
|
||||
selectedLines={selectedLines}
|
||||
setSelectedLines={setSelectedLines}
|
||||
partsOrderModalVisible={partsOrderModalVisible}
|
||||
jobId={jobId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function JobsDetailClaims({ job }) {
|
||||
initialValue: job.loss_desc
|
||||
})(<Input name='loss_desc' />)}
|
||||
</Form.Item>
|
||||
TODO: How to handle different taxes and marking them as exempt?
|
||||
TODO How to handle different taxes and marking them as exempt?
|
||||
{
|
||||
// <Form.Item label={t("jobs.fields.exempt")}>
|
||||
// {getFieldDecorator("exempt", {
|
||||
|
||||
@@ -25,13 +25,13 @@ export default function JobsDetailFinancials({ job }) {
|
||||
initialValue: job.depreciation_taxes
|
||||
})(<InputNumber name="depreciation_taxes" />)}
|
||||
</Form.Item>
|
||||
TODO: This is equivalent of GST payable.
|
||||
TODO This is equivalent of GST payable.
|
||||
<Form.Item label={t("jobs.fields.federal_tax_payable")}>
|
||||
{getFieldDecorator("federal_tax_payable", {
|
||||
initialValue: job.federal_tax_payable
|
||||
})(<InputNumber name="federal_tax_payable" />)}
|
||||
</Form.Item>
|
||||
TODO: equivalent of other customer amount
|
||||
TODO equivalent of other customer amount
|
||||
<Form.Item label={t("jobs.fields.other_amount_payable")}>
|
||||
{getFieldDecorator("other_amount_payable", {
|
||||
initialValue: job.other_amount_payable
|
||||
@@ -99,7 +99,7 @@ export default function JobsDetailFinancials({ job }) {
|
||||
initialValue: job.rate_lag
|
||||
})(<InputNumber name="rate_lag" />)}
|
||||
</Form.Item>
|
||||
Note //TODO: Remove ATP rate?
|
||||
Note //TODO Remove ATP rate?
|
||||
<Form.Item label={t("jobs.fields.rate_atp")}>
|
||||
{getFieldDecorator("rate_atp", {
|
||||
initialValue: job.rate_atp
|
||||
|
||||
@@ -43,7 +43,7 @@ export default connect(
|
||||
|
||||
const tombstoneTitle = (
|
||||
<div>
|
||||
<Avatar size="large" alt="Vehicle Image" src={CarImage} />
|
||||
<Avatar size='large' alt='Vehicle Image' src={CarImage} />
|
||||
{`${t("jobs.fields.ro_number")} ${
|
||||
job.ro_number ? job.ro_number : t("general.labels.na")
|
||||
}`}
|
||||
@@ -54,8 +54,7 @@ export default connect(
|
||||
<Menu
|
||||
onClick={e => {
|
||||
updateJobStatus(e.key);
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{bodyshop.md_ro_statuses.statuses.map(item => (
|
||||
<Menu.Item key={item}>{item}</Menu.Item>
|
||||
))}
|
||||
@@ -63,24 +62,23 @@ export default connect(
|
||||
);
|
||||
|
||||
const menuExtra = [
|
||||
<Dropdown overlay={statusmenu} key="changestatus">
|
||||
<Dropdown overlay={statusmenu} key='changestatus'>
|
||||
<Button>
|
||||
{t("jobs.actions.changestatus")} <Icon type="down" />
|
||||
{t("jobs.actions.changestatus")} <Icon type='down' />
|
||||
</Button>
|
||||
</Dropdown>,
|
||||
<Badge key="schedule" count={job.appointments_aggregate.aggregate.count}>
|
||||
<Badge key='schedule' count={job.appointments_aggregate.aggregate.count}>
|
||||
<Button
|
||||
//TODO: Enabled logic based on status.
|
||||
//TODO Enabled logic based on status.
|
||||
onClick={() => {
|
||||
setscheduleModalVisible(true);
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{t("jobs.actions.schedule")}
|
||||
</Button>
|
||||
</Badge>,
|
||||
<Button
|
||||
key="convert"
|
||||
type="dashed"
|
||||
key='convert'
|
||||
type='dashed'
|
||||
disabled={job.converted}
|
||||
onClick={() => {
|
||||
mutationConvertJob({
|
||||
@@ -92,16 +90,14 @@ export default connect(
|
||||
message: t("jobs.successes.converted")
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{t("jobs.actions.convert")}
|
||||
</Button>,
|
||||
<Button
|
||||
type="primary"
|
||||
key="submit"
|
||||
htmlType="button"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
type='primary'
|
||||
key='submit'
|
||||
htmlType='button'
|
||||
onClick={handleSubmit}>
|
||||
{t("general.labels.save")}
|
||||
</Button>
|
||||
];
|
||||
@@ -114,9 +110,9 @@ export default connect(
|
||||
title={tombstoneTitle}
|
||||
//subTitle={tombstoneSubtitle}
|
||||
tags={
|
||||
<span key="job-status">
|
||||
{job.status ? <Tag color="blue">{job.status}</Tag> : null}
|
||||
<Tag color="red">
|
||||
<span key='job-status'>
|
||||
{job.status ? <Tag color='blue'>{job.status}</Tag> : null}
|
||||
<Tag color='red'>
|
||||
{job.owner ? (
|
||||
<Link to={`/manage/owners/${job.owner.id}`}>
|
||||
{`${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln ||
|
||||
@@ -126,7 +122,7 @@ export default connect(
|
||||
t("jobs.errors.noowner")
|
||||
)}
|
||||
</Tag>
|
||||
<Tag color="green">
|
||||
<Tag color='green'>
|
||||
{job.vehicle ? (
|
||||
<Link to={`/manage/vehicles/${job.vehicle.id}`}>
|
||||
{job.vehicle.v_model_yr || t("general.labels.na")}{" "}
|
||||
@@ -140,11 +136,10 @@ export default connect(
|
||||
<BarcodePopup value={job.id} />
|
||||
</span>
|
||||
}
|
||||
extra={menuExtra}
|
||||
>
|
||||
<Descriptions size="small" column={5}>
|
||||
extra={menuExtra}>
|
||||
<Descriptions size='small' column={5}>
|
||||
<Descriptions.Item label={t("jobs.fields.repairtotal")}>
|
||||
<CurrencyFormatter>{job.claim_total}</CurrencyFormatter>
|
||||
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label={t("jobs.fields.customerowing")}>
|
||||
@@ -157,7 +152,7 @@ export default connect(
|
||||
|
||||
<Descriptions.Item label={t("jobs.fields.scheduled_completion")}>
|
||||
{job.scheduled_completion ? (
|
||||
<Moment format="MM/DD/YYYY">{job.scheduled_completion}</Moment>
|
||||
<Moment format='MM/DD/YYYY'>{job.scheduled_completion}</Moment>
|
||||
) : null}
|
||||
</Descriptions.Item>
|
||||
|
||||
|
||||
@@ -2,12 +2,22 @@ import { Modal } from "antd";
|
||||
import React from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { QUERY_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import JobsFindModalComponent from "./jobs-find-modal.component";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
export default function JobsFindModalContainer({
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(function JobsFindModalContainer({
|
||||
bodyshop,
|
||||
loading,
|
||||
error,
|
||||
selectedJob,
|
||||
@@ -17,8 +27,11 @@ export default function JobsFindModalContainer({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const jobsList = useQuery(QUERY_ALL_OPEN_JOBS, {
|
||||
fetchPolicy: "network-only"
|
||||
const jobsList = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
fetchPolicy: "network-only",
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.open_statuses || ["Open"]
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -26,10 +39,9 @@ export default function JobsFindModalContainer({
|
||||
title={t("jobs.labels.existing_jobs")}
|
||||
width={"80%"}
|
||||
okButtonProps={{ disabled: selectedJob ? false : true }}
|
||||
{...modalProps}
|
||||
>
|
||||
{...modalProps}>
|
||||
{loading ? <LoadingSpinner /> : null}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{error ? <AlertComponent message={error.message} type='error' /> : null}
|
||||
{true ? (
|
||||
<JobsFindModalComponent
|
||||
selectedJob={selectedJob}
|
||||
@@ -43,4 +55,4 @@ export default function JobsFindModalContainer({
|
||||
) : null}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Input, Table, Icon } from "antd";
|
||||
import { Input, Table, Icon, Button } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
@@ -8,6 +8,8 @@ import { withRouter } from "react-router-dom";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
|
||||
export default withRouter(function JobsList({
|
||||
searchTextState,
|
||||
refetch,
|
||||
loading,
|
||||
jobs,
|
||||
selectedJob,
|
||||
@@ -21,6 +23,7 @@ export default withRouter(function JobsList({
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const setSearchText = searchTextState[1];
|
||||
const columns = [
|
||||
{
|
||||
title: t("jobs.fields.ro_number"),
|
||||
@@ -29,7 +32,11 @@ export default withRouter(function JobsList({
|
||||
width: "8%",
|
||||
// onFilter: (value, record) => record.ro_number.includes(value),
|
||||
// filteredValue: state.filteredInfo.text || null,
|
||||
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
a.ro_number ? a.ro_number : "EST-" + a.est_number,
|
||||
b.ro_number ? b.ro_number : "EST-" + b.est_number
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
|
||||
|
||||
@@ -73,7 +80,7 @@ export default withRouter(function JobsList({
|
||||
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
|
||||
<Icon
|
||||
style={{ margin: 4 }}
|
||||
type="message"
|
||||
type='message'
|
||||
onClick={() => {
|
||||
alert("SMSing will happen here.");
|
||||
}}
|
||||
@@ -205,19 +212,24 @@ export default withRouter(function JobsList({
|
||||
loading={loading}
|
||||
title={() => {
|
||||
return (
|
||||
<Input.Search
|
||||
placeholder="Search..."
|
||||
onSearch={value => {
|
||||
console.log(value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button onClick={() => refetch()}>
|
||||
<Icon type='sync' />
|
||||
</Button>
|
||||
<Input.Search
|
||||
placeholder='Search...'
|
||||
onChange={e => {
|
||||
setSearchText(e.target.value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
size="small"
|
||||
size='small'
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
rowKey="id"
|
||||
rowKey='id'
|
||||
dataSource={jobs}
|
||||
rowSelection={{ selectedRowKeys: [selectedJob] }}
|
||||
onChange={handleTableChange}
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function NoteUpsertModalContainer({
|
||||
insertNote({
|
||||
variables: {
|
||||
noteInput: [
|
||||
{ ...noteState, jobid: jobId, created_by: "patrick@bodyshop.app" } //TODO: Fix the created by.
|
||||
{ ...noteState, jobid: jobId, created_by: "patrick@bodyshop.app" } //TODO Fix the created by.
|
||||
]
|
||||
}
|
||||
}).then(r => {
|
||||
|
||||
@@ -27,7 +27,7 @@ function OwnerDetailFormContainer({ form, owner, refetch }) {
|
||||
notification["success"]({
|
||||
message: t("owners.successes.save")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
if (refetch) refetch().then();
|
||||
form.resetFields();
|
||||
});
|
||||
|
||||
@@ -20,12 +20,7 @@ export default function OwnerFindModalContainer({
|
||||
|
||||
const ownersList = useQuery(QUERY_SEARCH_OWNER_BY_IDX, {
|
||||
variables: {
|
||||
search: owner
|
||||
? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${owner.ownr_addr1 ||
|
||||
""} ${owner.ownr_city || ""} ${owner.ownr_zip ||
|
||||
""} ${owner.ownr_ea || ""} ${owner.ownr_ph1 ||
|
||||
""} ${owner.ownr_ph2 || ""}`
|
||||
: null
|
||||
search: owner ? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""}` : null
|
||||
},
|
||||
skip: !owner,
|
||||
fetchPolicy: "network-only"
|
||||
@@ -35,18 +30,17 @@ export default function OwnerFindModalContainer({
|
||||
<Modal
|
||||
title={t("owners.labels.existing_owners")}
|
||||
width={"80%"}
|
||||
{...modalProps}
|
||||
>
|
||||
{...modalProps}>
|
||||
{loading ? <LoadingSpinner /> : null}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{error ? <AlertComponent message={error.message} type='error' /> : null}
|
||||
{owner ? (
|
||||
<OwnerFindModalComponent
|
||||
selectedOwner={selectedOwner}
|
||||
setSelectedOwner={setSelectedOwner}
|
||||
ownersListLoading={ownersList.loading}
|
||||
ownersList={
|
||||
ownersList.data && ownersList.data.search_owners
|
||||
? ownersList.data.search_owners
|
||||
ownersList.data && ownersList.data.search_owner
|
||||
? ownersList.data.search_owner
|
||||
: null
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import React, { useState } from "react";
|
||||
import { AutoComplete, Icon, DatePicker, Radio } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
export default function PartsOrderModalComponent({
|
||||
vendorList,
|
||||
state,
|
||||
sendTypeState
|
||||
}) {
|
||||
const [partsOrder, setPartsOrder] = state;
|
||||
const [sendType, setSendType] = sendTypeState;
|
||||
const [vendorComplete, setVendorComplete] = useState(vendorList);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSearch = value => {
|
||||
if (value === "") setVendorComplete(vendorList);
|
||||
else
|
||||
setVendorComplete(
|
||||
vendorList.filter(v =>
|
||||
v.name.toLowerCase().includes(value.toLowerCase())
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleSelect = (value, option) => {
|
||||
console.log("value", value);
|
||||
console.log("option", option);
|
||||
setPartsOrder({ ...partsOrder, vendorid: option.key });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AutoComplete
|
||||
onSearch={handleSearch}
|
||||
onSelect={handleSelect}
|
||||
defaultOpen
|
||||
backfill
|
||||
optionLabelProp='value'
|
||||
dataSource={vendorComplete}
|
||||
placeholder={t("vendors.labels.search")}>
|
||||
{vendorComplete.map(v => (
|
||||
<AutoComplete.Option value={v.name} key={v.id}>
|
||||
<div>{v.name}</div>
|
||||
<div> {v.favorite ? <Icon type='heart' /> : null}</div>
|
||||
</AutoComplete.Option>
|
||||
))}
|
||||
</AutoComplete>
|
||||
{t("parts_orders.fields.deliver_by")}
|
||||
<DatePicker
|
||||
defaultValue={partsOrder.deliver_by}
|
||||
onChange={e => {
|
||||
setPartsOrder({ ...partsOrder, deliver_by: e });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Radio.Group
|
||||
defaultValue={sendType}
|
||||
onChange={e => setSendType(e.target.value)}>
|
||||
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
|
||||
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Modal, notification } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useQuery, useMutation } from "react-apollo";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries";
|
||||
import { INSERT_NEW_PARTS_ORDERS } from "../../graphql/parts-orders.queries";
|
||||
import {
|
||||
selectCurrentUser,
|
||||
selectBodyshop
|
||||
} from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import PartsOrderModalComponent from "./parts-order-modal.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser,
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(function PartsOrderModalContainer({
|
||||
partsOrderModalVisible,
|
||||
linesToOrder,
|
||||
jobId,
|
||||
currentUser,
|
||||
bodyshop
|
||||
}) {
|
||||
const [modalVisible, setModalVisible] = partsOrderModalVisible;
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, {
|
||||
fetchPolicy: "network-only",
|
||||
skip: !modalVisible
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
|
||||
const sendTypeState = useState("e");
|
||||
|
||||
const partsOrderState = useState({
|
||||
vendorid: null,
|
||||
jobid: jobId,
|
||||
user_email: currentUser.email
|
||||
});
|
||||
|
||||
console.log("sendTypeState[0]", sendTypeState[0]);
|
||||
const partsOrder = partsOrderState[0];
|
||||
const handleOk = () => {
|
||||
insertPartOrder({
|
||||
variables: {
|
||||
po: [
|
||||
{
|
||||
...partsOrder,
|
||||
status: bodyshop.md_order_statuses.default_ordered || "Ordered*",
|
||||
parts_order_lines: {
|
||||
data: linesToOrder.reduce((acc, value) => {
|
||||
acc.push({
|
||||
line_desc: value.line_desc,
|
||||
oem_partno: value.oem_partno,
|
||||
db_price: value.db_price,
|
||||
act_price: value.act_price,
|
||||
line_remarks: "Alalala",
|
||||
joblineid: value.joblineid,
|
||||
status:
|
||||
bodyshop.md_order_statuses.default_ordered || "Ordered*"
|
||||
});
|
||||
return acc;
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
.then(r => {
|
||||
notification["success"]({
|
||||
message: t("parts_orders.successes.created")
|
||||
});
|
||||
setModalVisible(false);
|
||||
})
|
||||
.catch(error => {
|
||||
notification["error"]({
|
||||
message: t("parts_orders.errors.creating"),
|
||||
description: error.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
onOk={handleOk}>
|
||||
{error ? <AlertComponent message={error.message} type='error' /> : null}
|
||||
<LoadingSpinner loading={loading}>
|
||||
<PartsOrderModalComponent
|
||||
vendorList={(data && data.vendors) || []}
|
||||
state={partsOrderState}
|
||||
sendTypeState={sendTypeState}
|
||||
/>
|
||||
</LoadingSpinner>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Button, Form, Input, notification } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import { Form, Input, notification, Button } from "antd";
|
||||
import { updateUserDetails } from "../../redux/user/user.actions";
|
||||
import { selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import ResetForm from "../form-items-formatted/reset-form-item.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
currentUser: selectCurrentUser
|
||||
@@ -47,33 +47,26 @@ export default connect(
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isFieldsTouched() ? (
|
||||
//TODO: Appropriate Error
|
||||
<AlertComponent
|
||||
message={t("jobs.errors.validation")}
|
||||
onClick={() => resetFields()}
|
||||
/>
|
||||
) : null}
|
||||
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
|
||||
|
||||
<Form onSubmit={handleSubmit} autoComplete={"no"}>
|
||||
<Form.Item label={t("user.fields.displayname")}>
|
||||
{getFieldDecorator("displayname", {
|
||||
initialValue: currentUser.displayName,
|
||||
rules: [{ required: true }]
|
||||
})(<Input name="displayname" />)}
|
||||
})(<Input name='displayname' />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("user.fields.photourl")}>
|
||||
{getFieldDecorator("photoURL", {
|
||||
initialValue: currentUser.photoURL
|
||||
})(<Input name="photoURL" />)}
|
||||
})(<Input name='photoURL' />)}
|
||||
</Form.Item>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
key="submit"
|
||||
htmlType="submit"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
type='primary'
|
||||
key='submit'
|
||||
htmlType='submit'
|
||||
onClick={handleSubmit}>
|
||||
{t("user.actions.updateprofile")}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function ScheduleJobModalComponent({
|
||||
...props
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
//TODO: Existing appointments list only refreshes sometimes after modal close. May have to do with the container class.
|
||||
//TODO Existing appointments list only refreshes sometimes after modal close. May have to do with the container class.
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
@@ -47,7 +47,7 @@ export default function ScheduleJobModalComponent({
|
||||
</Row>
|
||||
|
||||
{
|
||||
//TODO: Build out notifications.
|
||||
//TODO Build out notifications.
|
||||
}
|
||||
<Checkbox
|
||||
defaultChecked={formData.notifyCustomer}
|
||||
|
||||
@@ -43,7 +43,7 @@ export default connect(
|
||||
visible={scheduleModalVisible}
|
||||
onCancel={() => setscheduleModalVisible(false)}
|
||||
onOk={() => {
|
||||
//TODO: Customize the amount of minutes it will add.
|
||||
//TODO Customize the amount of minutes it will add.
|
||||
insertAppointment({
|
||||
variables: {
|
||||
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
|
||||
@@ -55,7 +55,7 @@ export default connect(
|
||||
});
|
||||
|
||||
if (formData.notifyCustomer) {
|
||||
//TODO: Implement customer reminder on scheduling.
|
||||
//TODO Implement customer reminder on scheduling.
|
||||
alert("Chosed to notify the customer somehow!");
|
||||
}
|
||||
setscheduleModalVisible(false);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
@import 'react-big-calendar/lib/sass/styles';
|
||||
@@ -1,7 +1,8 @@
|
||||
import moment from "moment";
|
||||
import React from "react";
|
||||
import { Calendar, momentLocalizer } from "react-big-calendar";
|
||||
import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||
//import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||
import "./schedule-calendar.styles.scss";
|
||||
import DateCellWrapper from "../schedule-datecellwrapper/schedule-datecellwrapper.component";
|
||||
import Event from "../schedule-event/schedule-event.container";
|
||||
const localizer = momentLocalizer(moment);
|
||||
@@ -19,7 +20,7 @@ export default function ScheduleCalendarWrapperComponent({
|
||||
step={30}
|
||||
showMultiDayTimes
|
||||
localizer={localizer}
|
||||
min={new Date("2020-01-01T06:00:00")} //TODO: Read from business settings.
|
||||
min={new Date("2020-01-01T06:00:00")} //TODO Read from business settings.
|
||||
max={new Date("2020-01-01T20:00:00")}
|
||||
components={{
|
||||
event: e => {
|
||||
|
||||
@@ -61,14 +61,14 @@ export default function ScheduleEventComponent({ event, handleCancel }) {
|
||||
</Button>
|
||||
<Button>
|
||||
{
|
||||
//TODO: Add reschedule Func.
|
||||
//TODO Add reschedule Func.
|
||||
}
|
||||
{t("appointments.actions.reschedule")}
|
||||
</Button>
|
||||
{event.isintake ? (
|
||||
<Button>
|
||||
{
|
||||
//TODO: Add intake func.
|
||||
//TODO Add intake func.
|
||||
}
|
||||
{t("appointments.actions.intake")}
|
||||
</Button>
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function ScheduleJobModalComponent({
|
||||
...props
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
//TODO: Existing appointments list only refreshes sometimes after modal close. May have to do with the container class.
|
||||
//TODO Existing appointments list only refreshes sometimes after modal close. May have to do with the container class.
|
||||
return (
|
||||
<Modal
|
||||
{...props}
|
||||
@@ -52,7 +52,7 @@ export default function ScheduleJobModalComponent({
|
||||
existingAppointments={existingAppointments}
|
||||
/>
|
||||
{
|
||||
//TODO: Build out notifications.
|
||||
//TODO Build out notifications.
|
||||
}
|
||||
<Checkbox
|
||||
defaultChecked={formData.notifyCustomer}
|
||||
|
||||
@@ -60,7 +60,7 @@ export default connect(
|
||||
visible={scheduleModalVisible}
|
||||
onCancel={() => setscheduleModalVisible(false)}
|
||||
onOk={() => {
|
||||
//TODO: Customize the amount of minutes it will add.
|
||||
//TODO Customize the amount of minutes it will add.
|
||||
insertAppointment({
|
||||
variables: {
|
||||
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
|
||||
@@ -73,7 +73,7 @@ export default connect(
|
||||
});
|
||||
|
||||
if (formData.notifyCustomer) {
|
||||
//TODO: Implement customer reminder on scheduling.
|
||||
//TODO Implement customer reminder on scheduling.
|
||||
alert("Chosed to notify the customer somehow!");
|
||||
}
|
||||
setscheduleModalVisible(false);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
import CKEditor from "@ckeditor/ckeditor5-react";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
|
||||
export default function SendEmailButtonComponent({
|
||||
emailConfig,
|
||||
handleConfigChange,
|
||||
handleHtmlChange
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
THis is where the text editing will happen To
|
||||
<Input
|
||||
defaultValue={emailConfig.to}
|
||||
onChange={handleConfigChange}
|
||||
name='to'
|
||||
/>
|
||||
CC
|
||||
<Input
|
||||
defaultValue={emailConfig.cc}
|
||||
onChange={handleConfigChange}
|
||||
name='cc'
|
||||
/>
|
||||
Subject
|
||||
<Input
|
||||
defaultValue={emailConfig.subject}
|
||||
onChange={handleConfigChange}
|
||||
name='subject'
|
||||
/>
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={emailConfig.html}
|
||||
onInit={editor => {
|
||||
// You can store the "editor" and use when it is needed.
|
||||
console.log("Editor is ready to use!", editor);
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
const data = editor.getData();
|
||||
console.log({ event, editor, data });
|
||||
handleHtmlChange(data);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Button, Modal } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
//Message options has the to & from details
|
||||
//Query Config is what can get popped into UseQuery(QUERY_NAME, {variables: {vars}, fetchonly})
|
||||
//Template Which template should be used to send the email.
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { renderEmail } from "react-html-email";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import SendEmailButtonComponent from "./send-email-button.component";
|
||||
export default function SendEmail({
|
||||
MessageOptions,
|
||||
QueryConfig,
|
||||
Template,
|
||||
...otherProps
|
||||
}) {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [emailConfig, setEmailConfig] = useState({ ...MessageOptions });
|
||||
const [gqlQuery, vars] = QueryConfig;
|
||||
|
||||
const { loading, data } = useQuery(gqlQuery, {
|
||||
...vars,
|
||||
fetchPolicy: "network-only",
|
||||
skip: !modalVisible
|
||||
});
|
||||
if (data && !emailConfig.html) {
|
||||
console.log(ReactDOMServer.renderToStaticMarkup(<Template data={data} />));
|
||||
setEmailConfig({
|
||||
...emailConfig,
|
||||
//html: ReactDOMServer.renderToStaticMarkup(<Template data={data} />)
|
||||
html: renderEmail(<Template data={data} />)
|
||||
});
|
||||
}
|
||||
|
||||
const handleConfigChange = event => {
|
||||
const { name, value } = event.target;
|
||||
setEmailConfig({ ...emailConfig, [name]: value });
|
||||
};
|
||||
|
||||
const handleHtmlChange = text => {
|
||||
setEmailConfig({ ...emailConfig, html: text });
|
||||
};
|
||||
|
||||
const sendEmail = () => {
|
||||
axios.post("/sendemail", emailConfig).then(response => {
|
||||
alert(JSON.stringify(response));
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
destroyOnClose={true}
|
||||
visible={modalVisible}
|
||||
width={"80%"}
|
||||
onOk={sendEmail}
|
||||
onCancel={() => setModalVisible(false)}>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<SendEmailButtonComponent
|
||||
handleConfigChange={handleConfigChange}
|
||||
emailConfig={emailConfig}
|
||||
handleHtmlChange={handleHtmlChange}
|
||||
/>
|
||||
</LoadingSpinner>
|
||||
</Modal>
|
||||
|
||||
<Button onClick={() => setModalVisible(true)}>
|
||||
{otherProps.children}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ function ShopEmployeesContainer({ form, bodyshop }) {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.delete")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
employeeState[1](null);
|
||||
refetch().then(r => form.resetFields());
|
||||
})
|
||||
@@ -61,7 +61,7 @@ function ShopEmployeesContainer({ form, bodyshop }) {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
employeeState[1](null);
|
||||
refetch().then(r => form.resetFields());
|
||||
})
|
||||
@@ -78,7 +78,7 @@ function ShopEmployeesContainer({ form, bodyshop }) {
|
||||
notification["success"]({
|
||||
message: t("employees.successes.save")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
employeeState[1](null);
|
||||
refetch()
|
||||
.then(r => form.resetFields())
|
||||
|
||||
@@ -26,7 +26,7 @@ function VehicleDetailFormContainer({ form, vehicle, refetch }) {
|
||||
notification["success"]({
|
||||
message: t("vehicles.successes.save")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
//TODO Better way to reset the field decorators?
|
||||
if (refetch) refetch().then();
|
||||
form.resetFields();
|
||||
});
|
||||
|
||||
195
client/src/components/vendors-form/vendors-form.component.jsx
Normal file
195
client/src/components/vendors-form/vendors-form.component.jsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Col,
|
||||
Form,
|
||||
Icon,
|
||||
Input,
|
||||
InputNumber,
|
||||
Row
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormItemEmail from "../form-items-formatted/email-form-item.component";
|
||||
import ResetForm from "../form-items-formatted/reset-form-item.component";
|
||||
let id = 0;
|
||||
|
||||
export default function VendorsFormComponent({ form, vendor, handleDelete }) {
|
||||
const {
|
||||
getFieldDecorator,
|
||||
isFieldsTouched,
|
||||
getFieldValue,
|
||||
resetFields
|
||||
} = form;
|
||||
|
||||
getFieldDecorator("keys", {
|
||||
initialValue: Array.isArray(vendor.favorite) ? vendor.favorite : []
|
||||
});
|
||||
|
||||
const remove = k => {
|
||||
// can use data-binding to get
|
||||
const keys = form.getFieldValue("keys");
|
||||
console.log("keys", keys);
|
||||
// We need at least one passenger
|
||||
if (keys.length === 1) {
|
||||
return;
|
||||
}
|
||||
// can use data-binding to set
|
||||
form.setFieldsValue({
|
||||
keys: keys.filter(key => key !== k)
|
||||
});
|
||||
};
|
||||
|
||||
const add = props => {
|
||||
console.log("props", props);
|
||||
// can use data-binding to get
|
||||
const keys = form.getFieldValue("keys");
|
||||
console.log("keys", keys);
|
||||
const nextKeys = keys.concat(id++);
|
||||
// can use data-binding to set
|
||||
// important! notify form to detect changes
|
||||
form.setFieldsValue({
|
||||
keys: nextKeys
|
||||
});
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
{isFieldsTouched() ? <ResetForm resetFields={resetFields} /> : null}
|
||||
<Button htmlType="submit" type="primary">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button type="danger" onClick={handleDelete}>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
|
||||
{getFieldValue("keys").map((k, index) => (
|
||||
<Form.Item required={false} key={k}>
|
||||
{getFieldDecorator(`favorite[${k}].make`, {
|
||||
validateTrigger: ["onChange", "onBlur"]
|
||||
})(
|
||||
<Input
|
||||
placeholder="passenger name"
|
||||
style={{ width: "60%", marginRight: 8 }}
|
||||
/>
|
||||
)}
|
||||
{getFieldValue("keys").length > 1 ? (
|
||||
<Icon
|
||||
className="dynamic-delete-button"
|
||||
type="minus-circle-o"
|
||||
onClick={() => remove(k)}
|
||||
/>
|
||||
) : null}
|
||||
<Form.Item label="Group">
|
||||
{getFieldDecorator(`favorite[${k}].type`, {
|
||||
initialValue: null
|
||||
})(
|
||||
<Checkbox.Group style={{ width: "100%" }}>
|
||||
<Row>
|
||||
<Col span={8}>
|
||||
<Checkbox value="OEM">OEM</Checkbox>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Checkbox value="LKQ">LKQ</Checkbox>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Checkbox value="AM">AM</Checkbox>
|
||||
</Col>
|
||||
</Row>
|
||||
</Checkbox.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item label={t("vendors.fields.favorite")}>
|
||||
<Button type="dashed" onClick={add} style={{ width: "60%" }}>
|
||||
<Icon type="plus" /> Add field
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("vendors.fields.zip")}>
|
||||
{getFieldDecorator("zip", {
|
||||
initialValue: vendor.zip
|
||||
})(<Input name="zip" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.terms")}>
|
||||
{getFieldDecorator("terms", {
|
||||
initialValue: vendor.terms
|
||||
})(<Input name="terms" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.taxid")}>
|
||||
{getFieldDecorator("taxid", {
|
||||
initialValue: vendor.taxid
|
||||
})(<Input name="taxid" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.street1")}>
|
||||
{getFieldDecorator("street1", {
|
||||
initialValue: vendor.street1
|
||||
})(<Input name="street1" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.street2")}>
|
||||
{getFieldDecorator("street2", {
|
||||
initialValue: vendor.street2
|
||||
})(<Input name="street2" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.state")}>
|
||||
{getFieldDecorator("state", {
|
||||
initialValue: vendor.state
|
||||
})(<Input name="state" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.prompt_discount")}>
|
||||
{getFieldDecorator("prompt_discount", {
|
||||
initialValue: vendor.prompt_discount
|
||||
})(<InputNumber name="prompt_discount" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.name")}>
|
||||
{getFieldDecorator("name", {
|
||||
initialValue: vendor.name
|
||||
})(<Input name="name" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.email")}>
|
||||
{getFieldDecorator("email", {
|
||||
initialValue: vendor.email,
|
||||
rules: [
|
||||
{
|
||||
type: "email",
|
||||
message: t("general.validation.invalidemail")
|
||||
}
|
||||
]
|
||||
})(<FormItemEmail name="email" email={getFieldValue("email")} />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.due_date")}>
|
||||
{getFieldDecorator("due_date", {
|
||||
initialValue: vendor.due_date
|
||||
})(<InputNumber name="due_date" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.display_name")}>
|
||||
{getFieldDecorator("display_name", {
|
||||
initialValue: vendor.display_name
|
||||
})(<Input name="display_name" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.discount")}>
|
||||
{getFieldDecorator("discount", {
|
||||
initialValue: vendor.discount
|
||||
})(<InputNumber name="discount" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.country")}>
|
||||
{getFieldDecorator("country", {
|
||||
initialValue: vendor.country
|
||||
})(<Input name="country" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.cost_center")}>
|
||||
{getFieldDecorator("cost_center", {
|
||||
initialValue: vendor.cost_center,
|
||||
rules: [{ required: true, message: t("general.validation.required") }]
|
||||
})(<Input name="cost_center" />)}
|
||||
</Form.Item>
|
||||
<Form.Item label={t("vendors.fields.city")}>
|
||||
{getFieldDecorator("city", {
|
||||
initialValue: vendor.city
|
||||
})(<Input name="city" />)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
118
client/src/components/vendors-form/vendors-form.container.jsx
Normal file
118
client/src/components/vendors-form/vendors-form.container.jsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import { Form, notification } from "antd";
|
||||
import React from "react";
|
||||
import { useMutation, useQuery } from "react-apollo";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import {
|
||||
DELETE_VENDOR,
|
||||
INSERT_NEW_VENDOR,
|
||||
UPDATE_VENDOR,
|
||||
QUERY_VENDOR_BY_ID
|
||||
} from "../../graphql/vendors.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import VendorsFormComponent from "./vendors-form.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
function VendorsFormContainer({ form, vendorId, refetch, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const { loading, error, data } = useQuery(QUERY_VENDOR_BY_ID, {
|
||||
variables: { id: vendorId },
|
||||
fetchPolicy: "network-only",
|
||||
skip: !vendorId
|
||||
});
|
||||
const [updateVendor] = useMutation(UPDATE_VENDOR);
|
||||
const [insertvendor] = useMutation(INSERT_NEW_VENDOR);
|
||||
const [deleteVendor] = useMutation(DELETE_VENDOR);
|
||||
const handleDelete = () => {
|
||||
deleteVendor({ variables: { id: vendorId } })
|
||||
.then(r => {
|
||||
notification["success"]({
|
||||
message: t("vendors.successes.deleted")
|
||||
});
|
||||
//TODO Better way to reset the field decorators?
|
||||
if (refetch) refetch().then(r => form.resetFields());
|
||||
})
|
||||
.catch(error => {
|
||||
notification["error"]({
|
||||
message: t("vendors.errors.deleting")
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
form.validateFieldsAndScroll((err, values) => {
|
||||
if (err) {
|
||||
notification["error"]({
|
||||
message: t("jobs.errors.validationtitle"),
|
||||
description: t("jobs.errors.validation")
|
||||
});
|
||||
}
|
||||
if (!err) {
|
||||
console.log("Received values of form: ", values);
|
||||
delete values.keys;
|
||||
if (vendorId) {
|
||||
//It's a vendor to update.
|
||||
updateVendor({
|
||||
variables: { id: vendorId, vendor: values }
|
||||
})
|
||||
.then(r => {
|
||||
notification["success"]({
|
||||
message: t("vendors.successes.saved")
|
||||
});
|
||||
//TODO Better way to reset the field decorators?
|
||||
if (refetch) refetch().then(r => form.resetFields());
|
||||
})
|
||||
.catch(error => {
|
||||
notification["error"]({
|
||||
message: t("vendors.errors.saving")
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//It's a new vendor to insert.
|
||||
insertvendor({
|
||||
variables: { vendorInput: [{ ...values, bodyshopid: bodyshop.id }] }
|
||||
})
|
||||
.then(r => {
|
||||
notification["success"]({
|
||||
message: t("vendors.successes.saved")
|
||||
});
|
||||
//TODO Better way to reset the field decorators?
|
||||
if (refetch) refetch().then(r => form.resetFields());
|
||||
})
|
||||
.catch(error => {
|
||||
notification["error"]({
|
||||
message: t("vendors.errors.saving")
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
return (
|
||||
<Form onSubmit={handleSubmit} autoComplete="new-password">
|
||||
{data ? (
|
||||
<VendorsFormComponent
|
||||
form={form}
|
||||
vendor={data ? data.vendors_by_pk : null}
|
||||
handleDelete={handleDelete}
|
||||
/>
|
||||
) : (
|
||||
t("vendors.labels.noneselected")
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(Form.create({ name: "VendorsFormContainer" })(VendorsFormContainer));
|
||||
102
client/src/components/vendors-list/vendors-list.component.jsx
Normal file
102
client/src/components/vendors-list/vendors-list.component.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Input, Table, Button } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
export default function VendorsListComponent({
|
||||
selectedVendor,
|
||||
setSelectedVendor,
|
||||
handleNewVendor,
|
||||
loading,
|
||||
refetch,
|
||||
handleOnRowClick,
|
||||
vendors
|
||||
}) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: { text: "" }
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: t("vendors.fields.name"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
sorter: (a, b) => alphaSort(a.name, b.name),
|
||||
sortOrder: state.sortedInfo.columnKey === "name" && state.sortedInfo.order
|
||||
},
|
||||
{
|
||||
title: t("vendors.fields.cost_center"),
|
||||
dataIndex: "cost_center",
|
||||
key: "cost_center",
|
||||
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order
|
||||
},
|
||||
{
|
||||
title: t("vendors.fields.street1"),
|
||||
dataIndex: "street1",
|
||||
key: "street1",
|
||||
width: "10%",
|
||||
sorter: (a, b) => alphaSort(a.street1, b.street1),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "street1" && state.sortedInfo.order
|
||||
},
|
||||
{
|
||||
title: t("vendors.fields.city"),
|
||||
dataIndex: "city",
|
||||
key: "city"
|
||||
}
|
||||
];
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
//TODO Implement search
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
loading={loading}
|
||||
title={() => {
|
||||
return (
|
||||
<div>
|
||||
{" "}
|
||||
<Input.Search
|
||||
placeholder={t("general.labels.search")}
|
||||
onSearch={value => {
|
||||
console.log(value);
|
||||
}}
|
||||
enterButton
|
||||
/>
|
||||
<Button onClick={handleNewVendor}>
|
||||
{t("vendors.actions.new")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
size="small"
|
||||
pagination={{ position: "top" }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
rowKey="id"
|
||||
onChange={handleTableChange}
|
||||
dataSource={vendors}
|
||||
rowSelection={{
|
||||
onSelect: record => {
|
||||
setSelectedVendor(record);
|
||||
},
|
||||
type: "radio",
|
||||
selectedRowKeys: selectedVendor ? selectedVendor.id : null
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: event => {
|
||||
handleOnRowClick(record);
|
||||
}
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
|
||||
import VendorsListComponent from "./vendors-list.component";
|
||||
|
||||
export default function VendorsListContainer({ selectedVendorState }) {
|
||||
const [selectedVendor, setSelectedVendor] = selectedVendorState;
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_VENDORS, {
|
||||
fetchPolicy: "network-only"
|
||||
});
|
||||
|
||||
const handleNewVendor = () => {
|
||||
setSelectedVendor({});
|
||||
};
|
||||
|
||||
const handleOnRowClick = record => {
|
||||
if (record) {
|
||||
setSelectedVendor(record);
|
||||
return;
|
||||
}
|
||||
setSelectedVendor(null);
|
||||
};
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
return (
|
||||
<VendorsListComponent
|
||||
selectedVendor={selectedVendor}
|
||||
setSelectedVendor={setSelectedVendor}
|
||||
handleNewVendor={handleNewVendor}
|
||||
handleOnRowClick={handleOnRowClick}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
vendors={data ? data.vendors : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
46
client/src/emails/components/grid/grid.component.jsx
Normal file
46
client/src/emails/components/grid/grid.component.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
|
||||
function Cell({ children }) {
|
||||
return <td>{children}</td>;
|
||||
}
|
||||
|
||||
function Row({ children }) {
|
||||
return (
|
||||
<tr>
|
||||
{React.Children.map(children, el => {
|
||||
if (el.type === Cell) return el;
|
||||
|
||||
return <td>{el}</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function Grid({ children }) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
{React.Children.map(children, el => {
|
||||
if (!el) return;
|
||||
|
||||
if (el.type === Row) return el;
|
||||
|
||||
if (el.type === Cell) {
|
||||
return <tr>{el}</tr>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{el}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
Grid.Row = Row;
|
||||
Grid.Cell = Cell;
|
||||
|
||||
export default Grid;
|
||||
30
client/src/emails/parts-order/parts-order.email.jsx
Normal file
30
client/src/emails/parts-order/parts-order.email.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { A, Box, Email, Item, Span } from "react-html-email";
|
||||
|
||||
export default function PartsOrderEmail({ data }) {
|
||||
console.log("Data", data);
|
||||
return (
|
||||
<Email title='Hello World!'>
|
||||
<Box>
|
||||
<Item align='center'>
|
||||
<Span fontSize={20}>
|
||||
This is an example email made with:
|
||||
<A href='https://github.com/chromakode/react-html-email'>
|
||||
react-html-email
|
||||
</A>
|
||||
.
|
||||
</Span>
|
||||
</Item>
|
||||
<Item align='center'>
|
||||
<Span fontSize={20}>
|
||||
This is an example email made with:
|
||||
<A href='https://github.com/chromakode/react-html-email'>
|
||||
react-html-email
|
||||
</A>
|
||||
.
|
||||
</Span>
|
||||
</Item>
|
||||
</Box>
|
||||
</Email>
|
||||
);
|
||||
}
|
||||
45
client/src/emails/parts-order/parts-order.query.js
Normal file
45
client/src/emails/parts-order/parts-order.query.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const REPORT_QUERY_PARTS_ORDER_BY_PK = gql`
|
||||
query REPORT_QUERY_PARTS_ORDER_BY_PK($id: uuid!) {
|
||||
parts_orders_by_pk(id: $id) {
|
||||
job {
|
||||
id
|
||||
vehicle {
|
||||
id
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
}
|
||||
ro_number
|
||||
est_number
|
||||
}
|
||||
id
|
||||
deliver_by
|
||||
parts_order_lines {
|
||||
id
|
||||
db_price
|
||||
act_price
|
||||
line_desc
|
||||
line_remarks
|
||||
oem_partno
|
||||
status
|
||||
}
|
||||
status
|
||||
user_email
|
||||
}
|
||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||
id
|
||||
address1
|
||||
address2
|
||||
city
|
||||
email
|
||||
federal_tax_id
|
||||
state
|
||||
shopname
|
||||
zip_post
|
||||
logo_img_path
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -33,16 +33,17 @@ const errorLink = onError(
|
||||
console.log("Got the new token.", token);
|
||||
window.localStorage.setItem("token", token);
|
||||
|
||||
// const oldHeaders = operation.getContext().headers;
|
||||
// operation.setContext({
|
||||
// headers: {
|
||||
// ...oldHeaders,
|
||||
// authorization: token ? `Bearer ${token}` : ""
|
||||
// }
|
||||
// });
|
||||
const oldHeaders = operation.getContext().headers;
|
||||
operation.setContext({
|
||||
headers: {
|
||||
...oldHeaders,
|
||||
authorization: token ? `Bearer ${token}` : ""
|
||||
}
|
||||
});
|
||||
console.log(operation.getContext());
|
||||
// console.log("forward", forward);
|
||||
// console.log("operation", operation);
|
||||
return forward(operation).subscribe();
|
||||
return forward(operation);
|
||||
|
||||
// return new Observable(observer => {
|
||||
// const subscriber = {
|
||||
|
||||
@@ -14,6 +14,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
insurance_vendor_id
|
||||
logo_img_path
|
||||
md_ro_statuses
|
||||
md_order_statuses
|
||||
shopname
|
||||
state
|
||||
state_tax_id
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const QUERY_ALL_OPEN_JOBS = gql`
|
||||
query QUERY_ALL_OPEN_JOBS {
|
||||
jobs {
|
||||
export const QUERY_ALL_ACTIVE_JOBS = gql`
|
||||
query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!) {
|
||||
jobs(where: { status: { _in: $statuses } }) {
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
@@ -44,7 +44,6 @@ export const QUERY_ALL_OPEN_JOBS = gql`
|
||||
scheduled_delivery
|
||||
status
|
||||
updated_at
|
||||
clm_total
|
||||
ded_amt
|
||||
vehicle {
|
||||
id
|
||||
@@ -108,6 +107,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
converted
|
||||
est_number
|
||||
ro_number
|
||||
clm_total
|
||||
vehicle {
|
||||
id
|
||||
plate_no
|
||||
|
||||
@@ -2,7 +2,7 @@ import { gql } from "apollo-boost";
|
||||
|
||||
export const QUERY_SEARCH_OWNER_BY_IDX = gql`
|
||||
query QUERY_SEARCH_OWNER_BY_IDX($search: String!) {
|
||||
search_owners(args: { search: $search }) {
|
||||
search_owner(args: { search: $search }) {
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_ph1
|
||||
|
||||
11
client/src/graphql/parts-orders.queries.js
Normal file
11
client/src/graphql/parts-orders.queries.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const INSERT_NEW_PARTS_ORDERS = gql`
|
||||
mutation INSERT_NEW_PARTS_ORDERS($po: [parts_orders_insert_input!]!) {
|
||||
insert_parts_orders(objects: $po) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
78
client/src/graphql/vendors.queries.js
Normal file
78
client/src/graphql/vendors.queries.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const QUERY_VENDOR_BY_ID = gql`
|
||||
query QUERY_VENDOR_BY_ID($id: uuid!) {
|
||||
vendors_by_pk(id: $id) {
|
||||
zip
|
||||
terms
|
||||
taxid
|
||||
street2
|
||||
state
|
||||
prompt_discount
|
||||
name
|
||||
id
|
||||
favorite
|
||||
email
|
||||
due_date
|
||||
display_name
|
||||
discount
|
||||
country
|
||||
cost_center
|
||||
city
|
||||
street1
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_VENDOR = gql`
|
||||
mutation UPDATE_VENDOR($id: uuid!, $vendor: vendors_set_input!) {
|
||||
update_vendors(where: { id: { _eq: $id } }, _set: $vendor) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_VENDORS = gql`
|
||||
query QUERY_ALL_VENDORS {
|
||||
vendors {
|
||||
name
|
||||
id
|
||||
street1
|
||||
cost_center
|
||||
city
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const INSERT_NEW_VENDOR = gql`
|
||||
mutation INSERT_NEW_VENDOR($vendorInput: [vendors_insert_input!]!) {
|
||||
insert_vendors(objects: $vendorInput) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_VENDOR = gql`
|
||||
mutation DELETE_VENDOR($id: uuid!) {
|
||||
delete_vendors(where: { id: { _eq: $id } }) {
|
||||
returning {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
|
||||
query QUERY_ALL_VENDORS_FOR_ORDER {
|
||||
vendors(order_by: { favorite: desc }) {
|
||||
name
|
||||
cost_center
|
||||
id
|
||||
favorite
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -13,7 +13,7 @@ export default function JobsAvailablePageComponent({
|
||||
deleteJob={deleteJob}
|
||||
estDataLazyLoad={estDataLazyLoad}
|
||||
/>
|
||||
Available Supplements (//TODO: LOGIC HAS NOT YET BEEN COPIED FROM OTHER COMPONENT)
|
||||
Available Supplements (//TODO LOGIC HAS NOT YET BEEN COPIED FROM OTHER COMPONENT)
|
||||
<JobsAvailableSupplementContainer
|
||||
deleteJob={deleteJob}
|
||||
estDataLazyLoad={estDataLazyLoad}
|
||||
|
||||
@@ -36,8 +36,7 @@ function JobsDetailPageContainer({ match, form }) {
|
||||
refetch();
|
||||
})
|
||||
.catch(error => {
|
||||
//TODO Error handling.
|
||||
console.log("error", error);
|
||||
notification[error]({ message: t("jobs.errors.saving") });
|
||||
});
|
||||
};
|
||||
|
||||
@@ -70,7 +69,6 @@ function JobsDetailPageContainer({ match, form }) {
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.savetitle")
|
||||
});
|
||||
//TODO: Better way to reset the field decorators?
|
||||
refetch().then(r => form.resetFields());
|
||||
});
|
||||
}
|
||||
@@ -78,7 +76,7 @@ function JobsDetailPageContainer({ match, form }) {
|
||||
};
|
||||
|
||||
if (loading) return <SpinComponent />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
|
||||
return data.jobs_by_pk ? (
|
||||
<JobDetailFormContext.Provider value={form}>
|
||||
@@ -94,7 +92,7 @@ function JobsDetailPageContainer({ match, form }) {
|
||||
/>
|
||||
</JobDetailFormContext.Provider>
|
||||
) : (
|
||||
<AlertComponent message={t("jobs.errors.noaccess")} type="error" />
|
||||
<AlertComponent message={t("jobs.errors.noaccess")} type='error' />
|
||||
);
|
||||
}
|
||||
export default Form.create({ name: "JobsDetailPageContainer" })(
|
||||
|
||||
@@ -4,12 +4,25 @@ import { useTranslation } from "react-i18next";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
|
||||
import JobsList from "../../components/jobs-list/jobs-list.component";
|
||||
import { QUERY_ALL_OPEN_JOBS } from "../../graphql/jobs.queries";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
|
||||
//TODO: Implement pagination for this.
|
||||
export default function JobsPage({ match, location }) {
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_OPEN_JOBS, {
|
||||
fetchPolicy: "network-only"
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(function JobsPage({ match, location, bodyshop }) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
fetchPolicy: "network-only",
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.open_statuses || ["Open"]
|
||||
}
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -19,17 +32,53 @@ export default function JobsPage({ match, location }) {
|
||||
|
||||
const { hash } = location;
|
||||
const [selectedJob, setSelectedJob] = useState(hash ? hash.substr(1) : null);
|
||||
const searchTextState = useState("");
|
||||
const searchText = searchTextState[0];
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
//TODO Implement pagination for this.
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
console.log(typeof searchText);
|
||||
return (
|
||||
<div>
|
||||
<JobsList
|
||||
searchTextState={searchTextState}
|
||||
refetch={refetch}
|
||||
loading={loading}
|
||||
selectedJob={selectedJob}
|
||||
setSelectedJob={setSelectedJob}
|
||||
jobs={data ? data.jobs : null}
|
||||
jobs={
|
||||
data
|
||||
? searchTextState[0] === ""
|
||||
? data.jobs
|
||||
: data.jobs.filter(
|
||||
j =>
|
||||
(j.ro_number || "")
|
||||
.toString()
|
||||
.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.vehicle.plate_no || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.vehicle.v_model_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase()) ||
|
||||
(j.vehicle.v_make_desc || "")
|
||||
.toLowerCase()
|
||||
.includes(searchText.toLowerCase())
|
||||
)
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<JobDetailCards selectedJob={selectedJob} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import React from "react";
|
||||
import { Typography } from "antd";
|
||||
import { Typography, Layout } from "antd";
|
||||
|
||||
import HeaderContainer from "../../components/header/header.container";
|
||||
|
||||
export default function LandingPage() {
|
||||
const { Header, Content } = Layout;
|
||||
return (
|
||||
<div>
|
||||
<HeaderContainer landingHeader />
|
||||
<Typography.Title>Welcome to bodyshop.app.</Typography.Title>
|
||||
</div>
|
||||
<Layout style={{ minHeight: "100vh" }}>
|
||||
<Header>
|
||||
<HeaderContainer landingHeader />
|
||||
</Header>
|
||||
|
||||
<Content className="content-container" style={{ padding: "0em 4em 4em" }}>
|
||||
<Typography.Title>Welcome to bodyshop.app.</Typography.Title>
|
||||
</Content>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
import React from 'react'
|
||||
import React from "react";
|
||||
import SendEmailButton from "../../components/send-email-button/send-email-button.container";
|
||||
import PartsOrderEmail from "../../emails/parts-order/parts-order.email";
|
||||
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../emails/parts-order/parts-order.query";
|
||||
|
||||
export default function ManageRootPageComponent() {
|
||||
return (
|
||||
<div>
|
||||
Temporary Home Page.
|
||||
</div>
|
||||
)
|
||||
//const client = useApolloClient();
|
||||
return (
|
||||
<div>
|
||||
<SendEmailButton
|
||||
MessageOptions={{
|
||||
from: {
|
||||
name: "Kavia"
|
||||
},
|
||||
to: "patrickwf@gmail.com",
|
||||
replyTo: "patrickwf@gmail.com"
|
||||
}}
|
||||
Template={PartsOrderEmail}
|
||||
QueryConfig={[
|
||||
REPORT_QUERY_PARTS_ORDER_BY_PK,
|
||||
{
|
||||
variables: { id: "ebe0fb6b-6ec4-4ae0-8fdc-49bdf1e37ff3" }
|
||||
}
|
||||
]}>
|
||||
Send an Email in new Window
|
||||
</SendEmailButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import { createStructuredSelector } from "reselect";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
|
||||
import { setBodyshop } from "../../redux/user/user.actions";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser
|
||||
} from "../../redux/user/user.selectors";
|
||||
import ManagePage from "./manage.page";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -33,12 +36,10 @@ export default connect(
|
||||
|
||||
useEffect(() => {
|
||||
if (data) setBodyshop(data.bodyshops[0]);
|
||||
return () => {
|
||||
//
|
||||
};
|
||||
return () => {};
|
||||
}, [data, setBodyshop]);
|
||||
|
||||
//TODO Translate later.
|
||||
if (!bodyshop) return <LoadingSpinner message="Loading bodyshop data." />;
|
||||
if (!bodyshop)
|
||||
return <LoadingSpinner message={t("general.labels.loadingshop")} />;
|
||||
return <ManagePage match={match} />;
|
||||
});
|
||||
|
||||
@@ -40,6 +40,9 @@ const OwnersDetailContainer = lazy(() =>
|
||||
import("../owners-detail/owners-detail.page.container")
|
||||
);
|
||||
const ShopPage = lazy(() => import("../shop/shop.page.component"));
|
||||
const ShopVendorPageContainer = lazy(() =>
|
||||
import("../shop-vendor/shop-vendor.page.container")
|
||||
);
|
||||
|
||||
const { Header, Content, Footer } = Layout;
|
||||
|
||||
@@ -57,15 +60,13 @@ export default function Manage({ match }) {
|
||||
</Header>
|
||||
<Layout>
|
||||
<Content
|
||||
className="content-container"
|
||||
style={{ padding: "0em 4em 4em" }}
|
||||
>
|
||||
className='content-container'
|
||||
style={{ padding: "0em 4em 4em" }}>
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingSpinner message={t("general.labels.loadingapp")} />
|
||||
}
|
||||
>
|
||||
}>
|
||||
<Route exact path={`${match.path}`} component={ManageRootPage} />
|
||||
|
||||
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
|
||||
@@ -118,6 +119,11 @@ export default function Manage({ match }) {
|
||||
/>
|
||||
|
||||
<Route exact path={`${match.path}/shop/`} component={ShopPage} />
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/shop/vendors`}
|
||||
component={ShopVendorPageContainer}
|
||||
/>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</Content>
|
||||
|
||||
15
client/src/pages/shop-vendor/shop-vendor.page.component.jsx
generated
Normal file
15
client/src/pages/shop-vendor/shop-vendor.page.component.jsx
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import VendorsListContainer from "../../components/vendors-list/vendors-list.container";
|
||||
import VendorsFormContainer from "../../components/vendors-form/vendors-form.container";
|
||||
|
||||
export default function ShopVendorPageComponent({ selectedVendorState }) {
|
||||
//TODO Figure out how to handle the refresh list when saving form
|
||||
return (
|
||||
<div>
|
||||
<VendorsListContainer selectedVendorState={selectedVendorState} />
|
||||
<VendorsFormContainer
|
||||
vendorId={selectedVendorState[0] ? selectedVendorState[0].id : null}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
client/src/pages/shop-vendor/shop-vendor.page.container.jsx
Normal file
17
client/src/pages/shop-vendor/shop-vendor.page.container.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ShopVendorPageComponent from "./shop-vendor.page.component";
|
||||
|
||||
export default function ShopVendorPageContainer() {
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
document.title = t("titles.shop_vendors");
|
||||
}, [t]);
|
||||
|
||||
const selectedVendorState = useState();
|
||||
return (
|
||||
<div>
|
||||
<ShopVendorPageComponent selectedVendorState={selectedVendorState} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export function* updateUserDetails(userDetails) {
|
||||
yield put(updateUserDetailsSuccess(userDetails.payload));
|
||||
} catch (error) {
|
||||
//yield put(signOutFailure(error.message));
|
||||
//TODO: error handling
|
||||
//TODO error handling
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,9 +106,12 @@
|
||||
"in": "In",
|
||||
"loading": "Loading...",
|
||||
"loadingapp": "Loading Bodyshop.app",
|
||||
"loadingshop": "Loading shop data...",
|
||||
"loggingin": "Logging you in...",
|
||||
"na": "N/A",
|
||||
"out": "Out",
|
||||
"save": "Save",
|
||||
"search": "Search...",
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"languages": {
|
||||
@@ -120,6 +123,7 @@
|
||||
"unsavedchanges": "You have unsaved changes."
|
||||
},
|
||||
"validation": {
|
||||
"invalidemail": "Please enter a valid email.",
|
||||
"required": "This field is required. "
|
||||
}
|
||||
},
|
||||
@@ -301,6 +305,8 @@
|
||||
"owners": "Owners",
|
||||
"schedule": "Schedule",
|
||||
"shop": "My Shop",
|
||||
"shop_config": "Configuration",
|
||||
"shop_vendors": "Vendors",
|
||||
"vehicles": "Vehicles"
|
||||
},
|
||||
"jobsdetail": {
|
||||
@@ -369,6 +375,26 @@
|
||||
"save": "Owner saved successfully."
|
||||
}
|
||||
},
|
||||
"parts": {
|
||||
"actions": {
|
||||
"order": "Order Parts"
|
||||
}
|
||||
},
|
||||
"parts_orders": {
|
||||
"errors": {
|
||||
"creating": "Error encountered when creating parts order. "
|
||||
},
|
||||
"fields": {
|
||||
"deliver_by": "Deliver By"
|
||||
},
|
||||
"labels": {
|
||||
"email": "Send by Email",
|
||||
"print": "Show Printed Form"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Parts order created successfully. "
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"errors": {
|
||||
"state": "Error reading page state. Please refresh."
|
||||
@@ -385,6 +411,7 @@
|
||||
"profile": "My Profile | $t(titles.app)",
|
||||
"schedule": "Schedule | $t(titles.app)",
|
||||
"shop": "My Shop | $t(titles.app)",
|
||||
"shop_vendors": "Vendors | $t(titles.app)",
|
||||
"vehicledetail": "Vehicle Details {{vehicle}} | $t(titles.app)",
|
||||
"vehicles": "All Vehicles | $t(titles.app)"
|
||||
},
|
||||
@@ -430,6 +457,41 @@
|
||||
"successes": {
|
||||
"save": "Vehicle saved successfully."
|
||||
}
|
||||
},
|
||||
"vendors": {
|
||||
"actions": {
|
||||
"new": "New Vendor"
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "Error encountered while deleting vendor. ",
|
||||
"saving": "Error encountered while saving vendor. "
|
||||
},
|
||||
"fields": {
|
||||
"city": "City",
|
||||
"cost_center": "Cost Center",
|
||||
"country": "Country",
|
||||
"discount": "Discount %",
|
||||
"display_name": "Display Name",
|
||||
"due_date": "Payment Due Date",
|
||||
"email": "Contact Email",
|
||||
"favorite": "Favorite?",
|
||||
"name": "Vendor Name",
|
||||
"prompt_discount": "Prompt Discount %",
|
||||
"state": "State/Province",
|
||||
"street1": "Street",
|
||||
"street2": "Address 2",
|
||||
"taxid": "Tax ID",
|
||||
"terms": "Payment Terms",
|
||||
"zip": "Zip/Postal Code"
|
||||
},
|
||||
"labels": {
|
||||
"noneselected": "No vendor is selected.",
|
||||
"search": "Type a Vendor's Name"
|
||||
},
|
||||
"successes": {
|
||||
"deleted": "Vendor deleted successfully. ",
|
||||
"saved": "Vendor saved successfully."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +106,12 @@
|
||||
"in": "en",
|
||||
"loading": "Cargando...",
|
||||
"loadingapp": "Cargando Bodyshop.app",
|
||||
"loadingshop": "Cargando datos de la tienda ...",
|
||||
"loggingin": "Iniciando sesión ...",
|
||||
"na": "N / A",
|
||||
"out": "Afuera",
|
||||
"save": "Salvar",
|
||||
"search": "Buscar...",
|
||||
"unknown": "Desconocido"
|
||||
},
|
||||
"languages": {
|
||||
@@ -120,6 +123,7 @@
|
||||
"unsavedchanges": "Usted tiene cambios no guardados."
|
||||
},
|
||||
"validation": {
|
||||
"invalidemail": "Por favor introduzca una dirección de correo electrónico válida.",
|
||||
"required": "Este campo es requerido."
|
||||
}
|
||||
},
|
||||
@@ -301,6 +305,8 @@
|
||||
"owners": "propietarios",
|
||||
"schedule": "Programar",
|
||||
"shop": "Mi tienda",
|
||||
"shop_config": "Configuración",
|
||||
"shop_vendors": "Vendedores",
|
||||
"vehicles": "Vehículos"
|
||||
},
|
||||
"jobsdetail": {
|
||||
@@ -369,6 +375,26 @@
|
||||
"save": "Propietario guardado con éxito."
|
||||
}
|
||||
},
|
||||
"parts": {
|
||||
"actions": {
|
||||
"order": "Pedido de piezas"
|
||||
}
|
||||
},
|
||||
"parts_orders": {
|
||||
"errors": {
|
||||
"creating": "Se encontró un error al crear el pedido de piezas."
|
||||
},
|
||||
"fields": {
|
||||
"deliver_by": "Entregado por"
|
||||
},
|
||||
"labels": {
|
||||
"email": "Enviar por correo electrónico",
|
||||
"print": "Mostrar formulario impreso"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Pedido de piezas creado con éxito."
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"errors": {
|
||||
"state": "Error al leer el estado de la página. Porfavor refresca."
|
||||
@@ -385,6 +411,7 @@
|
||||
"profile": "Mi perfil | $t(titles.app)",
|
||||
"schedule": "Horario | $t(titles.app)",
|
||||
"shop": "Mi tienda | $t(titles.app)",
|
||||
"shop_vendors": "Vendedores | $t(titles.app)",
|
||||
"vehicledetail": "Detalles del vehículo {{vehicle}} | $t(titles.app)",
|
||||
"vehicles": "Todos los vehiculos | $t(titles.app)"
|
||||
},
|
||||
@@ -430,6 +457,41 @@
|
||||
"successes": {
|
||||
"save": "Vehículo guardado con éxito."
|
||||
}
|
||||
},
|
||||
"vendors": {
|
||||
"actions": {
|
||||
"new": "Nuevo vendedor"
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "Se encontró un error al eliminar el proveedor.",
|
||||
"saving": "Se encontró un error al guardar el proveedor."
|
||||
},
|
||||
"fields": {
|
||||
"city": "ciudad",
|
||||
"cost_center": "Centro de costos",
|
||||
"country": "País",
|
||||
"discount": "% De descuento",
|
||||
"display_name": "Nombre para mostrar",
|
||||
"due_date": "Fecha de vencimiento del pago",
|
||||
"email": "Email de contacto",
|
||||
"favorite": "¿Favorito?",
|
||||
"name": "Nombre del vendedor",
|
||||
"prompt_discount": "Descuento pronto",
|
||||
"state": "Provincia del estado",
|
||||
"street1": "calle",
|
||||
"street2": "Dirección 2",
|
||||
"taxid": "Identificación del impuesto",
|
||||
"terms": "Términos de pago",
|
||||
"zip": "código postal"
|
||||
},
|
||||
"labels": {
|
||||
"noneselected": "Ningún vendedor está seleccionado.",
|
||||
"search": "Escriba el nombre de un proveedor"
|
||||
},
|
||||
"successes": {
|
||||
"deleted": "Proveedor eliminado correctamente.",
|
||||
"saved": "Proveedor guardado con éxito."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +106,12 @@
|
||||
"in": "dans",
|
||||
"loading": "Chargement...",
|
||||
"loadingapp": "Chargement de Bodyshop.app",
|
||||
"loadingshop": "Chargement des données de la boutique ...",
|
||||
"loggingin": "Vous connecter ...",
|
||||
"na": "N / A",
|
||||
"out": "En dehors",
|
||||
"save": "sauvegarder",
|
||||
"search": "Chercher...",
|
||||
"unknown": "Inconnu"
|
||||
},
|
||||
"languages": {
|
||||
@@ -120,6 +123,7 @@
|
||||
"unsavedchanges": "Vous avez des changements non enregistrés."
|
||||
},
|
||||
"validation": {
|
||||
"invalidemail": "S'il vous plaît entrer un email valide.",
|
||||
"required": "Ce champ est requis."
|
||||
}
|
||||
},
|
||||
@@ -301,6 +305,8 @@
|
||||
"owners": "Propriétaires",
|
||||
"schedule": "Programme",
|
||||
"shop": "Mon magasin",
|
||||
"shop_config": "Configuration",
|
||||
"shop_vendors": "Vendeurs",
|
||||
"vehicles": "Véhicules"
|
||||
},
|
||||
"jobsdetail": {
|
||||
@@ -369,6 +375,26 @@
|
||||
"save": "Le propriétaire a bien enregistré."
|
||||
}
|
||||
},
|
||||
"parts": {
|
||||
"actions": {
|
||||
"order": "Commander des pièces"
|
||||
}
|
||||
},
|
||||
"parts_orders": {
|
||||
"errors": {
|
||||
"creating": "Erreur rencontrée lors de la création de la commande de pièces."
|
||||
},
|
||||
"fields": {
|
||||
"deliver_by": "Livrer par"
|
||||
},
|
||||
"labels": {
|
||||
"email": "Envoyé par email",
|
||||
"print": "Afficher le formulaire imprimé"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Commande de pièces créée avec succès."
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
"errors": {
|
||||
"state": "Erreur lors de la lecture de l'état de la page. Rafraichissez, s'il vous plait."
|
||||
@@ -385,6 +411,7 @@
|
||||
"profile": "Mon profil | $t(titles.app)",
|
||||
"schedule": "Horaire | $t(titles.app)",
|
||||
"shop": "Mon magasin | $t(titles.app)",
|
||||
"shop_vendors": "Vendeurs | $t(titles.app)",
|
||||
"vehicledetail": "Détails du véhicule {{vehicle} | $t(titles.app)",
|
||||
"vehicles": "Tous les véhicules | $t(titles.app)"
|
||||
},
|
||||
@@ -430,6 +457,41 @@
|
||||
"successes": {
|
||||
"save": "Le véhicule a été enregistré avec succès."
|
||||
}
|
||||
},
|
||||
"vendors": {
|
||||
"actions": {
|
||||
"new": "Nouveau vendeur"
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "Erreur rencontrée lors de la suppression du fournisseur.",
|
||||
"saving": "Erreur rencontrée lors de l'enregistrement du fournisseur."
|
||||
},
|
||||
"fields": {
|
||||
"city": "Ville",
|
||||
"cost_center": "Centre de coûts",
|
||||
"country": "Pays",
|
||||
"discount": "Remise %",
|
||||
"display_name": "Afficher un nom",
|
||||
"due_date": "Date limite de paiement",
|
||||
"email": "Email du contact",
|
||||
"favorite": "Préféré?",
|
||||
"name": "Nom du vendeur",
|
||||
"prompt_discount": "Remise rapide%",
|
||||
"state": "Etat / Province",
|
||||
"street1": "rue",
|
||||
"street2": "Adresse 2 ",
|
||||
"taxid": "Identifiant de taxe",
|
||||
"terms": "Modalités de paiement",
|
||||
"zip": "Zip / code postal"
|
||||
},
|
||||
"labels": {
|
||||
"noneselected": "Aucun fournisseur n'est sélectionné.",
|
||||
"search": "Tapez le nom d'un vendeur"
|
||||
},
|
||||
"successes": {
|
||||
"deleted": "Le fournisseur a bien été supprimé.",
|
||||
"saved": "Le fournisseur a bien enregistré."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export function alphaSort(a, b) {
|
||||
A = a ? a.toLowerCase() : "";
|
||||
|
||||
B = b ? b.toLowerCase() : "";
|
||||
console.log("Objects", A, B, A < B, A > B);
|
||||
|
||||
if (A < B)
|
||||
//sort string ascending
|
||||
|
||||
242
client/yarn.lock
242
client/yarn.lock
@@ -967,6 +967,18 @@
|
||||
lodash "^4.17.13"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@ckeditor/ckeditor5-build-classic@^16.0.0":
|
||||
version "16.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-build-classic/-/ckeditor5-build-classic-16.0.0.tgz#9141e94ea6765eda4925aaf062448507410bbe70"
|
||||
integrity sha512-gBfZqWg3hmCvhq6/wX5UJp4qwl6gB+NJPpOkya2Y3jWKA0HKf3UdlhIvaHq7dRaqhi4unmqaJZVEk5VpZ1vDOg==
|
||||
|
||||
"@ckeditor/ckeditor5-react@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@ckeditor/ckeditor5-react/-/ckeditor5-react-2.1.0.tgz#f612546a5a328899a436d71b72ffd632600049e8"
|
||||
integrity sha512-rlHjRKhwP9tNK0Yj2UJShL14mRfxLPgJ+Pv6zTv/Mvmd4wrwGnJf+5ybOAGK92S02hP1cXH+9km+PRO1b4V+ng==
|
||||
dependencies:
|
||||
prop-types "^15.6.1"
|
||||
|
||||
"@cnakazawa/watch@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.3.tgz#099139eaec7ebf07a27c1786a3ff64f39464d2ef"
|
||||
@@ -1668,6 +1680,14 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/body-parser@*":
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f"
|
||||
integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==
|
||||
dependencies:
|
||||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/bytebuffer@^5.0.40":
|
||||
version "5.0.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.40.tgz#d6faac40dcfb09cd856cdc4c01d3690ba536d3ee"
|
||||
@@ -1681,6 +1701,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@types/connect@*":
|
||||
version "3.4.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546"
|
||||
integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/eslint-visitor-keys@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
@@ -1691,6 +1718,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||
integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
|
||||
|
||||
"@types/express-serve-static-core@*":
|
||||
version "4.17.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz#f6f41fa35d42e79dbf6610eccbb2637e6008a0cf"
|
||||
integrity sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/range-parser" "*"
|
||||
|
||||
"@types/express@^4.17.2":
|
||||
version "4.17.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.2.tgz#a0fb7a23d8855bac31bc01d5a58cadd9b2173e6c"
|
||||
integrity sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==
|
||||
dependencies:
|
||||
"@types/body-parser" "*"
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/glob@^7.1.1":
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575"
|
||||
@@ -1730,6 +1774,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
|
||||
|
||||
"@types/mime@*":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d"
|
||||
integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
@@ -1760,6 +1809,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||
|
||||
"@types/range-parser@*":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c"
|
||||
integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
|
||||
|
||||
"@types/react-slick@^0.23.4":
|
||||
version "0.23.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-slick/-/react-slick-0.23.4.tgz#c97e2a9e7e3d1933c68593b8e82752fab1e8ce53"
|
||||
@@ -1775,6 +1829,14 @@
|
||||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/serve-static@*":
|
||||
version "1.13.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1"
|
||||
integrity sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==
|
||||
dependencies:
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/mime" "*"
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
@@ -2518,7 +2580,7 @@ arrify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
asap@~2.0.3, asap@~2.0.6:
|
||||
asap@^2.0.0, asap@~2.0.3, asap@~2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
@@ -3051,6 +3113,11 @@ bser@2.1.1:
|
||||
dependencies:
|
||||
node-int64 "^0.4.0"
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
@@ -4148,6 +4215,11 @@ date-arithmetic@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/date-arithmetic/-/date-arithmetic-4.1.0.tgz#e5d6434e9deb71f79760a37b729e4a515e730ddf"
|
||||
integrity sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==
|
||||
|
||||
dayjs@^1.8.20:
|
||||
version "1.8.20"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.20.tgz#724a5cb6ad1f6fc066b0bd9a800dedcc7886f19e"
|
||||
integrity sha512-mH0MCDxw6UCGJYxVN78h8ugWycZAO8thkj3bW6vApL5tS0hQplIDdAQcmbvl7n35H0AKdCJQaArTrIQw2xt4Qg==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
@@ -4535,6 +4607,13 @@ ecc-jsbn@~0.1.1:
|
||||
jsbn "~0.1.0"
|
||||
safer-buffer "^2.1.0"
|
||||
|
||||
ecdsa-sig-formatter@1.0.11:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
|
||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
@@ -5798,7 +5877,7 @@ har-schema@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
||||
|
||||
har-validator@~5.1.0:
|
||||
har-validator@~5.1.0, har-validator@~5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
|
||||
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
|
||||
@@ -7320,6 +7399,22 @@ jsonify@~0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
|
||||
integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=
|
||||
|
||||
jsonwebtoken@^8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
dependencies:
|
||||
jws "^3.2.2"
|
||||
lodash.includes "^4.3.0"
|
||||
lodash.isboolean "^3.0.3"
|
||||
lodash.isinteger "^4.0.4"
|
||||
lodash.isnumber "^3.0.3"
|
||||
lodash.isplainobject "^4.0.6"
|
||||
lodash.isstring "^4.0.1"
|
||||
lodash.once "^4.0.0"
|
||||
ms "^2.1.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
|
||||
@@ -7338,6 +7433,23 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3:
|
||||
array-includes "^3.0.3"
|
||||
object.assign "^4.1.0"
|
||||
|
||||
jwa@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
|
||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
|
||||
dependencies:
|
||||
buffer-equal-constant-time "1.0.1"
|
||||
ecdsa-sig-formatter "1.0.11"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
jws@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
|
||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
|
||||
dependencies:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
@@ -7552,16 +7664,51 @@ lodash.flattendeep@^4.4.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
|
||||
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
|
||||
|
||||
lodash.includes@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||
|
||||
lodash.isboolean@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.isinteger@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||
|
||||
lodash.isnumber@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||
|
||||
lodash.isplainobject@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||
|
||||
lodash.isstring@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||
|
||||
lodash.memoize@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.once@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
@@ -8980,6 +9127,11 @@ pnp-webpack-plugin@1.6.0:
|
||||
dependencies:
|
||||
ts-pnp "^1.1.2"
|
||||
|
||||
pop-iterate@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pop-iterate/-/pop-iterate-1.0.1.tgz#ceacfdab4abf353d7a0f2aaa2c1fc7b3f9413ba3"
|
||||
integrity sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=
|
||||
|
||||
popper.js@^1.15.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
|
||||
@@ -9755,7 +9907,7 @@ prop-types-exact@^1.2.0:
|
||||
object.assign "^4.1.0"
|
||||
reflect.ownkeys "^0.2.0"
|
||||
|
||||
prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
|
||||
version "15.7.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||
@@ -9868,6 +10020,15 @@ punycode@^2.1.0, punycode@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
q@2.0.x:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-2.0.3.tgz#75b8db0255a1a5af82f58c3f3aaa1efec7d0d134"
|
||||
integrity sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=
|
||||
dependencies:
|
||||
asap "^2.0.0"
|
||||
pop-iterate "^1.0.1"
|
||||
weak-map "^1.0.5"
|
||||
|
||||
q@^1.1.2:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
@@ -10499,6 +10660,13 @@ react-error-overlay@^6.0.5:
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.5.tgz#55d59c2a3810e8b41922e0b4e5f85dcf239bd533"
|
||||
integrity sha512-+DMR2k5c6BqMDSMF8hLH0vYKtKTeikiFW+fj0LClN+XZg4N9b8QUAdHC62CGWNLTi/gnuuemNcNcTFrCvK1f+A==
|
||||
|
||||
react-html-email@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-html-email/-/react-html-email-3.0.0.tgz#a5a51b78271a33daf9b14638e9e4c5df93f691aa"
|
||||
integrity sha512-eIBmZjqoS1SLAOi/QYTHLnZSmBM5AhP/cScJGhVDHlBJFtMyiR4qBUQYQDCqRtNPDKHWGMbbS/+XHSgD+C2hng==
|
||||
dependencies:
|
||||
prop-types "^15.5.10"
|
||||
|
||||
react-i18next@^11.3.1:
|
||||
version "11.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.3.1.tgz#9269282c3f566015f0bdf8fdbf46782bbe50f5a7"
|
||||
@@ -11002,6 +11170,32 @@ request@^2.87.0, request@^2.88.0:
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
request@^2.88.2:
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
caseless "~0.12.0"
|
||||
combined-stream "~1.0.6"
|
||||
extend "~3.0.2"
|
||||
forever-agent "~0.6.1"
|
||||
form-data "~2.3.2"
|
||||
har-validator "~5.1.3"
|
||||
http-signature "~1.2.0"
|
||||
is-typedarray "~1.0.0"
|
||||
isstream "~0.1.2"
|
||||
json-stringify-safe "~5.0.1"
|
||||
mime-types "~2.1.19"
|
||||
oauth-sign "~0.9.0"
|
||||
performance-now "^2.1.0"
|
||||
qs "~6.5.2"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.5.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
@@ -11165,6 +11359,11 @@ rmc-feedback@^2.0.0:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
|
||||
rootpath@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/rootpath/-/rootpath-0.1.2.tgz#5b379a87dca906e9b91d690a599439bef267ea6b"
|
||||
integrity sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=
|
||||
|
||||
rst-selector-parser@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
|
||||
@@ -11196,8 +11395,6 @@ rxjs@^6.5.3:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
|
||||
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
@@ -11299,6 +11496,11 @@ schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6
|
||||
ajv "^6.10.2"
|
||||
ajv-keywords "^3.4.1"
|
||||
|
||||
scmp@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/scmp/-/scmp-2.1.0.tgz#37b8e197c425bdeb570ab91cc356b311a11f9c9a"
|
||||
integrity sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==
|
||||
|
||||
scss-tokenizer@^0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
|
||||
@@ -12275,7 +12477,7 @@ toidentifier@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||
|
||||
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0:
|
||||
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@^2.5.0, tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||
@@ -12351,6 +12553,22 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
twilio@^3.39.5:
|
||||
version "3.39.5"
|
||||
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.39.5.tgz#1512ae1de83441609acf7328a75442c439e6c3af"
|
||||
integrity sha512-IuiHFRuq7e+rXMNRDqwXo16ITefPsPh1reI/rRKFVbvRNRY88LnnN5Qdjyw90UWqWxqMwGqYXt253tAvt1pI3g==
|
||||
dependencies:
|
||||
"@types/express" "^4.17.2"
|
||||
dayjs "^1.8.20"
|
||||
jsonwebtoken "^8.5.1"
|
||||
lodash "^4.17.15"
|
||||
q "2.0.x"
|
||||
request "^2.88.2"
|
||||
rootpath "^0.1.2"
|
||||
scmp "^2.1.0"
|
||||
url-parse "^1.4.7"
|
||||
xmlbuilder "^13.0.2"
|
||||
|
||||
type-check@~0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
|
||||
@@ -12531,7 +12749,7 @@ url-loader@2.3.0:
|
||||
mime "^2.4.4"
|
||||
schema-utils "^2.5.0"
|
||||
|
||||
url-parse@^1.4.3:
|
||||
url-parse@^1.4.3, url-parse@^1.4.7:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
|
||||
@@ -12697,6 +12915,11 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
||||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
weak-map@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/weak-map/-/weak-map-1.0.5.tgz#79691584d98607f5070bd3b70a40e6bb22e401eb"
|
||||
integrity sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
@@ -13108,6 +13331,11 @@ xml-name-validator@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
|
||||
|
||||
xmlbuilder@^13.0.2:
|
||||
version "13.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-13.0.2.tgz#02ae33614b6a047d1c32b5389c1fdacb2bce47a7"
|
||||
integrity sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==
|
||||
|
||||
xmlchars@^2.1.1:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
|
||||
@@ -2,7 +2,7 @@ const functions = require("firebase-functions");
|
||||
const admin = require("firebase-admin");
|
||||
admin.initializeApp(functions.config().firebase);
|
||||
|
||||
//Todo: Move this to an environment parameter.
|
||||
//TODO Move this to an environment parameter.
|
||||
const GRAPHQL_ENDPOINT = functions.config().auth.graphql_endpoint;
|
||||
const HASURA_SECRET_ADMIN_KEY = functions.config().auth.hasura_secret_admin_key;
|
||||
const UPSERT_USER = `
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" DROP COLUMN "cost_center";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ADD COLUMN "cost_center" text NOT NULL;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" DROP COLUMN "favorite";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,4 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ADD COLUMN "favorite" boolean NOT NULL DEFAULT
|
||||
false;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- name
|
||||
- street1
|
||||
- street2
|
||||
- city
|
||||
- state
|
||||
- zip
|
||||
- country
|
||||
- email
|
||||
- taxid
|
||||
- discount
|
||||
- prompt_discount
|
||||
- due_date
|
||||
- terms
|
||||
- display_name
|
||||
- cost_center
|
||||
- favorite
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,43 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- favorite
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- favorite
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,9 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" TYPE boolean;
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" SET NOT NULL;
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: COMMENT ON COLUMN "public"."vendors"."favorite" IS E'null'
|
||||
type: run_sql
|
||||
@@ -0,0 +1,9 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" TYPE bool;
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" DROP NOT NULL;
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: COMMENT ON COLUMN "public"."vendors"."favorite" IS E''
|
||||
type: run_sql
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- id
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- name
|
||||
- street1
|
||||
- street2
|
||||
- city
|
||||
- state
|
||||
- zip
|
||||
- country
|
||||
- email
|
||||
- taxid
|
||||
- discount
|
||||
- prompt_discount
|
||||
- due_date
|
||||
- terms
|
||||
- display_name
|
||||
- cost_center
|
||||
- favorite
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- favorite
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,44 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- favorite
|
||||
- due_date
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,9 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ADD COLUMN "favorite" bool
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" DROP NOT NULL
|
||||
type: run_sql
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ALTER COLUMN "favorite" SET DEFAULT false
|
||||
type: run_sql
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" DROP COLUMN "favorite" CASCADE
|
||||
type: run_sql
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" DROP COLUMN "favorite";
|
||||
type: run_sql
|
||||
@@ -0,0 +1,3 @@
|
||||
- args:
|
||||
sql: ALTER TABLE "public"."vendors" ADD COLUMN "favorite" jsonb NULL;
|
||||
type: run_sql
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- due_date
|
||||
- favorite
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,44 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,45 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_select_permission
|
||||
- args:
|
||||
permission:
|
||||
allow_aggregations: false
|
||||
columns:
|
||||
- due_date
|
||||
- favorite
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
computed_fields: []
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_select_permission
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_update_permission
|
||||
- args:
|
||||
permission:
|
||||
columns:
|
||||
- due_date
|
||||
- favorite
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
filter:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_update_permission
|
||||
@@ -0,0 +1,46 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- bodyshopid
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- created_at
|
||||
- discount
|
||||
- display_name
|
||||
- due_date
|
||||
- email
|
||||
- id
|
||||
- name
|
||||
- prompt_discount
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- updated_at
|
||||
- zip
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
@@ -0,0 +1,47 @@
|
||||
- args:
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: drop_insert_permission
|
||||
- args:
|
||||
permission:
|
||||
check:
|
||||
bodyshop:
|
||||
associations:
|
||||
_and:
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
columns:
|
||||
- due_date
|
||||
- favorite
|
||||
- discount
|
||||
- prompt_discount
|
||||
- city
|
||||
- cost_center
|
||||
- country
|
||||
- display_name
|
||||
- email
|
||||
- name
|
||||
- state
|
||||
- street1
|
||||
- street2
|
||||
- taxid
|
||||
- terms
|
||||
- zip
|
||||
- created_at
|
||||
- updated_at
|
||||
- bodyshopid
|
||||
- id
|
||||
localPresets:
|
||||
- key: ""
|
||||
value: ""
|
||||
set: {}
|
||||
role: user
|
||||
table:
|
||||
name: vendors
|
||||
schema: public
|
||||
type: create_insert_permission
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user