Compare commits

...

65 Commits

Author SHA1 Message Date
Patrick Fic
3e1663bf18 IO-1267 missing query info in detail cards. 2021-07-20 10:36:49 -07:00
Patrick Fic
9a60149d75 IO-1266 attach pdf copy of email. 2021-07-20 10:34:03 -07:00
Patrick Fic
54b483333f Revert "IO-1257 Accept special characters"
This reverts commit a8ad65000d.
2021-07-16 14:42:12 -07:00
Patrick Fic
990ec1a553 IO-1189 IO-1190 IO-1259 Added gen doc keys. 2021-07-16 10:13:50 -07:00
Patrick Fic
6bd49a461e IO-1264 Add CSR reports 2021-07-16 09:25:03 -07:00
Patrick Fic
0d30fc0e32 IO-1219 Improve display of job status. 2021-07-15 13:59:23 -07:00
Patrick Fic
7ce4264309 IO-1260 Remove VIN Unique Key 2021-07-15 11:40:09 -07:00
Patrick Fic
5385e6918b IO-1219 Add status to job search select. 2021-07-14 15:48:27 -07:00
Patrick Fic
a8ad65000d IO-1257 Accept special characters 2021-07-14 14:51:19 -07:00
Patrick Fic
ae9ca0ac3b IO-1258 Update reconciliation on posting bill 2021-07-13 12:30:12 -07:00
Patrick Fic
9fd23e9181 IO-1253 Disable receiving inhouse bills. 2021-07-13 12:14:28 -07:00
Patrick Fic
a288a1a2a4 IO-1250 Mark job as PST Exempt 2021-07-13 11:49:27 -07:00
Patrick Fic
33e2201524 IO-1256 Payments on job missing field & edit from list. 2021-07-13 11:09:15 -07:00
Patrick Fic
34422dfef7 IO-1255 Resolve parts return and posting for non-defined lines 2021-07-13 11:02:54 -07:00
Patrick Fic
c6635845f5 Merged in feature/2021-07-09 (pull request #134)
Removed uneeded imports.

Approved-by: Patrick Fic
2021-07-08 18:46:03 +00:00
Patrick Fic
e770232e1d Removed uneeded imports. 2021-07-08 11:45:43 -07:00
Patrick Fic
51dcf3a7c6 Merged in feature/2021-07-09 (pull request #133)
feature/2021-07-09

Approved-by: Patrick Fic
2021-07-08 18:38:34 +00:00
Patrick Fic
afd745917d IO-1230 remove sort on export logs for ro num 2021-07-08 11:34:39 -07:00
Patrick Fic
aa8e12ef58 IO-1248 IO-1247 Resolve nulls in system & payment search update. 2021-07-08 11:33:02 -07:00
Patrick Fic
41bbda7bcf IO-12424 Delete line to mark as removed. 2021-07-08 11:17:51 -07:00
Patrick Fic
f19289362d IO-1249 Adjust reconciliation window sizing. 2021-07-08 11:12:58 -07:00
Patrick Fic
2d546d92b5 Merged in feature/2021-07-09 (pull request #131)
IO-1245 Change Address 1

Approved-by: Patrick Fic
2021-07-07 23:30:51 +00:00
Patrick Fic
1360a73028 IO-1245 Change Address 1 2021-07-07 16:30:05 -07:00
Patrick Fic
7de224831f Merged in feature/2021-07-09 (pull request #130)
feature/2021-07-09

Approved-by: Patrick Fic
2021-07-07 22:14:01 +00:00
Patrick Fic
e9cda93898 IO-1245 Resolve QB Consistency Issue 2021-07-07 15:04:56 -07:00
Patrick Fic
2c1f5a9f34 IO-1241 Supplement Merge with discarded changes. 2021-07-06 09:45:02 -07:00
Patrick Fic
d88d7ebebd Merged in feature/2021-07-09 (pull request #129)
IO-1239 Resolve extra lines on glass claim export
2021-07-05 17:37:52 +00:00
Patrick Fic
17dcc2efd8 IO-1239 Resolve extra lines on glass claim export 2021-07-05 10:35:45 -07:00
Patrick Fic
3391d7d3f4 Merged in hotfix/2021-07-02 (pull request #127)
IO-1231 Resolve dates not saving on job close.

Approved-by: Patrick Fic
2021-07-02 20:13:40 +00:00
Patrick Fic
bccb5e353b IO-1231 Resolve dates not saving on job close. 2021-07-02 13:13:19 -07:00
Patrick Fic
8e05105917 Merged in hotfix/2021-06-30 (pull request #125)
Hotfix/2021 06 30
2021-06-30 20:46:41 +00:00
Patrick Fic
81babca775 Landing page update. 2021-06-30 13:44:46 -07:00
Patrick Fic
fe8dd2a920 Landing page updates. 2021-06-30 13:44:26 -07:00
Patrick Fic
11af41f3c0 Merged in hotfix/2021-06-30 (pull request #123)
hotfix/2021-06-30

Approved-by: Patrick Fic
2021-06-30 20:05:17 +00:00
Patrick Fic
6a24c10225 Update loading page. 2021-06-29 14:46:33 -07:00
Patrick Fic
8c50589eba Merged in hotfix/2021-06-30 (pull request #122)
Add crisp changes & auto triggers.

Approved-by: Patrick Fic
2021-06-29 20:47:08 +00:00
Patrick Fic
eaa134d474 Add crisp changes & auto triggers. 2021-06-29 13:45:05 -07:00
Patrick Fic
3d61d95e44 Merged in hotfix/2021-06-30 (pull request #121)
hotfix/2021-06-30

Approved-by: Patrick Fic
2021-06-29 20:16:33 +00:00
Patrick Fic
d9d3c899a1 Improved Landing page 2021-06-29 13:12:53 -07:00
Patrick Fic
00f71eba77 Added landing page. 2021-06-29 07:09:57 -07:00
Patrick Fic
1e547f1815 Upsize global search bar. 2021-06-28 17:22:41 -07:00
Patrick Fic
4b7bbe686a IO-1222 Add preview to schedule view. 2021-06-28 13:38:09 -07:00
Patrick Fic
f744acd131 IO-1217 Allow remove and add production always 2021-06-28 10:51:27 -07:00
Patrick Fic
2be61379f8 Merged in feature/2021-06-25 (pull request #120)
feature/2021-06-25
2021-06-25 21:38:25 +00:00
Patrick Fic
227a1034cd Merged in feature/2021-06-25 (pull request #119)
Remove multi print center.

Approved-by: Patrick Fic
2021-06-25 21:12:04 +00:00
Patrick Fic
33f863e1e6 Merged in feature/2021-06-25 (pull request #118)
Added missing template.

Approved-by: Patrick Fic
2021-06-25 19:07:29 +00:00
Patrick Fic
4e161248b3 Merged in feature/2021-06-25 (pull request #117)
feature/2021-06-25
2021-06-25 14:46:53 +00:00
Patrick Fic
749dfc0fba Merged in feature/2021-06-25 (pull request #116)
feature/2021-06-25

Approved-by: Patrick Fic
2021-06-22 20:47:17 +00:00
Patrick Fic
ed00b4550c Merged in feature/2021-06-18 (pull request #114)
RO form items for checklist dates.

Approved-by: Patrick Fic
2021-06-18 20:50:42 +00:00
Patrick Fic
471c918ac3 Merged in feature/2021-06-18 (pull request #113)
Add delivery to checklist & remove jria submit on error.

Approved-by: Patrick Fic
2021-06-18 18:24:07 +00:00
Patrick Fic
0c23c16f3b Merged in feature/2021-06-18 (pull request #112)
feature/2021-06-18

Approved-by: Patrick Fic
2021-06-17 17:39:00 +00:00
Patrick Fic
0adb34b4d3 Merged in feature/2021-06-18 (pull request #111)
Feature/2021 06 18
2021-06-16 21:25:18 +00:00
Patrick Fic
859522b028 Merged in feature/2021-06-18 (pull request #110)
Feature/2021 06 18
2021-06-16 19:09:02 +00:00
Patrick Fic
e6cb804055 Merged in feature/2021-06-18 (pull request #109)
Feature/2021 06 18
2021-06-15 23:42:24 +00:00
Patrick Fic
a218564a24 Merged in feature/2021-06-18 (pull request #108)
Feature/2021 06 18
2021-06-15 02:38:39 +00:00
Patrick Fic
4071abcb56 Merged in feature/2021-06-18 (pull request #107)
Feature/2021 06 18
2021-06-14 17:48:14 +00:00
Patrick Fic
f81e026e12 Merged in feature/2021-06-18 (pull request #105)
Package updates.
2021-06-11 15:58:35 +00:00
Patrick Fic
21a1791e7a Merged in feature/2021-06-18 (pull request #104)
Update documents transformations + crisp.
2021-06-11 15:10:19 +00:00
Patrick Fic
0615e46d8a Merged in feature/2021-06-18 (pull request #103)
Feature/2021 06 18
2021-06-09 18:34:35 +00:00
Patrick Fic
9b881ee11a Merged in hotfix/2021-06-08 (pull request #100)
Add rounding for depreciation.
2021-06-08 22:42:53 +00:00
Patrick Fic
170f03979e Merged in feature/2021-06-18 (pull request #99)
Feature/2021 06 18
2021-06-08 17:41:26 +00:00
Patrick Fic
7c6b2faa1a Merged in feature/2020-06-04 (pull request #97)
Feature/2020 06 04
2021-06-04 20:20:49 +00:00
Patrick Fic
dbe3944089 Merged in feature/2020-06-04 (pull request #96)
Feature/2020 06 04
2021-06-03 17:33:36 +00:00
Patrick Fic
0b50f424fa Merged in feature/2020-06-04 (pull request #94)
Feature/2020 06 04
2021-06-02 23:10:45 +00:00
Patrick Fic
701a52dd22 Merged in feature/2020-06-04 (pull request #93)
Feature/2020 06 04
2021-06-02 17:09:31 +00:00
106 changed files with 5046 additions and 329 deletions

View File

@@ -301,6 +301,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>preview</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>reschedule</name>
<definition_loaded>false</definition_loaded>
@@ -2993,6 +3014,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>attach_pdf_to_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>bill_federal_tax_rate</name>
<definition_loaded>false</definition_loaded>
@@ -16495,6 +16537,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>markpstexempt</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>markpstexemptconfirm</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>postbills</name>
<definition_loaded>false</definition_loaded>
@@ -24108,6 +24192,612 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>landing</name>
<children>
<folder_node>
<name>bigfeature</name>
<children>
<concept_node>
<name>subtitle</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>title</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>footer</name>
<children>
<folder_node>
<name>company</name>
<children>
<concept_node>
<name>about</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>contact</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>disclaimers</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>privacypolicy</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>io</name>
<children>
<concept_node>
<name>help</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>status</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>slogan</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>hero</name>
<children>
<concept_node>
<name>button</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>title</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>features</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>managemyshop</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>pricing</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>pricing</name>
<children>
<folder_node>
<name>basic</name>
<children>
<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>sub</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>essentials</name>
<children>
<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>sub</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>pricingtitle</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>
<folder_node>
<name>pro</name>
<children>
<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>sub</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>title</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>
<folder_node>
<name>unlimited</name>
<children>
<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>sub</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>
<folder_node>
<name>menus</name>
<children>
@@ -29793,6 +30483,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>key_tag</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>paint_grid</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>parts_label_single</name>
<definition_loaded>false</definition_loaded>
@@ -30087,6 +30819,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>stolen_recovery_checklist</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>supplement_request</name>
<definition_loaded>false</definition_loaded>
@@ -31857,6 +32610,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>gsr_by_csr</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>gsr_by_delivery_date</name>
<definition_loaded>false</definition_loaded>
@@ -32298,6 +33072,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>open_orders_csr</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>open_orders_estimator</name>
<definition_loaded>false</definition_loaded>

View File

@@ -12,7 +12,9 @@ module.exports = {
modifyVars: {
...(process.env.NODE_ENV === "development"
? { "@primary-color": "#a51d1d" }
: { "@primary-color": "#1DA57A" }),
: {
//"@primary-color": "#1DA57A"
}),
// "@primary-color": " #1890ff", // primary color for all components
// "@link-color": "#1890ff", // link color
// "@success-color": "#52c41a", // success state color

View File

@@ -19,6 +19,7 @@
"craco-less": "^1.17.1",
"dinero.js": "^1.8.1",
"dotenv": "^9.0.2",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.0.0",
"firebase": "^8.6.0",
@@ -35,6 +36,8 @@
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.0",
"rc-queue-anim": "^1.8.5",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.33.2",
"react-color": "^2.19.3",
@@ -49,6 +52,7 @@
"react-resizable": "^3.0.1",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.0.7",
"redux": "^4.1.0",

View File

@@ -6,7 +6,6 @@ import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import JiraSupportComponent from "../components/jira-support-widget/jira-support-widget.component";
import client from "../utils/GraphQLClient";
import App from "./App";
moment.locale("en-US");
@@ -16,21 +15,6 @@ if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
export default function AppContainer() {
const { t } = useTranslation();
// useEffect(() => {
// // Include the Crisp code here, without the <script></script> tags
// window.$crisp = [];
// window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
// var d = document;
// var s = d.createElement("script");
// s.src = "https://client.crisp.chat/l.js";
// s.async = 1;
// d.getElementsByTagName("head")[0].appendChild(s);
// return () => {
// d.getElementsByTagName("head")[0].removeChild(s);
// };
// }, []);
return (
<ApolloProvider client={client}>
<ConfigProvider
@@ -46,7 +30,6 @@ export default function AppContainer() {
>
<GlobalLoadingBar />
<App />
<JiraSupportComponent />
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -8,7 +8,7 @@ import DocumentEditorContainer from "../components/document-editor/document-edit
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
//Component Imports
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import AboutPage from "../pages/about/about.page";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import TechPageContainer from "../pages/tech/tech.page.container";
import { setOnline } from "../redux/application/application.actions";
import { selectOnline } from "../redux/application/application.selectors";
@@ -17,7 +17,7 @@ import { selectCurrentUser } from "../redux/user/user.selectors";
import PrivateRoute from "../utils/private-route";
import "./App.styles.scss";
const LandingPage = lazy(() => import("../pages/landing/landing.page"));
import LandingPage from "../pages/landing/landing.page";
const ResetPassword = lazy(() =>
import("../pages/reset-password/reset-password.component")
);
@@ -100,7 +100,7 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
<Route exact path="/csi/:surveyId" component={CsiPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/about" component={AboutPage} />
<Route exact path="/disclaimer" component={DisclaimerPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" ?><svg style="enable-background:new 0 0 128 128;" version="1.1" viewBox="0 0 128 128" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:10;}
.st1{display:none;}
.st2{display:inline;opacity:0.25;fill:#F45EFD;}
</style><g id="_x31_2_3D_Printing"/><g id="_x31_1_VR_Gear"/><g id="_x31_0_Virtual_reality"/><g id="_x39__Augmented_reality"/><g id="_x38__Teleport"/><g id="_x37__Glassess"/><g id="_x36__Folding_phone"/><g id="_x35__Drone"/><g id="_x34__Retina_scan"/><g id="_x33__Smartwatch"/><g id="_x32__Bionic_Arm"/><g id="_x31__Chip"><g><path d="M108,40c-5.2,0-9.6,3.3-11.3,8H84V32h-8V20h-8v12h-8V20h-8v12h-8v16H24v-8.7c4.7-1.7,8-6.1,8-11.3c0-6.6-5.4-12-12-12 S8,21.4,8,28c0,5.2,3.3,9.6,8,11.3V56h28v8H16v16.7c-4.7,1.7-8,6.1-8,11.3c0,6.6,5.4,12,12,12s12-5.4,12-12c0-5.2-3.3-9.6-8-11.3 V72h20v16h8v12h8V88h8v12h8V88h8V72h8v16.7c-4.7,1.7-8,6.1-8,11.3c0,6.6,5.4,12,12,12s12-5.4,12-12c0-5.2-3.3-9.6-8-11.3V64H84v-8 h12.7c1.7,4.7,6.1,8,11.3,8c6.6,0,12-5.4,12-12S114.6,40,108,40z M20,32c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S22.2,32,20,32z M20,96c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S22.2,96,20,96z M76,80H52V40h24V80z M96,96c2.2,0,4,1.8,4,4s-1.8,4-4,4s-4-1.8-4-4 S93.8,96,96,96z M108,56c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4S110.2,56,108,56z"/><rect height="8" width="8" x="56" y="64"/></g></g><g class="st1" id="Guide"><path class="st2" d="M120,8v112H8V8H120 M128,0H0v128h128V0L128,0z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -81,6 +81,7 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
console.log("adjustmentsToInsert", adjustmentsToInsert);
const adjKeys = Object.keys(adjustmentsToInsert);

View File

@@ -1,5 +1,5 @@
import { HomeFilled } from "@ant-design/icons";
import { Breadcrumb } from "antd";
import { Breadcrumb, Row, Col } from "antd";
import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
@@ -14,27 +14,29 @@ const mapStateToProps = createStructuredSelector({
export function BreadCrumbs({ breadcrumbs }) {
return (
<div className="breadcrumb-container imex-flex-row">
<Breadcrumb separator=">" style={{ flex: 1 }}>
<Breadcrumb.Item>
<Link to={`/manage`}>
<HomeFilled />
</Link>
</Breadcrumb.Item>
{breadcrumbs.map((item) =>
item.link ? (
<Breadcrumb.Item key={item.label}>
<Link to={item.link}>{item.label} </Link>
</Breadcrumb.Item>
) : (
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)
)}
</Breadcrumb>
<div>
<Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}>
<Breadcrumb separator=">">
<Breadcrumb.Item>
<Link to={`/manage`}>
<HomeFilled />
</Link>
</Breadcrumb.Item>
{breadcrumbs.map((item) =>
item.link ? (
<Breadcrumb.Item key={item.label}>
<Link to={item.link}>{item.label} </Link>
</Breadcrumb.Item>
) : (
<Breadcrumb.Item key={item.label}>{item.label}</Breadcrumb.Item>
)
)}
</Breadcrumb>
</Col>
<Col xs={24} sm={24} md={8}>
<GlobalSearch />
</div>
</div>
</Col>
</Row>
);
}
export default connect(mapStateToProps, null)(BreadCrumbs);

View File

@@ -43,6 +43,10 @@ export function EmailOverlayContainer({
const [loading, setLoading] = useState(false);
const [sending, setSending] = useState(false);
const [rawHtml, setRawHtml] = useState("");
const [pdfCopytoAttach, setPdfCopytoAttach] = useState({
filename: null,
pdf: null,
});
const [selectedMedia, setSelectedMedia] = useState([]);
const defaultEmailFrom = {
@@ -59,17 +63,17 @@ export function EmailOverlayContainer({
const handleFinish = async (values) => {
logImEXEvent("email_send_from_modal");
const attachments = [];
//const attachments = [];
if (values.fileList)
await asyncForEach(values.fileList, async (f) => {
const t = {
ContentType: f.type,
Filename: f.name,
Base64Content: (await toBase64(f.originFileObj)).split(",")[1],
};
attachments.push(t);
});
// if (values.fileList)
// await asyncForEach(values.fileList, async (f) => {
// const t = {
// ContentType: f.type,
// Filename: f.name,
// Base64Content: (await toBase64(f.originFileObj)).split(",")[1],
// };
// attachments.push(t);
// });
setSending(true);
try {
@@ -77,11 +81,28 @@ export function EmailOverlayContainer({
...defaultEmailFrom,
...values,
html: rawHtml,
attachments:
values.fileList &&
(await Promise.all(
values.fileList.map(async (f) => await toBase64(f.originFileObj))
)),
attachments: [
...(values.fileList
? await Promise.all(
values.fileList.map(async (f) => {
return {
filename: f.name,
path: await toBase64(f.originFileObj),
};
})
)
: []),
...(pdfCopytoAttach.pdf
? [
{
path: pdfCopytoAttach.pdf,
filename:
pdfCopytoAttach.filename &&
`${pdfCopytoAttach.filename}.pdf`,
},
]
: []),
],
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.src),
//attachments,
});
@@ -99,13 +120,22 @@ export function EmailOverlayContainer({
const render = async () => {
logImEXEvent("email_render_template", { template: emailConfig.template });
setLoading(true);
let html = await RenderTemplate(emailConfig.template, bodyshop, true);
let { html, pdf, filename } = await RenderTemplate(
emailConfig.template,
bodyshop,
true
);
const response = await axios.post("/render/inlinecss", {
html: html,
url: `${window.location.protocol}://${window.location.host}/`,
});
setRawHtml(response.data);
if (pdf) {
setPdfCopytoAttach({ pdf, filename });
}
form.setFieldsValue({
...emailConfig.messageOptions,
cc:
@@ -166,8 +196,8 @@ const toBase64 = (file) =>
reader.onerror = (error) => reject(error);
});
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};
// const asyncForEach = async (array, callback) => {
// for (let index = 0; index < array.length; index++) {
// await callback(array[index], index, array);
// }
// };

View File

@@ -37,7 +37,7 @@ export default function GlobalSearch() {
value: job.ro_number,
label: (
<Link to={`/manage/jobs/${job.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<strong>{job.ro_number || t("general.labels.na")}</strong>
<span>{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
job.ownr_co_nm || ""
@@ -45,7 +45,7 @@ export default function GlobalSearch() {
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
job.v_model_desc || ""
}`}</span>
<span>{`${job.clm_no}`}</span>
<span>{`${job.clm_no || ""}`}</span>
</Space>
</Link>
),
@@ -62,7 +62,7 @@ export default function GlobalSearch() {
}`,
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{`${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
owner.ownr_co_nm || ""
}`}</span>
@@ -85,14 +85,14 @@ export default function GlobalSearch() {
} ${vehicle.v_model_desc || ""}`,
label: (
<Link to={`/manage/vehicles/${vehicle.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<span>
{`${vehicle.v_model_yr || ""} ${
vehicle.v_make_desc || ""
} ${vehicle.v_model_desc || ""}`}
</span>
<span>{vehicle.plate_no}</span>
<span> {vehicle.v_vin}</span>
<span>{vehicle.plate_no || ""}</span>
<span> {vehicle.v_vin || ""}</span>
</Space>
</Link>
),
@@ -107,11 +107,12 @@ export default function GlobalSearch() {
value: `${payment.job.ro_number} ${payment.payer} ${payment.amount}`,
label: (
<Link to={`/manage/jobs/${payment.job.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{payment.paymentnum}</span>
<span>{payment.job.ro_number}</span>
<span>{payment.job.memo}</span>
<span>{payment.job.amount}</span>
<span>{payment.job.transactionid}</span>
<span>{payment.memo || ""}</span>
<span>{payment.amount || ""}</span>
<span>{payment.transactionid || ""}</span>
</Space>
</Link>
),
@@ -126,7 +127,7 @@ export default function GlobalSearch() {
value: `${bill.invoice_number} - ${bill.vendor.name}`,
label: (
<Link to={`/manage/bills?billid=${bill.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{bill.invoice_number}</span>
<span>{bill.vendor.name}</span>
<span>{bill.date}</span>
@@ -146,7 +147,7 @@ export default function GlobalSearch() {
}`,
label: (
<Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{`${pb.firstname || ""} ${pb.lastname || ""} ${
pb.company || ""
}`}</span>
@@ -165,8 +166,6 @@ export default function GlobalSearch() {
return (
<AutoComplete
key="globalsearch"
dropdownMatchSelectWidth={"false"}
options={options}
onSearch={handleSearch}
allowClear

View File

@@ -1,28 +0,0 @@
import React from "react";
export default function JiraSupportComponent() {
//useScript();
return <div></div>;
}
// const useScript = () => {
// useEffect(() => {
// const script = document.createElement("script");
// script.src = "https://jsd-widget.atlassian.com/assets/embed.js";
// script.setAttribute("data-jsd-embedded", true);
// script.setAttribute("data-key", "d69bb65c-1dd3-483f-b109-66a970d03f44");
// script.setAttribute("data-base-url", "https://jsd-widget.atlassian.com");
// //script.async = true;
// script.onload = () => {
// var DOMContentLoaded_event = document.createEvent("Event");
// DOMContentLoaded_event.initEvent("DOMContentLoaded", true, true);
// window.document.dispatchEvent(DOMContentLoaded_event);
// };
// document.head.appendChild(script);
// return () => {
// document.head.removeChild(script);
// };
// }, []);
// };

View File

@@ -2,7 +2,7 @@ import { Button, Popover, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { Link, useHistory, useLocation } from "react-router-dom";
import { setModalContext } from "../../redux/modals/modals.actions";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
@@ -11,6 +11,8 @@ import { TemplateList } from "../../utils/TemplateConstants";
import DataLabel from "../data-label/data-label.component";
import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component";
import queryString from "query-string";
const mapDispatchToProps = (dispatch) => ({
setScheduleContext: (context) =>
dispatch(setModalContext({ context: context, modal: "schedule" })),
@@ -24,6 +26,8 @@ export function ScheduleEventComponent({
}) {
const { t } = useTranslation();
const [visible, setVisible] = useState(false);
const history = useHistory();
const searchParams = queryString.parse(useLocation().search);
const blockContent = (
<div>
@@ -88,6 +92,20 @@ export function ScheduleEventComponent({
<Button>{t("appointments.actions.viewjob")}</Button>
</Link>
) : null}
{event.job ? (
<Button
onClick={() => {
history.push({
search: queryString.stringify({
...searchParams,
selected: event.job.id,
}),
});
}}
>
{t("appointments.actions.preview")}
</Button>
) : null}
<Button
onClick={() => {
const Template = TemplateList("job").appointment_reminder;

View File

@@ -295,18 +295,18 @@ export function JobLinesComponent({
onClick={async () => {
await deleteJobLine({
variables: { joblineId: record.id },
update(cache) {
cache.modify({
id: cache.identify(job),
fields: {
joblines(existingJobLines, { readField }) {
return existingJobLines.filter(
(jlRef) => record.id !== readField("id", jlRef)
);
},
},
});
},
// update(cache) {
// cache.modify({
// id: cache.identify(job),
// fields: {
// joblines(existingJobLines, { readField }) {
// return existingJobLines.filter(
// (jlRef) => record.id !== readField("id", jlRef)
// );
// },
// },
// });
// },
});
await axios.post("/job/totalsssu", {
id: job.id,

View File

@@ -1,4 +1,5 @@
import { Button, Card, Space, Table } from "antd";
import { EditFilled } from "@ant-design/icons";
import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -115,16 +116,29 @@ export function JobPayments({
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<PrintWrapperComponent
templateObject={{
name: TemplateList("payment").payment_receipt.key,
variables: { id: record.id },
}}
messageObject={{
to: job.ownr_ea,
}}
id={job.id}
/>
<Space wrap>
<Button
disabled={record.exportedat}
onClick={() => {
setPaymentContext({
actions: { refetch: refetch },
context: record,
});
}}
>
<EditFilled />
</Button>
<PrintWrapperComponent
templateObject={{
name: TemplateList("payment").payment_receipt.key,
variables: { id: record.id },
}}
messageObject={{
to: job.ownr_ea,
}}
id={job.id}
/>
</Space>
),
},
];

View File

@@ -1,4 +1,4 @@
import { Checkbox, PageHeader, Table } from "antd";
import { Checkbox, Table, Typography } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -21,6 +21,7 @@ export default function JobReconciliationBillsTable({
title: t("billlines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
width: "35%",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
@@ -29,6 +30,8 @@ export default function JobReconciliationBillsTable({
title: t("billlines.labels.from"),
dataIndex: "from",
key: "from",
width: "20%",
ellipsis: true,
render: (text, record) =>
`${record.bill.vendor && record.bill.vendor.name} / ${
record.bill.invoice_number
@@ -57,7 +60,7 @@ export default function JobReconciliationBillsTable({
),
},
{
title: t("billlines.fields.quantity"),
title: t("joblines.fields.part_qty"),
dataIndex: "quantity",
key: "quantity",
sorter: (a, b) => a.quantity - b.quantity,
@@ -86,10 +89,12 @@ export default function JobReconciliationBillsTable({
};
return (
<PageHeader title={t("bills.labels.bills")}>
<div>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Table
pagination={false}
scroll={{ y: "40vh", x: true }}
size="small"
scroll={{ y: "80vh", x: true }}
columns={columns}
rowKey="id"
dataSource={invoiceLineData}
@@ -99,6 +104,6 @@ export default function JobReconciliationBillsTable({
selectedRowKeys: selectedLines,
}}
/>
</PageHeader>
</div>
);
}

View File

@@ -22,21 +22,23 @@ export default function JobReconciliationModalComponent({ job, bills }) {
);
return (
<div>
<Row gutter={[16, 16]}>
<Col span={12}>
<JobReconciliationPartsTable
jobLineData={jobLineData}
jobLineState={jobLineState}
/>
</Col>
<Col span={12}>
<JobReconciliationBillsTable
invoiceLineData={invoiceLineData}
billLineState={billLineState}
/>
</Col>
</Row>
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
<div style={{ flex: 1 }}>
<Row gutter={8}>
<Col span={12}>
<JobReconciliationPartsTable
jobLineData={jobLineData}
jobLineState={jobLineState}
/>
</Col>
<Col span={12}>
<JobReconciliationBillsTable
invoiceLineData={invoiceLineData}
billLineState={billLineState}
/>
</Col>
</Row>
</div>
<Row>
<JobReconciliationTotals
jobLines={jobLineData}

View File

@@ -0,0 +1,12 @@
.imex-reconciliation-modal {
top: 20px;
.ant-modal-content {
height: 95vh;
display: flex;
flex-direction: column;
.ant-modal-body {
display: flex;
flex: 1;
}
}
}

View File

@@ -10,6 +10,7 @@ import { selectReconciliation } from "../../redux/modals/modals.selectors";
import JobReconciliationModalComponent from "./job-reconciliation-modal.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import "./job-reconciliation-modal.styles.scss";
const mapStateToProps = createStructuredSelector({
reconciliationModal: selectReconciliation,
@@ -38,23 +39,23 @@ function JobReconciliationModalContainer({
return (
<Modal
title={t("jobs.labels.reconciliationheader")}
width={"90%"}
width={"95%"}
visible={visible}
okText={t("general.actions.close")}
onOk={handleCancel}
onCancel={handleCancel}
cancelButtonProps={{ display: "none" }}
destroyOnClose
className="imex-reconciliation-modal"
>
<LoadingSpinner loading={loading}>
{error && <AlertComponent message={error.message} type="error" />}
{data && (
<JobReconciliationModalComponent
job={data && data.jobs_by_pk}
bills={data && data.bills}
/>
)}
</LoadingSpinner>
{loading && <LoadingSpinner loading={loading} />}
{error && <AlertComponent message={error.message} type="error" />}
{data && (
<JobReconciliationModalComponent
job={data && data.jobs_by_pk}
bills={data && data.bills}
/>
)}
</Modal>
);
}

View File

@@ -1,4 +1,4 @@
import { PageHeader, Table } from "antd";
import { Table, Typography } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -102,11 +102,13 @@ export default function JobReconcilitionPartsTable({
};
return (
<PageHeader title={t("jobs.labels.lines")}>
<div>
<Typography.Title level={4}>{t("jobs.labels.lines")}</Typography.Title>
<Table
pagination={false}
columns={columns}
scroll={{ y: "40vh", x: true }}
size="small"
scroll={{ y: "80vh", x: true }}
rowKey="id"
dataSource={jobLineData}
onChange={handleTableChange}
@@ -122,6 +124,6 @@ export default function JobReconcilitionPartsTable({
<div style={{ fontStyle: "italic", margin: "4px" }}>
{t("jobs.labels.reconciliation.removedpartsstrikethrough")}
</div>
</PageHeader>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import { LoadingOutlined } from "@ant-design/icons";
import { useLazyQuery } from "@apollo/client";
import { Empty, Select } from "antd";
import { Empty, Select, Space, Tag } from "antd";
import _ from "lodash";
import React, { forwardRef, useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -80,13 +80,20 @@ const JobSearchSelect = (
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id} status={o.status}>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
o.ro_number || t("general.labels.na")
} | ${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
o.v_model_desc || ""
}`}
<Space align="center">
<span>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
o.ro_number || t("general.labels.na")
} | ${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
o.v_model_desc || ""
}`}
</span>
<Tag>
<strong>{o.status}</strong>
</Tag>
</Space>
</Option>
))
: null}

View File

@@ -0,0 +1,57 @@
import { DownCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Dropdown, Menu, notification } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
export function JobsAdminStatus({ bodyshop, job }) {
const { t } = useTranslation();
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
const updateJobStatus = (status) => {
mutationUpdateJobstatus({
variables: { jobId: job.id, status: status },
})
.then((r) => {
notification["success"]({ message: t("jobs.successes.save") });
// refetch();
})
.catch((error) => {
notification["error"]({ message: t("jobs.errors.saving") });
});
};
const statusmenu = (
<Menu
onClick={(e) => {
updateJobStatus(e.key);
}}
>
{bodyshop.md_ro_statuses.statuses.map((item) => (
<Menu.Item key={item}>{item}</Menu.Item>
))}
</Menu>
);
return (
<Dropdown overlay={statusmenu} trigger={["click"]} key="changestatus">
<Button shape="round">
<span>{job.status}</span>
<DownCircleFilled />
</Button>
</Dropdown>
);
}

View File

@@ -11,6 +11,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
query: GET_ALL_JOBLINES_BY_PK,
variables: { id: jobId },
});
const existingLines = _.cloneDeep(existingLinesFromDb);
const linesToInsert = [];
const linesToUpdate = [];
@@ -19,11 +20,14 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
const matchingIndex = existingLines.findIndex(
(eL) => eL.unq_seq === newLine.unq_seq
);
//Should do a check to make sure there is only 1 matching unq sequence number.
if (matchingIndex >= 0) {
//Found a relevant matching line. Add it to lines to update.
linesToUpdate.push({
id: existingLines[matchingIndex].id,
newData: newLine,
newData: { ...newLine, removed: false },
});
//Splice out item we found for performance.

View File

@@ -11,7 +11,8 @@ import FormItemPhone, {
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import { JobsDetailRatesParts } from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -187,7 +188,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
header={t("menus.jobsdetail.financials")}
>
<JobsDetailRatesChangeButton form={form} />
<JobsMarkPstExempt form={form} />
<LayoutFormRow>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput />

View File

@@ -181,7 +181,7 @@ export function JobsDetailHeaderActions({
{job.inproduction ? (
<Menu.Item
key="addtoproduction"
disabled={!!!job.converted || jobRO}
disabled={!job.converted}
onClick={() => AddToProduction(client, job.id, refetch, true)}
>
{t("jobs.actions.removefromproduction")}
@@ -189,7 +189,7 @@ export function JobsDetailHeaderActions({
) : (
<Menu.Item
key="addtoproduction"
disabled={!!!job.converted || !!job.inproduction || jobRO}
disabled={!job.converted}
onClick={() => AddToProduction(client, job.id, refetch)}
>
{t("jobs.actions.addtoproduction")}

View File

@@ -1,4 +1,13 @@
import { Form, Input, InputNumber, Select, Space, Switch, Tooltip } from "antd";
import {
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Tooltip,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -7,6 +16,7 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
@@ -103,8 +113,19 @@ export function JobsDetailRates({ jobRO, form, job }) {
<Switch disabled={jobRO} />
</Form.Item>
</FormRow>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<FormRow header={t("jobs.forms.laborrates")}>
<Divider
orientation="left"
type="horizontal"
style={{ marginTop: ".8rem", float: "right" }}
>
{t("jobs.forms.laborrates")}
</Divider>
<Space>
<div></div>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<JobsMarkPstExempt form={form} />
</Space>
<FormRow noDivider>
<Form.Item
label={t("jobs.fields.labor_rate_desc")}
name="labor_rate_desc"
@@ -180,7 +201,6 @@ export function JobsDetailRates({ jobRO, form, job }) {
<CurrencyInput disabled={jobRO} />
</Form.Item>
</FormRow>
<JobsDetailRatesParts form={form} />
</div>
);

View File

@@ -19,7 +19,11 @@ export function JobsDetailRatesParts({
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel header={t("jobs.labels.parts_tax_rates")} key="rates">
<Collapse.Panel
forceRender
header={t("jobs.labels.parts_tax_rates")}
key="rates"
>
<LayoutFormRow header={t("joblines.fields.part_types.PAA")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}

View File

@@ -0,0 +1,55 @@
import { Popconfirm, Button } from "antd";
import React from "react";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
});
export function JobsMarkPstExempt({ jobRO, form }) {
const { t } = useTranslation();
const handleConfirm = () => {
const newPartRates = _.cloneDeep(form.getFieldValue("parts_tax_rates"));
Object.keys(newPartRates).forEach((key) => {
newPartRates[key] = {
...newPartRates[key],
prt_tax_in: false,
prt_tax_rt: 0,
};
});
form.setFieldsValue({
state_tax_rate: 0,
tax_lbr_rt: 0,
tax_levies_rt: 0,
tax_sub_rt: 0,
tax_shop_mat_rt: 0,
tax_paint_mat_rt: 0,
tax_str_rt: 0,
tax_tow_rt: 0,
parts_tax_rates: newPartRates,
});
};
return (
<Popconfirm
onConfirm={handleConfirm}
disabled={jobRO}
okText={t("general.labels.yes")}
cancelText={t("general.labels.no")}
title={t("jobs.actions.markpstexemptconfirm")}
>
<Button type="link" disabled={jobRO}>
{t("jobs.actions.markpstexempt")}
</Button>
</Popconfirm>
);
}
export default connect(mapStateToProps, null)(JobsMarkPstExempt);

View File

@@ -31,11 +31,14 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
title: t("jobs.fields.vehicle"),
dataIndex: "vehicleid",
key: "vehicleid",
render: (text, record) => (
<Link to={`/manage/vehicles/${record.vehicleid}`}>
{`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc}`}
</Link>
),
render: (text, record) =>
record.vehicleid ? (
<Link to={`/manage/vehicles/${record.vehicleid}`}>
{`${record.v_model_yr} ${record.v_make_desc} ${record.v_model_desc}`}
</Link>
) : (
t("jobs.errors.novehicle")
),
},
{
title: t("jobs.fields.clm_no"),

View File

@@ -90,7 +90,11 @@ export function PartsOrderListTableComponent({
</Button>
)}
<Button
disabled={jobRO || record.return}
disabled={
jobRO ||
record.return ||
record.vendor.id === bodyshop.inhousevendorid
}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
@@ -139,7 +143,10 @@ export function PartsOrderListTableComponent({
</Button>
</Popconfirm>
<Button
disabled={jobRO ? !record.return : jobRO}
disabled={
(jobRO ? !record.return : jobRO) ||
record.vendor.id === bodyshop.inhousevendorid
}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
@@ -157,7 +164,7 @@ export function PartsOrderListTableComponent({
quantity: pol.quantity,
actual_price: pol.act_price,
cost_center: pol.jobline.part_type
cost_center: pol.jobline?.part_type
? responsibilityCenters.defaults.costs[
pol.jobline.part_type
] || null

View File

@@ -101,7 +101,9 @@ export function PartsOrderModalContainer({
const jobLinesResult = await updateJobLines({
variables: {
ids: values.parts_order_lines.data.map((item) => item.job_line_id),
ids: values.parts_order_lines.data
.filter((item) => item.job_line_id)
.map((item) => item.job_line_id),
status: isReturn
? bodyshop.md_order_statuses.default_returned || "Returned*"
: bodyshop.md_order_statuses.default_ordered || "Ordered*",

View File

@@ -9,6 +9,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import Event from "../job-at-change/schedule-event.container";
import HeaderComponent from "./schedule-calendar-header.component";
import "./schedule-calendar.styles.scss";
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -45,45 +46,48 @@ export function ScheduleCalendarWrapperComponent({
const selectedDate = new Date(date || moment(search.date) || Date.now());
return (
<Calendar
events={data}
defaultView={search.view || defaultView || "week"}
date={selectedDate}
onNavigate={(date, view, action) => {
search.date = date.toISOString().substr(0, 10);
history.push({ search: queryString.stringify(search) });
}}
onRangeChange={(start, end) => {
if (setDateRangeCallback) setDateRangeCallback({ start, end });
}}
onView={(view) => {
search.view = view;
history.push({ search: queryString.stringify(search) });
}}
step={15}
// timeslots={1}
showMultiDayTimes
localizer={localizer}
min={
bodyshop.schedule_start_time
? new Date(bodyshop.schedule_start_time)
: new Date("2020-01-01T06:00:00")
}
max={
bodyshop.schedule_end_time
? new Date(bodyshop.schedule_end_time)
: new Date("2020-01-01T20:00:00")
}
eventPropGetter={handleEventPropStyles}
components={{
event: (e) =>
Event({ bodyshop: bodyshop, event: e.event, refetch: refetch }),
header: (p) => (
<HeaderComponent {...p} events={data} refetch={refetch} />
),
}}
{...otherProps}
/>
<>
<JobDetailCards />
<Calendar
events={data}
defaultView={search.view || defaultView || "week"}
date={selectedDate}
onNavigate={(date, view, action) => {
search.date = date.toISOString().substr(0, 10);
history.push({ search: queryString.stringify(search) });
}}
onRangeChange={(start, end) => {
if (setDateRangeCallback) setDateRangeCallback({ start, end });
}}
onView={(view) => {
search.view = view;
history.push({ search: queryString.stringify(search) });
}}
step={15}
// timeslots={1}
showMultiDayTimes
localizer={localizer}
min={
bodyshop.schedule_start_time
? new Date(bodyshop.schedule_start_time)
: new Date("2020-01-01T06:00:00")
}
max={
bodyshop.schedule_end_time
? new Date(bodyshop.schedule_end_time)
: new Date("2020-01-01T20:00:00")
}
eventPropGetter={handleEventPropStyles}
components={{
event: (e) =>
Event({ bodyshop: bodyshop, event: e.event, refetch: refetch }),
header: (p) => (
<HeaderComponent {...p} events={data} refetch={refetch} />
),
}}
{...otherProps}
/>
</>
);
}

View File

@@ -9,6 +9,7 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
return (
<Row gutter={[16, 16]}>
<ScheduleModal />
<Col span={24}>
<PageHeader
extra={

View File

@@ -436,6 +436,14 @@ export default function ShopInfoGeneral({ form }) {
>
<CurrencyInput />
</Form.Item>
<Form.Item
name={["attach_pdf_to_email"]}
label={t("bodyshop.fields.attach_pdf_to_email")}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}>

View File

@@ -91,6 +91,7 @@ export const QUERY_BODYSHOP = gql`
md_jobline_presets
cdk_dealerid
features
attach_pdf_to_email
employees {
id
active
@@ -178,6 +179,7 @@ export const UPDATE_SHOP = gql`
jc_hourly_rates
md_jobline_presets
cdk_dealerid
attach_pdf_to_email
employees {
id
first_name

View File

@@ -23,19 +23,6 @@ export const GET_ALL_JOBLINES_BY_PK = gql`
notes
location
tax_part
parts_order_lines {
id
parts_order {
id
order_number
order_date
user_email
vendor {
id
name
}
}
}
}
}
`;
@@ -228,7 +215,11 @@ export const generateJobLinesUpdatesForInvoicing = (joblines) => {
export const DELETE_JOB_LINE_BY_PK = gql`
mutation DELETE_JOB_LINE_BY_PK($joblineId: uuid!) {
delete_joblines_by_pk(id: $joblineId) {
update_joblines_by_pk(
pk_columns: { id: $joblineId }
_set: { removed: true }
) {
removed
id
}
}

View File

@@ -559,6 +559,7 @@ export const GET_JOB_BY_PK = gql`
}
payments {
id
jobid
amount
payer
created_at
@@ -566,6 +567,8 @@ export const GET_JOB_BY_PK = gql`
transactionid
memo
date
type
exportedat
}
cccontracts {
id
@@ -686,6 +689,8 @@ export const QUERY_JOB_CARD_DETAILS = gql`
v_make_desc
v_model_desc
v_color
v_vin
plate_st
plate_no
vehicle {
id
@@ -1040,6 +1045,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
v_make_desc
v_model_desc
v_model_yr
status
}
}
`;

View File

@@ -36,6 +36,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
search_payments(args: { search: $search }) {
id
amount
paymentnum
job {
ro_number
id

View File

@@ -1,10 +1,7 @@
import { AlertOutlined } from "@ant-design/icons";
import * as Sentry from "@sentry/react";
import { Button, notification, Space } from "antd";
//import "antd/dist/antd.css";
import "antd/dist/antd.less";
import Dinero from "dinero.js";
import i18n from "i18next";
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
@@ -15,7 +12,6 @@ import LoadingSpinner from "./components/loading-spinner/loading-spinner.compone
import "./index.css";
import { persistor, store } from "./redux/store";
import reportWebVitals from "./reportWebVitals";
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
import "./translations/i18n";
import "./utils/CleanAxios";
require("dotenv").config();
@@ -52,44 +48,44 @@ ReactDOM.render(
document.getElementById("root")
);
const onServiceWorkerUpdate = (registration) => {
console.log("onServiceWorkerUpdate", registration);
// const onServiceWorkerUpdate = (registration) => {
// console.log("onServiceWorkerUpdate", registration);
const btn = (
<Space flex>
<Button
onClick={async () => {
window.open("https://imex-online.noticeable.news/", "_blank");
}}
>
{i18n.t("general.actions.viewreleasenotes")}
</Button>
<Button
type="primary"
onClick={async () => {
if (registration && registration.waiting) {
await registration.unregister();
// Makes Workbox call skipWaiting()
registration.waiting.postMessage({ type: "SKIP_WAITING" });
// Once the service worker is unregistered, we can reload the page to let
// the browser download a fresh copy of our app (invalidating the cache)
window.location.reload();
}
}}
>
{i18n.t("general.actions.refresh")}
</Button>
</Space>
);
notification.open({
icon: <AlertOutlined />,
message: i18n.t("general.messages.newversiontitle"),
description: i18n.t("general.messages.newversionmessage"),
duration: 0,
btn,
key: "updateavailable",
});
};
// const btn = (
// <Space flex>
// <Button
// onClick={async () => {
// window.open("https://imex-online.noticeable.news/", "_blank");
// }}
// >
// {i18n.t("general.actions.viewreleasenotes")}
// </Button>
// <Button
// type="primary"
// onClick={async () => {
// if (registration && registration.waiting) {
// await registration.unregister();
// // Makes Workbox call skipWaiting()
// registration.waiting.postMessage({ type: "SKIP_WAITING" });
// // Once the service worker is unregistered, we can reload the page to let
// // the browser download a fresh copy of our app (invalidating the cache)
// window.location.reload();
// }
// }}
// >
// {i18n.t("general.actions.refresh")}
// </Button>
// </Space>
// );
// notification.open({
// icon: <AlertOutlined />,
// message: i18n.t("general.messages.newversiontitle"),
// description: i18n.t("general.messages.newversionmessage"),
// duration: 0,
// btn,
// key: "updateavailable",
// });
// };
serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
// serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
reportWebVitals();

View File

@@ -0,0 +1,53 @@
import React from 'react';
import { Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import QueueAnim from 'rc-queue-anim';
import TweenOne from 'rc-tween-one';
import { isImg } from './utils';
class Banner extends React.PureComponent {
render() {
const { ...currentProps } = this.props;
const { dataSource } = currentProps;
delete currentProps.dataSource;
delete currentProps.isMobile;
return (
<div {...currentProps} {...dataSource.wrapper}>
<QueueAnim
key="QueueAnim"
type={['bottom', 'top']}
delay={200}
{...dataSource.textWrapper}
>
<div key="title" {...dataSource.title}>
{typeof dataSource.title.children === 'string' &&
dataSource.title.children.match(isImg) ? (
<img src={dataSource.title.children} width="100%" alt="img" />
) : (
dataSource.title.children
)}
</div>
<div key="content" {...dataSource.content}>
{dataSource.content.children}
</div>
<Button ghost key="button" {...dataSource.button}>
{dataSource.button.children}
</Button>
</QueueAnim>
<TweenOne
animation={{
y: '-=20',
yoyo: true,
repeat: -1,
duration: 1000,
}}
className="banner0-icon"
key="icon"
>
<DownOutlined />
</TweenOne>
</div>
);
}
}
export default Banner;

View File

@@ -0,0 +1,49 @@
import React from 'react';
import QueueAnim from 'rc-queue-anim';
import { Row, Col } from 'antd';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { getChildrenToRender } from './utils';
class Content extends React.PureComponent {
render() {
const { dataSource, isMobile, ...props } = this.props;
const {
wrapper,
titleWrapper,
page,
OverPack: overPackData,
childWrapper,
} = dataSource;
return (
<div {...props} {...wrapper}>
<div {...page}>
<div {...titleWrapper}>
{titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack {...overPackData}>
<QueueAnim
type="bottom"
key="block"
leaveReverse
component={Row}
componentProps={childWrapper}
>
{childWrapper.children.map((block, i) => {
const { children: item, ...blockProps } = block;
return (
<Col key={i.toString()} {...blockProps}>
<div {...item}>
{item.children.map(getChildrenToRender)}
</div>
</Col>
);
})}
</QueueAnim>
</OverPack>
</div>
</div>
);
}
}
export default Content;

View File

@@ -0,0 +1,70 @@
import React from 'react';
import QueueAnim from 'rc-queue-anim';
import TweenOne from 'rc-tween-one';
import { Row, Col } from 'antd';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
function Content1(props) {
const { ...tagProps } = props;
const { dataSource, isMobile } = tagProps;
delete tagProps.dataSource;
delete tagProps.isMobile;
const animType = {
queue: isMobile ? 'bottom' : 'right',
one: isMobile
? {
scaleY: '+=0.3',
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
}
: {
x: '-=30',
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
},
};
return (
<div {...tagProps} {...dataSource.wrapper}>
<OverPack {...dataSource.OverPack} component={Row}>
<TweenOne
key="img"
animation={animType.one}
resetStyle
{...dataSource.imgWrapper}
component={Col}
componentProps={{
md: dataSource.imgWrapper.md,
xs: dataSource.imgWrapper.xs,
}}
>
<span {...dataSource.img}>
<img src={dataSource.img.children} width="100%" alt="img" />
</span>
</TweenOne>
<QueueAnim
key="text"
type={animType.queue}
leaveReverse
ease={['easeOutQuad', 'easeInQuad']}
{...dataSource.textWrapper}
component={Col}
componentProps={{
md: dataSource.textWrapper.md,
xs: dataSource.textWrapper.xs,
}}
>
<h2 key="h1" {...dataSource.title}>
{dataSource.title.children}
</h2>
<div key="p" {...dataSource.content}>
{dataSource.content.children}
</div>
</QueueAnim>
</OverPack>
</div>
);
}
export default Content1;

View File

@@ -0,0 +1,60 @@
import React from 'react';
import { Row, Col } from 'antd';
import { TweenOneGroup } from 'rc-tween-one';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { getChildrenToRender } from './utils';
class Content12 extends React.PureComponent {
getChildrenToRender = (data) =>
data.map((item) => {
return (
<Col key={item.name} {...item}>
<div {...item.children.wrapper}>
<span {...item.children.img}>
<img src={item.children.img.children} alt="img" />
</span>
</div>
</Col>
);
});
render() {
const { ...props } = this.props;
const { dataSource } = props;
delete props.dataSource;
delete props.isMobile;
const childrenToRender = this.getChildrenToRender(
dataSource.block.children
);
return (
<div {...props} {...dataSource.wrapper}>
<div {...dataSource.page}>
<div key="title" {...dataSource.titleWrapper}>
{dataSource.titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack
className={`content-template ${props.className}`}
{...dataSource.OverPack}
>
<TweenOneGroup
component={Row}
key="ul"
enter={{
y: '+=30',
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
}}
leave={{ y: '+=30', opacity: 0, ease: 'easeOutQuad' }}
{...dataSource.block}
>
{childrenToRender}
</TweenOneGroup>
</OverPack>
</div>
</div>
);
}
}
export default Content12;

View File

@@ -0,0 +1,94 @@
import React from 'react';
import QueueAnim from 'rc-queue-anim';
import TweenOne from 'rc-tween-one';
import { Row, Col } from 'antd';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import { getChildrenToRender } from './utils';
class Content3 extends React.PureComponent {
getDelay = (e, b) => (e % b) * 100 + Math.floor(e / b) * 100 + b * 100;
render() {
const { ...props } = this.props;
const { dataSource, isMobile } = props;
delete props.dataSource;
delete props.isMobile;
let clearFloatNum = 0;
const children = dataSource.block.children.map((item, i) => {
const childObj = item.children;
const delay = isMobile ? i * 50 : this.getDelay(i, 24 / item.md);
const liAnim = {
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
delay,
};
const childrenAnim = { ...liAnim, x: '+=10', delay: delay + 100 };
clearFloatNum += item.md;
clearFloatNum = clearFloatNum > 24 ? 0 : clearFloatNum;
return (
<TweenOne
component={Col}
animation={liAnim}
key={item.name}
{...item}
componentProps={{ md: item.md, xs: item.xs }}
className={
!clearFloatNum
? `${item.className || ''} clear-both`.trim()
: item.className
}
>
<TweenOne
animation={{
x: '-=10',
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
}}
key="img"
{...childObj.icon}
>
<img src={childObj.icon.children} width="100%" alt="img" />
</TweenOne>
<div {...childObj.textWrapper}>
<TweenOne
key="h2"
animation={childrenAnim}
component="h2"
{...childObj.title}
>
{childObj.title.children}
</TweenOne>
<TweenOne
key="p"
animation={{ ...childrenAnim, delay: delay + 200 }}
component="div"
{...childObj.content}
>
{childObj.content.children}
</TweenOne>
</div>
</TweenOne>
);
});
return (
<div {...props} {...dataSource.wrapper}>
<div {...dataSource.page}>
<div {...dataSource.titleWrapper}>
{dataSource.titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack {...dataSource.OverPack}>
<QueueAnim key="u" type="bottom">
<Row key="row" {...dataSource.block}>
{children}
</Row>
</QueueAnim>
</OverPack>
</div>
</div>
);
}
}
export default Content3;

View File

@@ -0,0 +1,59 @@
import React from 'react';
import TweenOne from 'rc-tween-one';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import VideoPlay from 'react-sublime-video';
import { getChildrenToRender } from './utils';
function Content4(props) {
const { ...tagProps } = props;
const { dataSource, isMobile } = tagProps;
delete tagProps.dataSource;
delete tagProps.isMobile;
const animation = {
y: '+=30',
opacity: 0,
type: 'from',
ease: 'easeOutQuad',
};
const videoChildren = dataSource.video.children.video;
const videoNameArray = videoChildren.split('.');
const type = videoNameArray[videoNameArray.length - 1];
return (
<div {...tagProps} {...dataSource.wrapper}>
<div {...dataSource.page}>
<div key="title" {...dataSource.titleWrapper}>
{dataSource.titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack {...dataSource.OverPack}>
<TweenOne
key="video"
animation={{ ...animation, delay: 300 }}
{...dataSource.video}
>
{isMobile ? (
<video
width="100%"
loop
controls
poster={dataSource.video.children.image}
>
<source src={videoChildren} type={`video/${type}`} />
<track kind="captions" />
</video>
) : (
<VideoPlay
loop
width="100%"
poster={dataSource.video.children.image}
>
<source src={videoChildren} type={`video/${type}`} />
</VideoPlay>
)}
</TweenOne>
</OverPack>
</div>
</div>
);
}
export default Content4;

View File

@@ -0,0 +1,69 @@
import React from 'react';
import TweenOne from 'rc-tween-one';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import QueueAnim from 'rc-queue-anim';
import { Row, Col } from 'antd';
import { getChildrenToRender } from './utils';
import { isImg } from './utils';
class Footer extends React.Component {
static defaultProps = {
className: 'footer1',
};
getLiChildren = (data) =>
data.map((item, i) => {
const { title, childWrapper, ...itemProps } = item;
return (
<Col key={i.toString()} {...itemProps} title={null} content={null}>
<h2 {...title}>
{typeof title.children === 'string' &&
title.children.match(isImg) ? (
<img src={title.children} width="100%" alt="img" />
) : (
title.children
)}
</h2>
<div {...childWrapper}>
{childWrapper.children.map(getChildrenToRender)}
</div>
</Col>
);
});
render() {
const { ...props } = this.props;
const { dataSource } = props;
delete props.dataSource;
delete props.isMobile;
const childrenToRender = this.getLiChildren(dataSource.block.children);
return (
<div {...props} {...dataSource.wrapper}>
<OverPack {...dataSource.OverPack}>
<QueueAnim
type="bottom"
key="ul"
leaveReverse
component={Row}
{...dataSource.block}
>
{childrenToRender}
</QueueAnim>
<TweenOne
animation={{ y: '+=30', opacity: 0, type: 'from' }}
key="copyright"
{...dataSource.copyrightWrapper}
>
<div {...dataSource.copyrightPage}>
<div {...dataSource.copyright}>
{dataSource.copyright.children}
</div>
</div>
</TweenOne>
</OverPack>
</div>
);
}
}
export default Footer;

135
client/src/landing/Nav0.jsx Normal file
View File

@@ -0,0 +1,135 @@
import React from 'react';
import TweenOne from 'rc-tween-one';
import { Menu } from 'antd';
import { getChildrenToRender } from './utils';
const { Item, SubMenu } = Menu;
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
phoneOpen: undefined,
};
}
phoneClick = () => {
const phoneOpen = !this.state.phoneOpen;
this.setState({
phoneOpen,
});
};
render() {
const { dataSource, isMobile, ...props } = this.props;
const { phoneOpen } = this.state;
const navData = dataSource.Menu.children;
const navChildren = navData.map((item) => {
const { children: a, subItem, ...itemProps } = item;
if (subItem) {
return (
<SubMenu
key={item.name}
{...itemProps}
title={
<div
{...a}
className={`header0-item-block ${a.className}`.trim()}
>
{a.children.map(getChildrenToRender)}
</div>
}
popupClassName="header0-item-child"
>
{subItem.map(($item, ii) => {
const { children: childItem } = $item;
const child = childItem.href ? (
<a {...childItem}>
{childItem.children.map(getChildrenToRender)}
</a>
) : (
<div {...childItem}>
{childItem.children.map(getChildrenToRender)}
</div>
);
return (
<Item key={$item.name || ii.toString()} {...$item}>
{child}
</Item>
);
})}
</SubMenu>
);
}
return (
<Item key={item.name} {...itemProps}>
<a {...a} className={`header0-item-block ${a.className}`.trim()}>
{a.children.map(getChildrenToRender)}
</a>
</Item>
);
});
const moment = phoneOpen === undefined ? 300 : null;
return (
<TweenOne
component="header"
animation={{ opacity: 0, type: 'from' }}
{...dataSource.wrapper}
{...props}
>
<div
{...dataSource.page}
className={`${dataSource.page.className}${phoneOpen ? ' open' : ''}`}
>
<TweenOne
animation={{ x: -30, type: 'from', ease: 'easeOutQuad' }}
{...dataSource.logo}
>
<img width="100%" src={dataSource.logo.children} alt="img" />
</TweenOne>
{isMobile && (
<div
{...dataSource.mobileMenu}
onClick={() => {
this.phoneClick();
}}
>
<em />
<em />
<em />
</div>
)}
<TweenOne
{...dataSource.Menu}
animation={
isMobile
? {
height: 0,
duration: 300,
onComplete: (e) => {
if (this.state.phoneOpen) {
e.target.style.height = 'auto';
}
},
ease: 'easeInOutQuad',
}
: null
}
moment={moment}
reverse={!!phoneOpen}
>
<Menu
mode={isMobile ? 'inline' : 'horizontal'}
defaultSelectedKeys={['sub0']}
theme="dark"
>
{navChildren}
</Menu>
</TweenOne>
</div>
</TweenOne>
);
}
}
export default Header;

View File

@@ -0,0 +1,73 @@
import React from 'react';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import QueueAnim from 'rc-queue-anim';
import { Row, Col, Button } from 'antd';
import { getChildrenToRender } from './utils';
class Pricing1 extends React.PureComponent {
getChildrenToRender = (item) => {
const {
wrapper,
topWrapper,
name,
buttonWrapper,
line,
content,
money,
} = item.children;
return (
<Col key={item.name} {...item}>
<QueueAnim type="bottom" {...wrapper}>
<div {...topWrapper}>
<div {...name} key="name">
{name.children}
</div>
<h1 {...money} key="money">
{money.children}
</h1>
</div>
<div {...content} key="content">
{content.children}
</div>
<i {...line} key="line" />
<div {...buttonWrapper} key="button">
<Button {...buttonWrapper.children.a}>
{buttonWrapper.children.a.children}
</Button>
</div>
</QueueAnim>
</Col>
);
};
render() {
const { ...props } = this.props;
const { dataSource } = props;
delete props.dataSource;
delete props.isMobile;
const { block } = dataSource;
const childrenToRender = block.children.map(this.getChildrenToRender);
return (
<div {...props} {...dataSource.wrapper}>
<div {...dataSource.page}>
<div key="title" {...dataSource.titleWrapper}>
{dataSource.titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack {...dataSource.OverPack}>
<QueueAnim
type="bottom"
component={Row}
leaveReverse
ease={['easeOutQuad', 'easeInOutQuad']}
key="content"
>
{childrenToRender}
</QueueAnim>
</OverPack>
</div>
</div>
);
}
}
export default Pricing1;

View File

@@ -0,0 +1,114 @@
import React from 'react';
import OverPack from 'rc-scroll-anim/lib/ScrollOverPack';
import QueueAnim from 'rc-queue-anim';
import { Table } from 'antd';
import { getChildrenToRender, isImg } from './utils';
class Pricing2 extends React.PureComponent {
getColumns = (columns) => {
return columns.map((item) => {
const { childWrapper, ...$item } = item;
return {
align: 'center',
...$item,
title: (
<div {...childWrapper}>
{childWrapper.children.map(getChildrenToRender)}
</div>
),
};
});
};
getDataSource = (dataSource, columns) =>
dataSource.map((item, i) => {
const obj = { key: i.toString() };
item.children.forEach(($item, ii) => {
if (columns[ii]) {
obj[columns[ii].key] = (
<div {...$item}>
{typeof $item.children === 'string' &&
$item.children.match(isImg) ? (
<img src={$item.children} alt="img" />
) : (
$item.children
)}
</div>
);
}
});
return obj;
});
getMobileChild = (table) => {
const { columns, dataSource, ...tableProps } = table;
const names = columns.children.filter(
(item) => item.key.indexOf('name') >= 0
);
const newColumns = columns.children.filter(
(item) => item.key.indexOf('name') === -1
);
return newColumns.map((item, i) => {
const items = [].concat(names[0], item).filter((c) => c);
if (items.length > 1) {
items[0].colSpan = 0;
items[1].colSpan = 2;
}
const dataSources = dataSource.children.map(($item) => {
const child = $item.children.filter(
(c) => c.name.indexOf('name') === -1
);
const n = $item.children.filter((c) => c.name.indexOf('name') >= 0);
return {
...$item,
children: [].concat(n[0], child[i]).filter((c) => c),
};
});
const props = {
...tableProps,
columns: this.getColumns(items),
dataSource: this.getDataSource(dataSources, items),
};
return (
<Table key={i.toString()} {...props} pagination={false} bordered />
);
});
};
render() {
const { dataSource, isMobile, ...props } = this.props;
const { Table: table, wrapper, page, titleWrapper } = dataSource;
const { columns, dataSource: tableData, ...$table } = table;
const tableProps = {
...$table,
columns: this.getColumns(columns.children),
dataSource: this.getDataSource(tableData.children, columns.children),
};
const childrenToRender = isMobile ? (
this.getMobileChild(table)
) : (
<Table key="table" {...tableProps} pagination={false} bordered />
);
return (
<div {...props} {...wrapper}>
<div {...page}>
<div key="title" {...titleWrapper}>
{titleWrapper.children.map(getChildrenToRender)}
</div>
<OverPack {...dataSource.OverPack}>
<QueueAnim
type="bottom"
leaveReverse
ease={['easeOutQuad', 'easeInOutQuad']}
key="content"
>
{childrenToRender}
</QueueAnim>
</OverPack>
</div>
</div>
);
}
}
export default Pricing2;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
# 如何使用:
- umi 里如何使用[请查看](https://landing.ant.design/docs/use/umi)。
- 其它脚手架使用[请查看](https://landing.ant.design/docs/use/getting-started)。

View File

@@ -0,0 +1,150 @@
/* eslint no-undef: 0 */
/* eslint arrow-parens: 0 */
import { enquireScreen } from "enquire-js";
import React from "react";
import Banner0 from "./Banner0";
// import Content4 from "./Content4";
import Content0 from "./Content0";
import Content1 from "./Content1";
import {
Banner00DataSource,
// Content40DataSource,
Content00DataSource,
Content10DataSource,
// Pricing11DataSource,
// Content30DataSource,
// Content120DataSource,
Footer10DataSource,
Nav00DataSource,
Pricing20DataSource,
} from "./data.source";
// import Pricing1 from "./Pricing1";
// import Content3 from "./Content3";
// import Content12 from "./Content12";
import Footer1 from "./Footer1";
import "./less/antMotionStyle.less";
import Nav0 from "./Nav0";
import Pricing2 from "./Pricing2";
let isMobile;
enquireScreen((b) => {
isMobile = b;
});
const { location = {} } = typeof window !== "undefined" ? window : {};
export default class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
isMobile,
show: !location.port, // 如果不是 dva 2.0 请删除
};
}
componentDidMount() {
// 适配手机屏幕;
enquireScreen((b) => {
this.setState({ isMobile: !!b });
});
// dva 2.0 样式在组件渲染之后动态加载,导致滚动组件不生效;线上不影响;
/* 如果不是 dva 2.0 请删除 start */
if (location.port) {
// 样式 build 时间在 200-300ms 之间;
setTimeout(() => {
this.setState({
show: true,
});
}, 500);
}
/* 如果不是 dva 2.0 请删除 end */
console.log("Setting $crisp segments", ["lead"]);
window.$crisp.push(["set", "session:segments", [["lead"]]]);
window.$crisp.push([
"set",
"session:event",
[[["landing-page", {}, "green"]]],
]);
}
render() {
const children = [
<Nav0
id="Nav0_0"
key="Nav0_0"
dataSource={Nav00DataSource}
isMobile={this.state.isMobile}
/>,
<Banner0
id="Banner0_0"
key="Banner0_0"
dataSource={Banner00DataSource}
isMobile={this.state.isMobile}
/>,
...(process.env.NODE_ENV !== "production"
? [
// <Content4
// id="Content4_0"
// key="Content4_0"
// dataSource={Content40DataSource}
// isMobile={this.state.isMobile}
// />,
<Content1
id="Content1_0"
key="Content1_0"
dataSource={Content10DataSource}
isMobile={this.state.isMobile}
/>,
<Content0
id="Content0_0"
key="Content0_0"
dataSource={Content00DataSource}
isMobile={this.state.isMobile}
/>,
<Pricing2
id="Pricing2_0"
key="Pricing2_0"
dataSource={Pricing20DataSource}
isMobile={this.state.isMobile}
/>,
// <Pricing1
// id="Pricing1_1"
// key="Pricing1_1"
// dataSource={Pricing11DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content3
// id="Content3_0"
// key="Content3_0"
// dataSource={Content30DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content12
// id="Content12_0"
// key="Content12_0"
// dataSource={Content120DataSource}
// isMobile={this.state.isMobile}
// />,
]
: []),
<Footer1
id="Footer1_0"
key="Footer1_0"
dataSource={Footer10DataSource}
isMobile={this.state.isMobile}
/>,
];
return (
<div
className="templates-wrapper"
ref={(d) => {
this.dom = d;
}}
>
{/* 如果不是 dva 2.0 替换成 {children} start */}
{this.state.show && children}
{/* 如果不是 dva 2.0 替换成 {children} end */}
</div>
);
}
}

View File

@@ -0,0 +1,14 @@
@import './common.less';
@import './custom.less';
@import './content.less';
@import './nav0.less';
@import './banner0.less';
@import './content4.less';
@import './content0.less';
@import './content1.less';
@import './pricing2.less';
@import './pricing1.less';
@import './content3.less';
@import './content12.less';
@import './footer1.less';
@import './edit.less';

View File

@@ -0,0 +1,84 @@
@banner0: banner0;
.@{banner0} {
// 如果在第一屏且导航位置为 relative, 一屏为 height: calc(~"100vh - 64px");
width: 100%;
height: 100vh;
position: relative;
text-align: center;
border-color: #666;
background-image: url("../../assets/banner1.jpeg");
background-size: cover;
background-attachment: fixed;
background-position: center;
& &-text-wrapper {
display: inline-block;
position: absolute;
top: 20%;
margin: auto;
left: 0;
right: 0;
font-size: 14px;
color: @template-text-color-light;
width: 550px;
> .queue-anim-leaving {
position: relative !important;
}
}
& &-title {
width: 350px;
//left: 30px;
min-height: 60px;
margin: auto;
display: inline-block;
font-size: 40px;
position: relative;
}
& &-content {
margin-bottom: 20px;
word-wrap: break-word;
min-height: 24px;
}
& &-button {
border: 1px solid #fff;
color: #fff;
background: transparent;
box-shadow: 0 0 0 transparent;
font-size: 16px;
height: 40px;
transition: background 0.45s @ease-out, box-shadow 0.45s @ease-out;
&:hover {
color: #fff;
border-color: #fff;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(50, 250, 255, 0.75);
}
&:focus {
color: #fff;
border-color: #fff;
}
&.queue-anim-leaving {
width: auto;
}
}
& &-icon {
bottom: 20px;
font-size: 24px;
position: absolute;
left: 50%;
margin-left: -12px;
color: @template-text-color-light;
}
}
@media screen and (max-width: 767px) {
.@{banner0} {
background-attachment: inherit;
& &-text-wrapper {
width: 90%;
}
& &-title {
width: 90%;
left: 0;
}
}
}

View File

@@ -0,0 +1,42 @@
// @import "~antd/lib/style/v2-compatible-reset.less";
body {
word-wrap: break-word;
}
body,
div,
dl,
dt,
dd,
ul,
ol,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}
/* .content-wrapper > .tween-one-leaving,
.queue-anim-leaving {
// position: absolute !important;
// width: 100%;
} */
.video {
max-width: 800px;
}
#react-content {
min-height: 100%;
}
.home-page-wrapper p {
padding: 0;
margin: 0;
}

View File

@@ -0,0 +1,44 @@
@homepage: home-page;
.@{homepage}-wrapper {
width: 100%;
position: relative;
overflow: hidden;
.@{homepage} {
height: 100%;
max-width: 1200px;
position: relative;
margin: auto;
will-change: transform;
}
.title-wrapper > h1, > h1 {
font-size: 32px;
color: @text-color;
margin-bottom: 16px;
}
.title-wrapper {
margin: 0 auto 64px;
text-align: center;
}
}
.@{homepage} {
padding: 128px 24px;
}
@media screen and (max-width: 767px) {
.@{homepage}-wrapper {
.@{homepage} {
padding: 56px 24px;
>h1 {
font-size: 24px;
margin: 0 auto 32px;
&.title-h1 {
margin-bottom: 8px;
}
}
>p {
margin-bottom: 32px;
}
}
}
}

View File

@@ -0,0 +1,55 @@
@content0: content0;
.@{content0}-wrapper {
min-height: 446px;
overflow: hidden;
.@{content0} {
height: 100%;
padding: 64px 24px;
>.title-wrapper {
margin: 0 auto 48px;
}
&-block {
padding: 0 4%;
display: inline-block;
text-align: center;
min-height: 200px;
margin-bottom: 24px;
img {
width: 100%;
}
&-wrapper {
position: relative;
height: 100%;
top: 25%;
padding: 20px 0;
}
&.queue-anim-leaving {
position: relative !important;
}
&-icon {
width: 100px;
height: 100px;
margin: auto;
}
&-title {
line-height: 32px;
margin: 10px auto;
font-size: 24px;
}
}
}
}
@media screen and (max-width: 767px) {
.@{content0}-wrapper {
min-height: 880px;
}
}

View File

@@ -0,0 +1,76 @@
@content1: content1;
.@{content1}-wrapper {
height: 360px;
.@{content1} {
height: 100%;
padding: 0 24px;
&-img {
height: 100%;
transform-origin: top;
padding: 0 32px;
display: flex;
align-items: center;
justify-content: center;
span {
display: block;
width: 250px;
img {
display: block;
}
}
}
&-text {
padding: 0 32px;
height: 100%;
.@{content1}-content,
.@{content1}-title {
position: relative !important;
}
.@{content1}-title {
font-size: 32px;
font-weight: normal;
color: #404040;
margin-top: 120px;
}
.content {
margin-top: 20px;
}
}
}
}
@media screen and (max-width: 767px) {
.@{content1}-wrapper {
height: 600px;
.@{content1} {
&-img {
height: 200px;
padding: 0;
text-align: center;
margin-top: 64px;
span {
display: inline-block;
width: 180px;
height: 200px;
line-height: 200px;
margin: auto;
}
}
&-text {
height: auto;
margin-bottom: 20px;
text-align: center;
padding: 0;
.@{content1}-content,
.@{content1}-title {
width: 100%;
top: auto;
}
.@{content1}-title {
margin: 32px auto 16px;
font-size: 24px;
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
@content12: content12;
.@{content12}-wrapper {
background-color: #fafafa;
min-height: 470px;
.@{content12} {
padding: 64px 24px;
>p {
text-align: center;
}
}
.img-wrapper {
margin: 0 auto;
left: 0;
right: 0;
.block {
margin-bottom: 40px;
.block-content {
display: flex;
border-radius: 4px;
text-align: center;
position: relative;
overflow: hidden;
border: none;
height: 64px;
align-items: center;
transition: box-shadow .3s @ease-out, transform .3s @ease-out;
& > span {
width: 100%;
display: block;
}
}
}
}
}
@media screen and (max-width: 767px) {
.@{content12}-wrapper {
overflow: hidden;
.@{content12} {
ul {
li {
display: block;
width: 100%;
padding: 2%;
span {
height: 168px;
}
}
}
}
}
}

View File

@@ -0,0 +1,52 @@
@content3: content3;
.@{content3}-wrapper {
min-height: 764px;
.@{content3} {
height: 100%;
overflow: hidden;
& .title-content {
text-align: center;
}
&-block-wrapper {
position: relative;
.@{content3}-block {
display: inline-block;
padding: 48px 24px;
vertical-align: top;
.@{content3}-icon {
display: inline-block;
width: 15%;
vertical-align: top;
}
.@{content3}-text {
width: 85%;
display: inline-block;
padding-left: 8%;
}
&.clear-both {
clear: both;
}
}
}
}
}
@media screen and (max-width: 767px) {
.@{content3}-wrapper {
min-height: 1080px;
.@{content3} {
&-block-wrapper {
margin: 20px auto;
height: auto;
.@{content3}-block {
.@{content3}-title {
font-size: 20px;
}
&.queue-anim-leaving {
position: relative !important;
}
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
@content4: content4;
.@{content4}-wrapper {
min-height: 720px;
background: #fafafa;
.@{content4} {
height: 100%;
overflow: hidden;
&-video {
border-radius: 4px;
overflow: hidden;
max-width: 800px;
margin: auto;
background: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, .15);
video {
display: block;
margin: auto;
}
}
}
}
@media screen and (max-width: 767px) {
.@{content4}-wrapper {
min-height: 350px;
.@{content4} {
overflow: hidden;
width: 90%;
margin: auto;
&-video {
top: 15%;
background: url("https://zos.alipayobjects.com/rmsportal/HZgzhugQZkqUwBVeNyfz.jpg") no-repeat center;
background-size: cover;
}
}
}
}

View File

@@ -0,0 +1,35 @@
@import "~antd/lib/style/themes/default.less";
@line-color: #e9e9e9;
@shadow-color: rgba(0, 0, 0, 0.15);
@bottom-bar-bg-color: #262626;
@bottom-bar-line-color: #000;
@template-bg-color: #001529;
@template-bg-color-light: #ececec;
@template-nav-bg-color: #001529;
@template-text-color: #ccc;
@template-text-title-color: #bcbcbc;
@template-text-color-light: #fff;
@template-footer-text-color: #999;
@animate-duration: .45s;
/* 详细页图片或框框的样式;
*/
.page-shadow() {
box-shadow: 0 5px 8px @shadow-color;
}
.page-pro() {
border-radius: 6px;
border: 1px solid @line-color;
transform: translateY(0);
transition: transform .3s @ease-out, box-shadow .3s @ease-out;
&:hover {
.page-shadow();
transform: translateY(-5px);
}
}

View File

View File

@@ -0,0 +1,98 @@
.footer1-wrapper {
background: @template-bg-color;
overflow: hidden;
position: relative;
min-height: 360px;
color: @template-footer-text-color;
.footer1 {
.home-page {
padding: 64px 24px 80px;
}
}
.block {
padding: 0 32px;
.logo {
max-width: 180px;
}
.slogan {
font-size: 12px;
margin-top: -20px;
}
>h2 {
margin-bottom: 24px;
color: @template-text-color;
}
a {
color: @template-footer-text-color;
margin-bottom: 12px;
float: left;
clear: both;
&:hover {
color: @primary-color;
}
}
}
.copyright-wrapper {
width: 100%;
border-top: 1px solid fade(@line-color, 10);
.home-page {
padding: 0 24px;
overflow: hidden;
}
.copyright {
height: 80px;
text-align: center;
line-height: 80px;
}
}
}
@media screen and (max-width: 767px) {
.footer1 {
min-height: 550px;
&-wrapper {
.footer1 {
.home-page {
padding: 64px 24px 32px;
}
}
}
.logo {
margin: 0 auto 24px;
}
.block {
text-align: center;
margin-bottom: 32px;
padding: 0;
}
>ul {
width: 90%;
margin: 20px auto 0;
padding: 10px 0;
>li {
width: 100%;
h2 {
margin-bottom: 10px;
}
li {
display: inline-block;
margin-right: 10px;
}
}
}
.copyright {
&-wrapper {
.home-page {
padding: 0;
.copyright {
font-size: 12px;
}
}
}
span {
width: 90%;
}
}
}
}

View File

@@ -0,0 +1,187 @@
@header0: header0;
.@{header0} {
background: @template-nav-bg-color;
width: 100%;
z-index: 1;
box-shadow: 0 5px 8px fade(#000, 15);
position: relative;
top: 0;
.home-page {
padding: 0 24px;
}
&-logo {
display: inline-block;
position: relative;
width: 150px;
line-height: 64px;
& img {
vertical-align: middle;
display: inline-block;
}
& a {
display: block;
}
}
&-menu {
float: right;
.ant-menu {
line-height: 62px;
height: 64px;
a {
display: block;
}
}
}
&-item {
&-block {
padding: 0 8px;
>* {
display: inline-block;
}
}
}
&-item,
&-item-child,
&-menu {
.ant-menu-sub .ant-menu-item,
.ant-menu-inline .ant-menu-item {
height: auto;
line-height: 1.5;
}
.item {
&-sub-item {
display: block;
padding: 8px 24px;
}
&-image {
float: left;
margin-right: 16px;
margin-top: 4px;
position: relative;
z-index: 1;
}
&-title {
font-size: 14px;
color: #fff;
margin-left: 46px;
}
&-content {
font-size: 12px;
color: fade(#fff, 75);
margin-left: 46px;
}
}
}
}
@media screen and (max-width: 767px) {
.@{header0} {
&-logo {
z-index: 101;
}
&.home-page-wrapper .home-page {
padding: 0 24px;
}
&-menu {
height: auto;
float: inherit;
position: relative;
left: -24px;
width: ~"calc(100% + 48px)";
opacity: 0;
transition: opacity .3s @ease-in-out;
& li {
padding: 0 24px;
&.ant-menu-submenu {
padding: 0;
}
}
.item {
&-sub-item {
padding: 8px 0;
}
}
}
&-mobile-menu {
width: 16px;
height: 14px;
cursor: pointer;
position: absolute;
top: 24px;
right: 24px;
z-index: 100;
em {
display: block;
width: 100%;
height: 2px;
background: #fff;
margin-top: 4px;
transition: transform .3s @ease-in-out, opacity .3s @ease-in-out;
}
:first-child {
margin-top: 0;
}
}
.ant-menu {
height: auto;
overflow: hidden;
.ant-menu-item-selected {
border: none;
}
}
& .open {
height: auto;
.@{header0}-mobile-menu {
em {
&:nth-child(1) {
transform: translateY(6px) rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:nth-child(3) {
transform: translateY(-6px) rotate(-45deg);
}
}
}
>.@{header0}-menu {
opacity: 1;
pointer-events: auto;
}
}
&-item-block {
height: 40px;
line-height: 40px;
}
}
}

View File

@@ -0,0 +1,85 @@
@pricing1: pricing1;
.@{pricing1}-wrapper {
min-height: 760px;
.@{pricing1} {
>p {
text-align: center;
}
&-content-wrapper {
min-height: 400px;
}
&-block-box {
width: 260px;
border-radius: 4px;
background: #eef0f3;
text-align: center;
color: #666;
min-height: 400px;
margin: auto;
border: 1px solid transparent;
.page-pro();
&.active {
border-color: @primary-color;
background: #fff;
.@{pricing1} {
&-top-wrapper {
background: @primary-color;
}
&-name,
&-money,
&-button {
color: #fff;
}
&-button {
background: @primary-color;
}
}
}
}
&-block {
margin-bottom: 24px;
}
&-top-wrapper {
width: 100%;
padding: 16px 24px;
}
&-name {
font-size: 14px;
}
&-money {
font-family: 'Helvetica Neue', sans-serif;
font-size: 32px;
color: #666;
}
&-content {
font-size: 12px;
line-height: 2;
font-weight: 300;
margin: 32px 24px 48px;
}
&-line {
display: block;
height: 1px;
background: #d9d9d9;
margin: 0 24px;
}
&-button-wrapper {
margin: 18px 24px;
}
&-button {
padding: 0 24px;
}
}
&.home-page-wrapper {
.@{pricing1}-title-wrapper {
margin-bottom: 64px;
text-align: center;
}
}
}
@media screen and (max-width: 767px) {
.@{pricing1}-wrapper {
padding-bottom: 0;
}
}

View File

@@ -0,0 +1,59 @@
@pricing2: pricing2;
.@{pricing2}-wrapper {
min-height: 760px;
.@{pricing2} {
>p {
text-align: center;
}
&-content-wrapper {
min-height: 400px;
}
&-table-name-block {
text-align: center;
color: #666;
width: 100%;
}
&-table-name {
font-size: 24px;
}
&-table-money {
font-size: 16px;
margin: 8px 0 16px;
}
&-table-content {
text-align: center;
color: #666;
&-name {
color: #666;
text-align: center;
}
}
}
&.home-page-wrapper {
.@{pricing2}-title-wrapper {
margin-bottom: 64px;
text-align: center;
}
}
}
@media screen and (max-width: 767px) {
.@{pricing2} {
&-wrapper {
padding-bottom: 0;
}
&-table {
margin-bottom: 24px;
}
}
}

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { Button } from 'antd';
export const isImg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w-./?%&=]*)?/;
export const getChildrenToRender = (item, i) => {
let tag = item.name.indexOf('title') === 0 ? 'h1' : 'div';
tag = item.href ? 'a' : tag;
let children = typeof item.children === 'string' && item.children.match(isImg)
? React.createElement('img', { src: item.children, alt: 'img' })
: item.children;
if (item.name.indexOf('button') === 0 && typeof item.children === 'object') {
children = React.createElement(Button, {
...item.children
});
}
return React.createElement(tag, { key: i.toString(), ...item }, children);
};

View File

@@ -12,7 +12,6 @@ import AlertComponent from "../../components/alert/alert.component";
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -79,12 +78,10 @@ export function ExportLogsPageComponent({ bodyshop }) {
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) =>
record.job && (
<Link to={"/manage/jobs/" + record.job && record.job.id}>
<Link to={`/manage/jobs/${record.job.id}`}>
{(record.job && record.job.ro_number) || t("general.labels.na")}
</Link>
),

View File

@@ -15,6 +15,8 @@ import JobAdminOwnerReassociate from "../../components/jobs-admin-owner-reassoci
import JobsAdminUnvoid from "../../components/jobs-admin-unvoid/jobs-admin-unvoid.component";
import JobAdminVehicleReassociate from "../../components/jobs-admin-vehicle-reassociate/jobs-admin-vehicle-reassociate.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import JobsAdminStatus from "../../components/jobs-admin-change-status/jobs-admin-change.status.component";
import NotFound from "../../components/not-found/not-found.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
@@ -96,6 +98,7 @@ export function JobsCloseContainer({ setBreadcrumbs, setSelectedHeader }) {
<JobsAdminDeleteIntake job={data ? data.jobs_by_pk : {}} />
<JobsAdminMarkReexport job={data ? data.jobs_by_pk : {}} />
<JobsAdminUnvoid job={data ? data.jobs_by_pk : {}} />
<JobsAdminStatus job={data ? data.jobs_by_pk : {}} />
</Space>
</Card>
</Col>

View File

@@ -42,9 +42,26 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
setLoading(true);
const result = await client.mutate({
mutation: generateJobLinesUpdatesForInvoicing(values.joblines),
});
if (result.errors) {
return; // Abandon the rest of the close.
}
const closeResult = await closeJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(),
actual_in: values.actual_in,
actual_completion: values.actual_completion,
actual_delivery: values.actual_delivery,
},
},
refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"],
awaitRefetchQueries: true,
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
// form.resetFields();
@@ -56,18 +73,6 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
});
return; // Abandon the rest of the close.
}
form.resetFields();
form.resetFields();
const closeResult = await closeJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(),
},
},
});
if (!closeResult.errors) {
setLoading(false);
@@ -84,6 +89,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
}),
});
}
form.resetFields();
form.resetFields();
setLoading(false);
};

View File

@@ -3,6 +3,7 @@ import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import LandingPageStatic from "../../landing/index";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -13,5 +14,6 @@ export default connect(mapStateToProps, null)(LandingPage);
export function LandingPage({ currentUser }) {
if (currentUser.authorized) return <Redirect to={"/manage"} />;
return <Redirect to={"/signin"} />;
return <LandingPageStatic />;
//return <Redirect to={"/signin"} />;
}

View File

@@ -15,7 +15,6 @@ import FcmNotification from "../../components/fcm-notification/fcm-notification.
//import FooterComponent from "../../components/footer/footer.component";
//Component Imports
import HeaderContainer from "../../components/header/header.container";
import JiraSupportComponent from "../../components/jira-support-widget/jira-support-widget.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import PartnerPingComponent from "../../components/partner-ping/partner-ping.component";
import PrintCenterModalContainer from "../../components/print-center-modal/print-center-modal.container";
@@ -408,10 +407,9 @@ export function Manage({ match, conflict, bodyshop }) {
</div>
<div id="noticeable-widget" style={{ marginLeft: "1rem" }} />
</div>
<Link to="/about" target="_blank" style={{ color: "#ccc" }}>
<Link to="/disclaimer" target="_blank" style={{ color: "#ccc" }}>
Disclaimer & Notices
</Link>
<JiraSupportComponent />
</div>
</Footer>
</Content>

View File

@@ -7,6 +7,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import ManagePage from "./manage.page.component";
import "../../utils/RegisterSw";
const mapDispatchToProps = (dispatch) => ({
setBodyshop: (bs) => dispatch(setBodyshop(bs)),

View File

@@ -9,6 +9,7 @@ import LoadingSpinner from "../../components/loading-spinner/loading-spinner.com
import { useTranslation } from "react-i18next";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { createStructuredSelector } from "reselect";
import "../../utils/RegisterSw";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,

View File

@@ -17,7 +17,7 @@ export function* onCalculateScheduleLoad() {
}
export function* calculateScheduleLoad({ payload: end }) {
//REMINDER: Moment.js is not immutable. Today WILL change when adjusted.
const today = moment(new Date()).startOf("day");
const today = moment().startOf("day");
const state = yield select();
const buckets = state.user.bodyshop.ssbuckets;
@@ -42,6 +42,7 @@ export function* calculateScheduleLoad({ payload: end }) {
});
prodJobs.forEach((item) => {
//Add all of the jobs currently in production to the buckets so that we have a starting point.
const bucketId = CheckJobBucket(buckets, item);
if (bucketId) {
load.productionTotal[bucketId].count =

View File

@@ -166,11 +166,14 @@ export function* signInSuccessSaga({ payload }) {
LogRocket.identify(payload.email);
try {
// window.$crisp.push(["set", "user:email", [payload.email]]);
console.log("$crisp set nickname", [payload.displayName || payload.email]);
window.$crisp.push([
"set",
"user:nickname",
[payload.displayName || payload.email],
]);
console.log("Setting $crisp segments", ["user"]);
window.$crisp.push(["set", "session:segments", [["user"]]]);
} catch (error) {
console.log("Error updating Crisp settings.", error);
}

View File

@@ -24,6 +24,7 @@
"cancel": "Cancel",
"intake": "Intake",
"new": "New Appointment",
"preview": "Preview",
"reschedule": "Reschedule",
"sendreminder": "Send Reminder",
"viewjob": "View Job"
@@ -198,6 +199,7 @@
"label": "Label"
},
"appt_length": "Default Appointment Length",
"attach_pdf_to_email": "Attach PDF copy to sent emails?",
"bill_federal_tax_rate": "Bills - Federal Tax Rate %",
"bill_local_tax_rate": "Bill - Provincial/State Tax Rate %",
"bill_state_tax_rate": "Bill - Provincial/State Tax Rate %",
@@ -1033,6 +1035,8 @@
"intake": "Intake",
"manualnew": "Create New Job Manually",
"mark": "Mark",
"markpstexempt": "Mark Job PST Exempt",
"markpstexemptconfirm": "Are you sure you want to do this? To undo this, you must manually update all PST rates.",
"postbills": "Post Bills",
"printCenter": "Print Center",
"recalculate": "Recalculate",
@@ -1422,6 +1426,56 @@
"voided": "Job voided successfully."
}
},
"landing": {
"bigfeature": {
"subtitle": "ImEX Online is built using world class technology by experts in the collision repair industry. This translates to software that is tailor made for the unique challenges faced by repair facilities with no compromises. ",
"title": "Bringing the latest technology to the automotive repair industry. "
},
"footer": {
"company": {
"about": "About Us",
"contact": "Contact",
"disclaimers": "Disclaimers",
"name": "Company",
"privacypolicy": "Privacy Policy"
},
"io": {
"help": "Help",
"name": "ImEX Online",
"status": "System Status"
},
"slogan": "A whole new kind of shop management system."
},
"hero": {
"button": "Coming Soon",
"title": "A whole new kind of shop management system."
},
"labels": {
"features": "Features",
"managemyshop": "Manage my Shop",
"pricing": "Pricing"
},
"pricing": {
"basic": {
"name": "Basic",
"sub": "Best suited for shops looking to increase their volume."
},
"essentials": {
"name": "Essentials",
"sub": "Best suited for small and low volume shops."
},
"pricingtitle": "Features",
"pro": {
"name": "Pro",
"sub": "Empower your shop with the tools to operate at peak capacity."
},
"title": "Features",
"unlimited": {
"name": "Unlimited",
"sub": "Everything you need and more for the high volume shop."
}
}
},
"menus": {
"currentuser": {
"languageselector": "Language",
@@ -1779,6 +1833,8 @@
"invoice_total_payable": "Invoice (Total Payable)",
"job_costing_ro": "Job Costing",
"job_notes": "Job Notes",
"key_tag": "Key Tag",
"paint_grid": "Paint Grid",
"parts_label_single": "Parts Label - Single",
"parts_list": "Parts List",
"parts_order": "Parts Order Confirmation",
@@ -1793,6 +1849,7 @@
"qc_sheet": "Quality Control Sheet",
"ro_totals": "RO Totals",
"ro_with_description": "RO Summary with Descriptions",
"stolen_recovery_checklist": "Stolen Recovery Checklist",
"supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter",
"thirdpartypayer": "Third Party Payer",
@@ -1909,6 +1966,7 @@
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
"gsr_by_csr": "Gross Sales by CSR",
"gsr_by_delivery_date": "Gross Sales by Delivery Date",
"gsr_by_estimator": "Gross Sales by Estimator",
"gsr_by_exported_date": "Gross Sales by Export Date",
@@ -1930,6 +1988,7 @@
"job_costing_ro_estimator": "Job Costing by Estimator",
"job_costing_ro_ins_co": "Job Costing by RO Source",
"open_orders": "Open Orders by Date",
"open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator",
"open_orders_ins_co": "Open Orders by Insurance Company",
"parts_backorder": "Backordered Parts",
@@ -2231,7 +2290,7 @@
"city": "City",
"cost_center": "Cost Center",
"country": "Country",
"discount": "Discount %",
"discount": "Discount % (as decimal)",
"display_name": "Display Name",
"due_date": "Payment Due Date",
"email": "Contact Email",

View File

@@ -24,6 +24,7 @@
"cancel": "Cancelar",
"intake": "Consumo",
"new": "Nueva cita",
"preview": "",
"reschedule": "Reprogramar",
"sendreminder": "",
"viewjob": "Ver trabajo"
@@ -198,6 +199,7 @@
"label": ""
},
"appt_length": "",
"attach_pdf_to_email": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
@@ -1033,6 +1035,8 @@
"intake": "",
"manualnew": "",
"mark": "",
"markpstexempt": "",
"markpstexemptconfirm": "",
"postbills": "Contabilizar facturas",
"printCenter": "Centro de impresión",
"recalculate": "",
@@ -1422,6 +1426,56 @@
"voided": ""
}
},
"landing": {
"bigfeature": {
"subtitle": "",
"title": ""
},
"footer": {
"company": {
"about": "",
"contact": "",
"disclaimers": "",
"name": "",
"privacypolicy": ""
},
"io": {
"help": "",
"name": "",
"status": ""
},
"slogan": ""
},
"hero": {
"button": "",
"title": ""
},
"labels": {
"features": "",
"managemyshop": "",
"pricing": ""
},
"pricing": {
"basic": {
"name": "",
"sub": ""
},
"essentials": {
"name": "",
"sub": ""
},
"pricingtitle": "",
"pro": {
"name": "",
"sub": ""
},
"title": "",
"unlimited": {
"name": "",
"sub": ""
}
}
},
"menus": {
"currentuser": {
"languageselector": "idioma",
@@ -1779,6 +1833,8 @@
"invoice_total_payable": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -1793,6 +1849,7 @@
"qc_sheet": "",
"ro_totals": "",
"ro_with_description": "",
"stolen_recovery_checklist": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -1909,6 +1966,7 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
"gsr_by_exported_date": "",
@@ -1930,6 +1988,7 @@
"job_costing_ro_estimator": "",
"job_costing_ro_ins_co": "",
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",

View File

@@ -24,6 +24,7 @@
"cancel": "annuler",
"intake": "Admission",
"new": "Nouveau rendez-vous",
"preview": "",
"reschedule": "Replanifier",
"sendreminder": "",
"viewjob": "Voir le travail"
@@ -198,6 +199,7 @@
"label": ""
},
"appt_length": "",
"attach_pdf_to_email": "",
"bill_federal_tax_rate": "",
"bill_local_tax_rate": "",
"bill_state_tax_rate": "",
@@ -1033,6 +1035,8 @@
"intake": "",
"manualnew": "",
"mark": "",
"markpstexempt": "",
"markpstexemptconfirm": "",
"postbills": "Poster des factures",
"printCenter": "Centre d'impression",
"recalculate": "",
@@ -1422,6 +1426,56 @@
"voided": ""
}
},
"landing": {
"bigfeature": {
"subtitle": "",
"title": ""
},
"footer": {
"company": {
"about": "",
"contact": "",
"disclaimers": "",
"name": "",
"privacypolicy": ""
},
"io": {
"help": "",
"name": "",
"status": ""
},
"slogan": ""
},
"hero": {
"button": "",
"title": ""
},
"labels": {
"features": "",
"managemyshop": "",
"pricing": ""
},
"pricing": {
"basic": {
"name": "",
"sub": ""
},
"essentials": {
"name": "",
"sub": ""
},
"pricingtitle": "",
"pro": {
"name": "",
"sub": ""
},
"title": "",
"unlimited": {
"name": "",
"sub": ""
}
}
},
"menus": {
"currentuser": {
"languageselector": "La langue",
@@ -1779,6 +1833,8 @@
"invoice_total_payable": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -1793,6 +1849,7 @@
"qc_sheet": "",
"ro_totals": "",
"ro_with_description": "",
"stolen_recovery_checklist": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -1909,6 +1966,7 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"gsr_by_csr": "",
"gsr_by_delivery_date": "",
"gsr_by_estimator": "",
"gsr_by_exported_date": "",
@@ -1930,6 +1988,7 @@
"job_costing_ro_estimator": "",
"job_costing_ro_ins_co": "",
"open_orders": "",
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"parts_backorder": "",

View File

@@ -1,9 +1,9 @@
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import en_Translation from "./en_us/common.json";
import fr_Translation from "./fr/common.json";
import es_Translation from "./es/common.json";
import fr_Translation from "./fr/common.json";
// the translations
// (tip move them in a JSON file and import them)

View File

@@ -0,0 +1,46 @@
import { AlertOutlined } from "@ant-design/icons";
import { Button, notification, Space } from "antd";
import i18n from "i18next";
import React from "react";
import * as serviceWorkerRegistration from "../serviceWorkerRegistration";
const onServiceWorkerUpdate = (registration) => {
console.log("onServiceWorkerUpdate", registration);
const btn = (
<Space flex>
<Button
onClick={async () => {
window.open("https://imex-online.noticeable.news/", "_blank");
}}
>
{i18n.t("general.actions.viewreleasenotes")}
</Button>
<Button
type="primary"
onClick={async () => {
if (registration && registration.waiting) {
await registration.unregister();
// Makes Workbox call skipWaiting()
registration.waiting.postMessage({ type: "SKIP_WAITING" });
// Once the service worker is unregistered, we can reload the page to let
// the browser download a fresh copy of our app (invalidating the cache)
window.location.reload();
}
}}
>
{i18n.t("general.actions.refresh")}
</Button>
</Space>
);
notification.open({
icon: <AlertOutlined />,
message: i18n.t("general.messages.newversiontitle"),
description: i18n.t("general.messages.newversionmessage"),
duration: 0,
btn,
key: "updateavailable",
});
};
serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });

View File

@@ -8,6 +8,7 @@ import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store";
import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
import _ from "lodash";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -39,8 +40,10 @@ export default async function RenderTemplate(
offset: moment().utcOffset(),
},
};
try {
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {
render.download(
(Templates[templateObject.name] &&
@@ -48,8 +51,21 @@ export default async function RenderTemplate(
""
);
} else {
let pdf;
if (bodyshop.attach_pdf_to_email) {
const pdfRequest = _.cloneDeep(reportRequest);
pdfRequest.template.recipe = "chrome-pdf";
const pdfRender = await jsreport.renderAsync(pdfRequest);
pdf = pdfRender.toDataURI();
}
return new Promise((resolve, reject) => {
resolve(render.toString());
resolve({
pdf,
filename:
Templates[templateObject.name] &&
Templates[templateObject.name].title,
html: render.toString(),
});
});
}
} catch (error) {

View File

@@ -69,6 +69,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "pre",
},
stolen_recovery_checklist: {
title: i18n.t("printcenter.jobs.stolen_recovery_checklist"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.stolen_recovery_checklist"),
key: "stolen_recovery_checklist",
disabled: false,
group: "pre",
},
vehicle_check_in: {
title: i18n.t("printcenter.jobs.vehicle_check_in"),
description: "All Jobs Notes",
@@ -150,6 +158,22 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "ro",
},
key_tag: {
title: i18n.t("printcenter.jobs.key_tag"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.key_tag"),
key: "key_tag",
disabled: false,
group: "ro",
},
paint_grid: {
title: i18n.t("printcenter.jobs.paint_grid"),
description: "All Jobs Notes",
subject: i18n.t("printcenter.jobs.paint_grid"),
key: "paint_grid",
disabled: false,
group: "ro",
},
worksheet_by_line_number: {
title: i18n.t("printcenter.jobs.worksheet_by_line_number"),
description: "All Jobs Notes",
@@ -840,6 +864,18 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
gsr_by_csr: {
title: i18n.t("reportcenter.templates.gsr_by_csr"),
description: "",
subject: i18n.t("reportcenter.templates.gsr_by_csr"),
key: "gsr_by_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
},
gsr_by_make: {
title: i18n.t("reportcenter.templates.gsr_by_make"),
description: "",
@@ -949,6 +985,18 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open"),
},
},
open_orders_csr: {
title: i18n.t("reportcenter.templates.open_orders_csr"),
description: "",
subject: i18n.t("reportcenter.templates.open_orders_csr"),
key: "open_orders_csr",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
},
open_orders_estimator: {
title: i18n.t("reportcenter.templates.open_orders_estimator"),
description: "",

View File

@@ -3472,7 +3472,7 @@ babel-preset-react-app@^10.0.0:
babel-plugin-macros "2.8.0"
babel-plugin-transform-react-remove-prop-types "0.4.24"
babel-runtime@^6.26.0:
babel-runtime@6.x, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
@@ -4892,6 +4892,13 @@ deep-diff@^0.3.5:
resolved "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz"
integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=
deep-eql@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
dependencies:
type-detect "^4.0.0"
deep-equal@^1.0.1:
version "1.1.1"
resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz"
@@ -5313,6 +5320,18 @@ enhanced-resolve@^4.3.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
enquire-js@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/enquire-js/-/enquire-js-0.2.1.tgz#f2478cf5808d42f54e8231f20fa133493e7f0fcb"
integrity sha512-4vbcWD0ncK6VQ5M5giOImQb2hFPrKDZH5U+uRX9S6e9vfC6Q5PX6A38PVS6RMnCdr/luDTtJjjLuJinH/+a+Lw==
dependencies:
enquire.js "^2.1.6"
enquire.js@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814"
integrity sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ=
enquirer@^2.3.5:
version "2.3.6"
resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
@@ -10179,7 +10198,7 @@ prompts@2.4.0, prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@15.x, prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@15.x, prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.8, 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.npmjs.org/prop-types/-/prop-types-15.7.2.tgz"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -10335,7 +10354,7 @@ raf-schd@^4.0.2:
resolved "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
raf@^3.4.0, raf@^3.4.1:
raf@3.x, raf@^3.4.0, raf@^3.4.1, raf@~3.4.0:
version "3.4.1"
resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
@@ -10552,6 +10571,16 @@ rc-progress@~3.1.0:
"@babel/runtime" "^7.10.1"
classnames "^2.2.6"
rc-queue-anim@^1.8.5:
version "1.8.5"
resolved "https://registry.yarnpkg.com/rc-queue-anim/-/rc-queue-anim-1.8.5.tgz#01ef3872bdfa0b70bb915ef9a637fc404244a589"
integrity sha512-vTbZXBu7L3NcXUPjkFN7R80BE+1VYL1QBI10EioYYqZMk6A0iVbjoVAgfoK/Z4gIIxp399gJ8LqbMnQA1AhcXA==
dependencies:
babel-runtime "6.x"
prop-types "^15.6.0"
rc-tween-one "^2.5.0"
react-lifecycles-compat "^3.0.4"
rc-rate@~2.9.0:
version "2.9.1"
resolved "https://registry.npmjs.org/rc-rate/-/rc-rate-2.9.1.tgz"
@@ -10571,6 +10600,18 @@ rc-resize-observer@^1.0.0:
rc-util "^5.0.0"
resize-observer-polyfill "^1.5.1"
rc-scroll-anim@^2.7.6:
version "2.7.6"
resolved "https://registry.yarnpkg.com/rc-scroll-anim/-/rc-scroll-anim-2.7.6.tgz#f7e6622f2930ca3e1e258f7275bc2e1c26ce791c"
integrity sha512-VwXJYXjZy9TtH1wcQIG7/yjt/Ay3VEjQl/TITaWzK9O7ujjOXRVOYY/tqKshmBMgaJ2oGeFQNmCN8zTwXguq0g==
dependencies:
babel-runtime "6.x"
prop-types "^15.6.0"
raf "3.x"
rc-tween-one "^2.4.0"
react-lifecycles-compat "^3.0.4"
tween-functions "1.x"
rc-select@^12.0.0, rc-select@~12.1.6:
version "12.1.10"
resolved "https://registry.npmjs.org/rc-select/-/rc-select-12.1.10.tgz"
@@ -10687,6 +10728,30 @@ rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.1:
rc-motion "^2.0.0"
rc-util "^5.5.0"
rc-tween-one@^1.2.5:
version "1.8.1"
resolved "https://registry.yarnpkg.com/rc-tween-one/-/rc-tween-one-1.8.1.tgz#5b3b464b9bf2c369efc16c816cccf57bc16ae253"
integrity sha512-Avg8EXHdt6ABV9WnmTmh6zEaAzUvl4bFZKbP3y6BE8UGBGp1qUhlIgCB83gL+5eA0VECdM/b9PsEBRrcxzSpGw==
dependencies:
babel-runtime "6.x"
deep-eql "~3.0.1"
prop-types "^15.6.1"
raf "~3.4.0"
style-utils "~0.1.13"
tween-functions "~1.2.0"
rc-tween-one@^2.4.0, rc-tween-one@^2.5.0:
version "2.7.3"
resolved "https://registry.yarnpkg.com/rc-tween-one/-/rc-tween-one-2.7.3.tgz#c9d3c44743e25c654d65c22ec3934afd79923fae"
integrity sha512-n4OPRLO6VMZHj61unq5KKxHMMfBz52bxob94fN3U5M9GqFg3H+T3TXnpHPnAK+cq/xBGo70ik2vB4Fpjo/txcA==
dependencies:
babel-runtime "6.x"
prop-types "^15.6.1"
raf "~3.4.0"
react-lifecycles-compat "^3.0.4"
style-utils "~0.2.0"
tween-functions "~1.2.0"
rc-upload@~4.3.0:
version "4.3.1"
resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.3.1.tgz"
@@ -11064,6 +11129,14 @@ react-smooth@^2.0.0:
raf "^3.4.0"
react-transition-group "2.9.0"
react-sublime-video@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/react-sublime-video/-/react-sublime-video-0.2.5.tgz#c967b8e9a374b36d6a5d1b63b93fa6ec02e54842"
integrity sha1-yWe46aN0s21qXRtjuT+m7ALlSEI=
dependencies:
prop-types "^15.5.10"
rc-tween-one "^1.2.5"
react-transition-group@2, react-transition-group@2.9.0:
version "2.9.0"
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz"
@@ -12441,6 +12514,16 @@ style-loader@1.3.0:
loader-utils "^2.0.0"
schema-utils "^2.7.0"
style-utils@~0.1.13:
version "0.1.24"
resolved "https://registry.yarnpkg.com/style-utils/-/style-utils-0.1.24.tgz#fc0675d79a0b201bf86fc5d5a1dd202f838de544"
integrity sha512-MVZSKubpU/vIfpmOsi8/0ckWxb0WmGBmyNoEDGWZM9cM8n8sCL6DJftl3lEf8Uy5zKQ9+O1XdJxscWTDosCQpQ==
style-utils@~0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/style-utils/-/style-utils-0.2.1.tgz#c78fe6696214f4ab12701959f09553e9d81dd45b"
integrity sha512-eKRIfWnUSdBqe2ko+qisUwBSlfWpHru89geRqzmScpDhkPW1ksmE04d//nDcXeF+TVK5cnBG90mMmHgxyxXleQ==
styled-components@^5.3.0:
version "5.3.0"
resolved "https://registry.npmjs.org/styled-components/-/styled-components-5.3.0.tgz"
@@ -12848,6 +12931,11 @@ tty-browserify@0.0.0:
resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz"
integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
tween-functions@1.x, tween-functions@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"
integrity sha1-GuOlDnxguz3vd06scHrLynO7w/8=
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz"
@@ -12862,7 +12950,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-detect@4.0.8:
type-detect@4.0.8, type-detect@^4.0.0:
version "4.0.8"
resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,11 @@
- args:
cascade: false
read_only: false
sql: "CREATE OR REPLACE FUNCTION public.search_payments(search text)\n RETURNS
SETOF payments\n LANGUAGE plpgsql\n STABLE\nAS $function$\n\nBEGIN\n if search
= '' then\n return query select * from payments ;\n else \n return query
SELECT\n p.*\nFROM\n payments p, jobs j\nWHERE\np.jobid = j.id AND\n(\nsearch
<% p.paymentnum OR\nsearch <% j.ownr_fn OR\nsearch <% j.ownr_ln OR\nsearch <%
j.ownr_co_nm OR\nsearch <% j.ro_number OR\n search <% (p.payer) OR\n search
<% (p.transactionid) OR\n search <% (p.memo));\n end if;\n\n\tEND\n$function$;"
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."vehicles" add constraint "vehicles_v_vin_shopid_key"
unique ("v_vin", "shopid");
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: alter table "public"."vehicles" drop constraint "vehicles_v_vin_shopid_key";
type: run_sql

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "attach_pdf_to_email";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "attach_pdf_to_email" boolean
NOT NULL DEFAULT False;
type: run_sql

View File

@@ -0,0 +1,87 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- cdk_dealerid
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- features
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,88 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- attach_pdf_to_email
- bill_tax_rates
- cdk_dealerid
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- features
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- jobsizelimit
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- sub_status
- target_touchtime
- template_header
- textid
- updated_at
- use_fippa
- website
- workingdays
- zip_post
computed_fields: []
filter:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,79 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- enforce_referral
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- jc_hourly_rates
- logo_img_path
- md_categories
- md_ccc_rates
- md_classes
- md_hour_split
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_payment_types
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- use_fippa
- website
- workingdays
- zip_post
filter:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

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