Compare commits

...

740 Commits

Author SHA1 Message Date
Patrick Fic
5db43dd065 IO-70 Add basic functions of OEC. 2021-11-29 15:04:34 -08:00
Patrick Fic
53be0bbc1a IO-70 OEC WIP. 2021-11-29 08:57:44 -08:00
Patrick Fic
bfcc03850b IO-70 Add OEC Through Partner Call. 2021-11-23 00:30:11 -08:00
Patrick Fic
65402c1420 Clean up console log statements. 2021-11-22 23:20:13 -08:00
Patrick Fic
b462b2fa03 IO-1485 Auto CC on parts order. 2021-11-22 15:39:12 -08:00
Patrick Fic
0c26b90591 IO-1533 Resolve board rerender. 2021-11-21 21:59:33 -08:00
Patrick Fic
f8f2384c54 Merged in release/2021-11-19 (pull request #272)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-19 20:08:53 +00:00
Patrick Fic
ef18cf0718 IO-233 CDK Updates. 2021-11-19 10:50:38 -08:00
Patrick Fic
9d35fc85ad Merged in release/2021-11-19 (pull request #271)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 18:39:14 +00:00
Patrick Fic
e98f9763fd Resolve QB Payments Refund Issue. 2021-11-18 10:38:04 -08:00
Patrick Fic
95e5385cd1 Merged in release/2021-11-19 (pull request #270)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 17:19:57 +00:00
Patrick Fic
46abf01366 IO-1533 Resolve defaults check issue for production board. 2021-11-18 09:19:34 -08:00
Patrick Fic
87f06425e1 Merged in release/2021-11-19 (pull request #269)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:19:53 +00:00
Patrick Fic
45ab7543d5 Resolve CI Issues. 2021-11-17 22:18:09 -08:00
Patrick Fic
120e4fc94c Merged in release/2021-11-19 (pull request #268)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:17:09 +00:00
Patrick Fic
fed16efd10 IO-1533 Added check for null settings on kanban. 2021-11-17 22:16:43 -08:00
Patrick Fic
ea6277c586 Merged in release/2021-11-19 (pull request #267)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:08:21 +00:00
Patrick Fic
49bf461c36 IO-1533 Kanban Customizations 2021-11-17 22:07:50 -08:00
Patrick Fic
696781c857 IO-1533 Kanban Customizations 2021-11-17 21:47:05 -08:00
Patrick Fic
5540872f62 IO-1531 CC Contract add to production. 2021-11-17 20:18:21 -08:00
Patrick Fic
dac53a56c3 Merged in release/2021-11-19 (pull request #266)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 01:30:23 +00:00
Patrick Fic
69690f0184 IO-1533 Kanban Customizations 2021-11-17 15:29:04 -08:00
Patrick Fic
f11a4c93ac IO-233 CDK Updates 2021-11-17 14:44:07 -08:00
Patrick Fic
d74870812e Merged in release/2021-11-19 (pull request #265)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-16 01:17:31 +00:00
Patrick Fic
9eed33b5f2 Resolve CI issues with missing imports. 2021-11-15 17:15:51 -08:00
Patrick Fic
55b7715e1c IO-1529 Added gateway templates. 2021-11-15 17:14:12 -08:00
Patrick Fic
c129b5ba8c IO-1533 Kanban Customization 2021-11-15 13:53:54 -08:00
Patrick Fic
efc2844d99 IO-1530 Email individual note. 2021-11-15 11:06:10 -08:00
Patrick Fic
ab38f85d38 Package updates. 2021-11-15 10:36:41 -08:00
Patrick Fic
81eccba393 Merged in release/2021-11-12 (pull request #263)
release/2021-11-12

Approved-by: Patrick Fic
2021-11-12 21:46:33 +00:00
Patrick Fic
b3b49fd4ca Email test fix & Shop Config validation for resp centers. 2021-11-12 13:46:01 -08:00
Patrick Fic
b7a08db4e7 Merged in release/2021-11-12 (pull request #262)
IO-117 PBS WIP

Approved-by: Patrick Fic
2021-11-11 02:13:28 +00:00
Patrick Fic
2d63c3d576 IO-1528 Autohouse Updates. 2021-11-10 18:06:24 -08:00
Patrick Fic
5f4a2392af Costmetic updates & limit of conversations in subscription. 2021-11-10 17:09:09 -08:00
Patrick Fic
f6fe8be7c4 IO-1491 Simplify production query to use ignore employee relationships and match locally. 2021-11-10 16:54:02 -08:00
Patrick Fic
89af6d23e8 IO-1513 Added next contact date. 2021-11-10 16:22:52 -08:00
Patrick Fic
7e1431d65e IO-117 WIP PBS 2021-11-10 15:30:07 -08:00
Patrick Fic
d50e845ba0 IO-117 PBS WIP 2021-11-10 08:18:29 -08:00
Patrick Fic
6796c35e5b Merged in release/2021-11-12 (pull request #260)
release/2021-11-12

Approved-by: Patrick Fic
2021-11-10 01:06:57 +00:00
Patrick Fic
4a68a10005 IO-1486 Add dropdown for file handler. 2021-11-09 17:05:20 -08:00
Patrick Fic
17088b3025 IO-1188 Add AB proof of loss. 2021-11-09 16:33:20 -08:00
Patrick Fic
4e3c659b6d IO-1497 Ability to delete parts orders. 2021-11-09 16:31:57 -08:00
Patrick Fic
6afcf82cc4 IO-1505 Resolve PST exempt exporting to QBO. 2021-11-09 16:04:16 -08:00
Patrick Fic
f4ddb40bde IO-117 PBS WIP 2021-11-09 13:56:52 -08:00
Patrick Fic
6017e2172e IO-117 PBS WIP 2021-11-09 10:38:07 -08:00
Patrick Fic
bef9e64bf8 Resolve associations permissions to allow adjustments of RBAC. 2021-11-08 11:23:37 -08:00
Patrick Fic
e8d95bdb68 IO-1508 Add # of jobs in production. 2021-11-08 11:12:56 -08:00
Patrick Fic
10fb7d9d96 IO-1499 Reconciliation to consider quantity. 2021-11-08 10:50:21 -08:00
Patrick Fic
2ec196e664 IO-1510 Missing report templates 2021-11-08 10:29:16 -08:00
Patrick Fic
1f38b98bfd Package Updates 2021-11-08 10:27:38 -08:00
Patrick Fic
a7c76386bc Merged in release/2021-11-01 (pull request #258)
release/2021-11-01

Approved-by: Patrick Fic
2021-11-01 16:00:52 +00:00
Patrick Fic
042b67c531 IO-1438 Resolve issue with texting 2021-11-01 08:57:30 -07:00
Patrick Fic
2e72ed3698 IO-1438 Resolve Texting Issue 2021-11-01 08:40:53 -07:00
Patrick Fic
bc01f46388 Merge branch 'release/2021-10-29' into test 2021-10-29 13:48:27 -07:00
Patrick Fic
74f791541f IO-256 Resolve no invoice on CM QBO export. 2021-10-29 13:48:17 -07:00
Patrick Fic
84c7fdba5a Merge branch 'release/2021-10-29' into test 2021-10-29 11:30:53 -07:00
Patrick Fic
630fa23f6c Resolve uneeded import CI error. 2021-10-29 11:30:43 -07:00
Patrick Fic
8f58f09c8c Merge branch 'release/2021-10-29' into test 2021-10-29 11:19:36 -07:00
Patrick Fic
a0922f2944 IO-1476 Add Hrs to schedule modal. 2021-10-29 11:18:17 -07:00
Patrick Fic
b8f001625b IO-1502 PST on Adjustments for QBO 2021-10-29 10:55:35 -07:00
Patrick Fic
53394efebf IO-1503 QBO Credit Memos 2021-10-28 09:56:59 -07:00
Patrick Fic
d8296e1d01 Merge branch 'release/2021-10-29' into test 2021-10-27 11:37:04 -07:00
Patrick Fic
2c00e5ee79 IO-256 Resolve QBO Aposstroph Issues 2021-10-27 11:36:40 -07:00
Patrick Fic
34e220dcad IO-233 Update Regex for CDK. 2021-10-27 10:46:06 -07:00
Patrick Fic
fa1fffd8b9 Merge branch 'release/2021-10-29' into test 2021-10-27 08:39:20 -07:00
Patrick Fic
67eb430ff9 Add missing query field to CDK. 2021-10-27 08:39:05 -07:00
Patrick Fic
ff53355b4a Merge branch 'release/2021-10-29' into test 2021-10-27 08:30:03 -07:00
Patrick Fic
331b2c517b Update CDK Country Inclusion. 2021-10-27 08:29:43 -07:00
Patrick Fic
05d9f20a66 Mark Other RO field as Read Only 2021-10-26 15:38:05 -07:00
Patrick Fic
9e3a2e920d Merge branch 'release/2021-10-29' into test 2021-10-26 13:10:37 -07:00
Patrick Fic
1511b87959 IO-1487 MPI Totals Adjustments 2021-10-26 13:10:07 -07:00
Patrick Fic
9c3e4b7b83 IO-1489 Resolve payables memo for QBO. 2021-10-25 17:25:29 -07:00
Patrick Fic
b718f49071 IO-256 Resolve QBO Payable Vendor Insert. 2021-10-25 17:05:21 -07:00
Patrick Fic
e7157119ae Merged in release/2021-10-29 (pull request #256)
Release/2021 10 29
2021-10-25 21:58:53 +00:00
Patrick Fic
09187bdb7f Remove manual scheduling for CI. 2021-10-25 14:57:27 -07:00
Patrick Fic
2aed392fbe IO-658 IO-652 IO-657 add MPI Templates. 2021-10-25 14:46:24 -07:00
Patrick Fic
1a0054a911 IO-1438 Resolve inbound conversation creation. 2021-10-25 11:00:31 -07:00
Patrick Fic
6e6f3d3d3e Resolve user email naming if null. 2021-10-25 10:46:30 -07:00
Patrick Fic
29df140680 IO-1402 Respect blocked day today for scheduling. 2021-10-25 10:41:22 -07:00
Patrick Fic
f37c67a122 Merge branch 'master' into release/2021-10-29 2021-10-25 10:31:49 -07:00
Patrick Fic
e53c9aab72 IO-1379 CSI invite Key update. 2021-10-25 10:24:18 -07:00
Patrick Fic
385ad06adc IO-1375 Special characters in CDK 2021-10-25 10:12:43 -07:00
Patrick Fic
d1e2d943a9 Package updates. 2021-10-25 09:41:54 -07:00
Patrick Fic
90aa3557b7 Merged in release/2021-10-22 (pull request #255)
Change Nod Version.

Approved-by: Patrick Fic
2021-10-22 21:38:53 +00:00
Patrick Fic
8891167183 Merged in release/2021-10-22 (pull request #254)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:38:35 +00:00
Patrick Fic
6631e645df Change Nod Version. 2021-10-22 14:38:05 -07:00
Patrick Fic
b7f202969b Merged in release/2021-10-22 (pull request #253)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:29:49 +00:00
Patrick Fic
1dc3353ecc Merge branch 'release/2021-10-22' into test 2021-10-22 13:01:41 -07:00
Patrick Fic
b1a3f1a7b8 Finish revert stripe removal. 2021-10-22 13:01:26 -07:00
Patrick Fic
89f3a26635 Revert "Disable stripe promise."
This reverts commit 3ece5e0ba2.
2021-10-22 12:59:45 -07:00
Patrick Fic
506fe9b1af Merge branch 'release/2021-10-22' into test 2021-10-22 10:47:12 -07:00
Patrick Fic
37c898d3ce IO-256 Resolve QBO issues. 2021-10-22 10:46:03 -07:00
Patrick Fic
e4eac5714a Merge branch 'release/2021-10-22' into test 2021-10-22 10:00:42 -07:00
Patrick Fic
9d4a59ca16 IO-256 Remove credentials with Axios calls for QBO. 2021-10-22 10:00:23 -07:00
Patrick Fic
8ec524061a Merge branch 'release/2021-10-22' into test 2021-10-21 14:32:06 -07:00
Patrick Fic
434ed46b5a IO-1469 Add keep schedule details to job detail reschedule. 2021-10-21 14:30:50 -07:00
Patrick Fic
3ece5e0ba2 Disable stripe promise. 2021-10-21 14:16:18 -07:00
Patrick Fic
52a383ffb7 IO-1454 Disable production colors & add feature flags. 2021-10-21 14:03:48 -07:00
Patrick Fic
8ad1d5929a Reduce Sentry Logging. 2021-10-21 12:02:02 -07:00
Patrick Fic
445c01499b Merge branch 'release/2021-10-22' into test 2021-10-21 11:42:33 -07:00
Patrick Fic
0b05be841d Merge branch 'release/2021-10-22' of bitbucket.org:snaptsoft/bodyshop into release/2021-10-22 2021-10-21 11:42:10 -07:00
Patrick Fic
6bcb5f2af5 IO-1446 Smart Schedule Blocked Day Fixes 2021-10-21 11:42:05 -07:00
Patrick Fic
7482751c5b IO-1478 Make CC Contract RO lines editable. 2021-10-21 09:43:14 -07:00
Patrick Fic
7a025fff42 Merge branch 'release/2021-10-22' into test 2021-10-21 08:59:49 -07:00
Patrick Fic
602fe36638 QBO Cleanup. 2021-10-21 08:58:24 -07:00
Patrick Fic
da08fc74f1 IO-1475 Totals update for Mitchell Cloud discounts. 2021-10-21 08:53:15 -07:00
Patrick Fic
14af45baf0 IO-256 QBO SS Realm ID 2021-10-20 14:33:31 -07:00
Patrick Fic
420a88c505 Merged in release/2021-10-22 (pull request #252)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:12:44 +00:00
Patrick Fic
f3c44f8dd1 Merge branch 'feature/pbs' into release/2021-10-22 2021-10-18 22:12:16 -07:00
Patrick Fic
c72111e18b Merged in release/2021-10-22 (pull request #251)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:04:35 +00:00
Patrick Fic
1ca2870912 IO-1437 Add deductible Note 2021-10-18 22:02:45 -07:00
Patrick Fic
db9744e1e5 IO-1454 Production List Visual Indicators. 2021-10-18 21:54:24 -07:00
Patrick Fic
e700095551 IO-1492 Add estimator Presets 2021-10-18 20:26:57 -07:00
Patrick Fic
99196a77ed IO-1433 Add Loss of Use Field 2021-10-18 19:56:12 -07:00
Patrick Fic
289a8222a0 IO-223 ARM development 2021-10-18 19:37:27 -07:00
Patrick Fic
dc10f8d35b IO-1469 Retain event detals when rescheduling. 2021-10-18 13:56:48 -07:00
Patrick Fic
f448232fe7 IO-1435 Missed additional query. 2021-10-18 13:52:30 -07:00
Patrick Fic
4baf4b4afa IO-1435 Add scheduled time to job header if scheduled. 2021-10-18 13:48:23 -07:00
Patrick Fic
1785093023 IO-1449 Adjust display of time in schedulign setup. 2021-10-18 13:43:15 -07:00
Patrick Fic
0d65f8d894 IO-1407 Resolve bill not clearing on enter again 2021-10-18 13:40:31 -07:00
Patrick Fic
3d8c390291 IO-1408 Resolve bill form index issue. 2021-10-18 13:29:35 -07:00
Patrick Fic
f0a13856bc IO-1439 Trim QB name fields on export. 2021-10-18 12:16:12 -07:00
Patrick Fic
ad6394783d IO-1443 Expand Priority selection to 15. 2021-10-18 11:22:01 -07:00
Patrick Fic
16e9843298 Package Updates. 2021-10-18 11:21:51 -07:00
Patrick Fic
a4c949c376 Merged in release/2021-10-22 (pull request #250)
Release/2021 10 22
2021-10-18 17:01:18 +00:00
Patrick Fic
0e0d5316b7 Undo Quickbooks CORS to resolve prod error. 2021-10-18 07:55:36 -07:00
Patrick Fic
60cb6ee8fb IO-223 Begin ARMS Integration 2021-10-15 17:55:55 -07:00
Patrick Fic
7d3279d21a Merged in release/2021-10-15 (pull request #249)
Release/2021 10 15
2021-10-15 21:33:24 +00:00
Patrick Fic
402d13ad99 Merge branch 'test' into release/2021-10-15 2021-10-15 10:53:50 -07:00
Patrick Fic
e803f5a2d4 IO-1471 Total Hrs in produciton. 2021-10-15 10:53:24 -07:00
Patrick Fic
3989d0f1e2 Merge branch 'test' into feature/pbs 2021-10-14 17:20:15 -07:00
Patrick Fic
161d476ab3 IO-256 Export payments with no matching invoice & export cust data. 2021-10-14 15:07:31 -07:00
Patrick Fic
ce84a89cf3 Merge branch 'release/2021-10-15' into test 2021-10-14 11:22:14 -07:00
Patrick Fic
1d210a9e52 IO-1468 QB and CDK Updates for MCE Markup 2021-10-14 11:21:06 -07:00
Patrick Fic
660f463aea IO-1468 Resolve line markup for MCE. 2021-10-14 10:52:08 -07:00
Patrick Fic
ad9f01111c Merge branch 'release/2021-10-15' into test 2021-10-13 21:22:55 -07:00
Patrick Fic
1b885e4114 IO-256 Add description to receivables export. 2021-10-13 21:22:38 -07:00
Patrick Fic
b56742bcb2 IO-256 QBO Improvements. 2021-10-13 21:12:02 -07:00
Patrick Fic
40d4d69a9a Merge branch 'release/2021-10-15' into test 2021-10-13 17:03:00 -07:00
Patrick Fic
b54d5beb76 IO-256 Resolve Bill Center reference 2021-10-13 17:02:39 -07:00
Patrick Fic
5cbcd440f5 Merge branch 'feature/qbo' into test 2021-10-13 15:49:02 -07:00
Patrick Fic
1cdc34249a IO-256 Fix null handling for missing metadata. 2021-10-13 15:48:43 -07:00
Patrick Fic
8bbb218777 Merge branch 'feature/qbo' into test
# Conflicts:
#	server/accounting/qbo/qbo-payables.js
#	server/accounting/qbo/qbo-payments.js
#	server/accounting/qbo/qbo-receivables.js
2021-10-13 15:25:02 -07:00
Patrick Fic
755ac7f657 IO-256 Improved Logging 2021-10-13 15:24:14 -07:00
Patrick Fic
d7b884ff86 Revert "IO-256 Genericize email."
This reverts commit fda3620ed0.
2021-10-13 15:14:26 -07:00
Patrick Fic
fda3620ed0 IO-256 Genericize email. 2021-10-13 15:10:36 -07:00
Patrick Fic
61ad9f0d58 IO-256 Add CORS back for QBO. 2021-10-13 15:01:59 -07:00
Patrick Fic
804c8ad40a IO-256 Add cookie location 2021-10-13 14:49:47 -07:00
Patrick Fic
0ddf009f8f Reverse CORS & Fix Cookie Setting. 2021-10-13 14:35:39 -07:00
Patrick Fic
14309b5c96 QBO CORS Updates 2021-10-13 14:06:25 -07:00
Patrick Fic
57d9de469a Add CORS to Server. 2021-10-13 14:01:59 -07:00
Patrick Fic
404ade396c QB URL Updates. 2021-10-13 13:39:04 -07:00
Patrick Fic
bdad6da6d9 QB URL Fix. 2021-10-13 13:35:42 -07:00
Patrick Fic
e7ef3b94c1 QB Env update. 2021-10-13 13:34:56 -07:00
Patrick Fic
c19c92ab7e QBO port fix. 2021-10-13 13:25:15 -07:00
Patrick Fic
944229bae3 Update process env. 2021-10-13 13:22:56 -07:00
Patrick Fic
661b05d9e3 Merge branch 'feature/qbo' into test 2021-10-13 13:15:48 -07:00
Patrick Fic
4d1d471a66 Merge branch 'release/2021-10-15' into test 2021-10-13 13:15:35 -07:00
Patrick Fic
3e84fbbaf4 IO-1458 IO-1465 Sorting Fixes. 2021-10-13 11:52:11 -07:00
Patrick Fic
c4e59c1a5e IO-117 Begin PBS Structure. 2021-10-13 11:45:24 -07:00
Patrick Fic
9ee8e9007a IO-256 Add Payables Posting. 2021-10-12 20:18:06 -07:00
Patrick Fic
4d52a5c44a IO-256 Exporting of payments 2021-10-12 19:07:43 -07:00
Patrick Fic
fff9073f9d IO-256 Add vendor credits. 2021-10-12 16:54:28 -07:00
Patrick Fic
71f0b8a005 Merge branch 'release/2021-10-15' into test 2021-10-12 16:41:01 -07:00
Patrick Fic
d2b965f79e Resolve CI Issue. 2021-10-12 16:40:27 -07:00
Patrick Fic
a02aa71a95 Merged in release/2021-10-15 (pull request #248)
release/2021-10-15

Approved-by: Patrick Fic
2021-10-12 23:33:17 +00:00
Patrick Fic
7562bf5c95 IO-1418 Allow owner re-search on import. 2021-10-12 16:17:31 -07:00
Patrick Fic
f9521483e2 IO-1448 Disable negative deductible amounts. 2021-10-12 14:39:20 -07:00
Patrick Fic
ca85858885 IO-1453 Allow null scheduled completion dates on cehcklist view. 2021-10-12 14:27:22 -07:00
Patrick Fic
c7b3a94533 IO-1441 Conversion safety check. 2021-10-12 14:18:09 -07:00
Patrick Fic
b010c9ecb0 IO-1458 Production Board Sort ORder 2021-10-12 13:50:25 -07:00
Patrick Fic
0b98d04bac IO-1458 Production Board Sort Order 2021-10-12 13:49:32 -07:00
Patrick Fic
e25e388e59 Merged in release/2021-10-15 (pull request #247)
IO-233 Replace unknown CDK function.

Approved-by: Patrick Fic
2021-10-12 19:06:35 +00:00
Patrick Fic
10654b7916 IO-233 Replace unknown CDK function. 2021-10-12 12:04:50 -07:00
Patrick Fic
ff049ad3e8 Merged in release/2021-10-08 (pull request #246)
release/2021-10-08
2021-10-09 15:29:49 +00:00
Patrick Fic
3572fff2c1 Merged in release/2021-10-08 (pull request #245)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-07 23:38:03 +00:00
Patrick Fic
774e4cdf94 IO-1430 Add note on last contacted update. 2021-10-07 16:26:48 -07:00
Patrick Fic
65ade5cab8 IO-1428 add ph2 to global search. 2021-10-07 16:00:35 -07:00
Patrick Fic
bf21a073fb IO-1415 Update cache on line delete. 2021-10-07 15:48:29 -07:00
Patrick Fic
851f1c265f Merged in release/2021-10-08 (pull request #244)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-07 16:51:36 +00:00
Patrick Fic
51f3b5927b IO-1428 IO-1338 Add ownr_ph2. 2021-10-07 09:47:10 -07:00
Patrick Fic
42c779f368 IO-1415 Default tax part to true. 2021-10-07 09:19:37 -07:00
Patrick Fic
c104ee4fd9 Merged in release/2021-10-08 (pull request #243)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-06 20:37:34 +00:00
Patrick Fic
0b2efa31b5 IO-1375 IO-1383 CDK Tax Addition 2021-10-06 13:37:14 -07:00
Patrick Fic
17baa8fcb2 IO-1404 Rates in shop info minimums 2021-10-05 16:36:53 -07:00
Patrick Fic
e550baf59d Merged in release/2021-10-08 (pull request #242)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-05 23:20:08 +00:00
Patrick Fic
fd53eb92e6 Merge branch 'test' into release/2021-10-08 2021-10-05 16:19:41 -07:00
Patrick Fic
6521c0bfb8 IO-1413 Change color of kanban background 2021-10-05 14:22:45 -07:00
Patrick Fic
50489dd682 IO-1415 Add tax part & refresh line ticket on line edit 2021-10-05 14:15:21 -07:00
Patrick Fic
220afa5add IO-1405 resolve shift label issues. 2021-10-05 14:05:16 -07:00
Patrick Fic
9d549b02fe IO-1410 Remove ded from lbr lines on reconciliation. 2021-10-05 13:59:43 -07:00
Patrick Fic
ca2ded047b Merged in release/2021-10-08 (pull request #241)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-05 18:48:13 +00:00
Patrick Fic
c423e61ce8 IO-1422 Resolve send email with attachments. 2021-10-05 11:47:37 -07:00
Patrick Fic
65550c7bf4 IO-1436 App tax part fix to all regions. 2021-10-05 11:36:21 -07:00
Patrick Fic
cdb4da9e5f IO-1412 Resolve production list saving and loading. 2021-10-05 09:40:25 -07:00
Patrick Fic
b59a5303c6 IO-1430 add Last Contacted date to job. 2021-10-05 09:12:44 -07:00
Patrick Fic
9a72abbed0 IO-1426 Add time to date detail card. 2021-10-05 08:53:20 -07:00
Patrick Fic
762a5ff01b IO-1432 Add offset for speed print. 2021-10-05 08:49:38 -07:00
Patrick Fic
2f6571e703 Package updates. 2021-10-05 07:59:51 -07:00
Patrick Fic
80c90f1819 IO-1399 Parts Order Modal Updates 2021-10-04 16:13:33 -07:00
Patrick Fic
4b0c2c60a2 IO-1398 Parts Order Mark Duplication 2021-10-04 16:00:05 -07:00
Patrick Fic
ee7d7d2f6a IO-1397 Preview cards consistency updates. 2021-10-04 15:55:43 -07:00
Patrick Fic
0ad670013b IO-1395 Add missing info to job preview. 2021-10-04 15:48:28 -07:00
Patrick Fic
064a66aa66 IO-1329 Filtering on bill line posting 2021-10-04 14:37:37 -07:00
Patrick Fic
f46c19b152 Merged in release/2021-10-08 (pull request #239)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-04 20:35:21 +00:00
Patrick Fic
ea2c583b26 IO-1419 Resolve hsaura migration issues with new field. 2021-10-04 13:20:35 -07:00
Patrick Fic
7bd2a90141 Merged in release/2021-10-08 (pull request #238)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-04 19:11:34 +00:00
Patrick Fic
632549dd9d IO-1406 Parts Order label update 2021-10-04 12:08:47 -07:00
Patrick Fic
70ef274821 IO-1404 IO-1403 Remove negative value options for 2021-10-04 12:08:29 -07:00
Patrick Fic
7d9759fda4 IO-1419 Added comments to parts orders. 2021-10-04 11:59:33 -07:00
Patrick Fic
0fd06d5e4e IO-1421 Add day of week to smart scheduling buttons. 2021-10-04 11:48:55 -07:00
Patrick Fic
4995e44e06 IO-1424 Add other referral source 2021-10-04 11:43:33 -07:00
Patrick Fic
fd43d7d56d Merged in release/2021-10-08 (pull request #237)
JSR CHange

Approved-by: Patrick Fic
2021-10-04 14:57:42 +00:00
Patrick Fic
96f292f61c JSR CHange 2021-10-04 07:57:14 -07:00
Patrick Fic
e899e4545f Merged in release/2021-10-08 (pull request #236)
Release/2021 10 08
2021-10-02 06:19:45 +00:00
Patrick Fic
c62f5e3911 Merged in release/2021-10-08 (pull request #235)
Resolve CI.

Approved-by: Patrick Fic
2021-10-01 22:56:48 +00:00
Patrick Fic
9f1d184081 Resolve CI. 2021-10-01 15:56:24 -07:00
Patrick Fic
29e596eedb Merged in release/2021-10-08 (pull request #234)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-01 22:41:36 +00:00
Patrick Fic
6e28afda67 IO-1417 Resolve custom templates printing. 2021-10-01 15:41:17 -07:00
Patrick Fic
eca7e9fba1 Merged in release/2021-10-08 (pull request #233)
IO-1392 Add IOU Form to Print Center

Approved-by: Patrick Fic
2021-10-01 16:25:04 +00:00
Patrick Fic
aa2ac2b296 IO-1392 Add IOU Form to Print Center 2021-10-01 09:24:37 -07:00
Patrick Fic
ce57752b95 Merged in release/2021-10-08 (pull request #232)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-01 00:49:16 +00:00
Patrick Fic
63163c6459 IO1387 Add tax part override for non-R&R lines & Package Updates 2021-09-30 17:48:31 -07:00
Patrick Fic
2712ee5c0b IO-1416 Add missing LAA to resp center setup. 2021-09-30 09:44:15 -07:00
Patrick Fic
2e28a4a790 Merged in release/2021-10-01 (pull request #231)
Release/2021 10 01
2021-09-30 01:50:34 +00:00
Patrick Fic
4179943df6 Merged in release/2021-10-01 (pull request #230)
Release/2021 10 01
2021-09-29 22:55:42 +00:00
Patrick Fic
8bb8eee384 Remove open replay tracker. 2021-09-29 14:38:40 -07:00
Patrick Fic
85d79a8d7f Remove extra console logs. 2021-09-29 12:03:21 -07:00
Patrick Fic
d38dd67c2f Merged in release/2021-10-01 (pull request #229)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-29 15:50:34 +00:00
Patrick Fic
e73d082eab IO-1349 Resolve manual job creation. 2021-09-29 08:49:55 -07:00
Patrick Fic
2637538d9a IO-256 QBO Payables 2021-09-29 08:43:15 -07:00
Patrick Fic
0eacf9a840 Merged in release/2021-10-01 (pull request #228)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-28 20:19:33 +00:00
Patrick Fic
eb58274f90 IO-1391 Scoreboard Improvements 2021-09-28 13:19:02 -07:00
Patrick Fic
aa410d6847 IO-1283 IO-1371 Updates to add to scoreboard button. 2021-09-28 11:45:12 -07:00
Patrick Fic
97f1be9d6f IO-1390 Resolve breaking import changes from Hasura. 2021-09-28 10:59:18 -07:00
Patrick Fic
a7c9dde5e3 Merged in release/2021-10-01 (pull request #227)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-27 23:52:21 +00:00
Patrick Fic
8ec5bc049b Resolve Qb Export not separating for QBO. 2021-09-27 16:50:37 -07:00
Patrick Fic
1f2bec06ef IO-1381 Updates to mark up calculations process. 2021-09-27 16:34:11 -07:00
Patrick Fic
1cad57bec2 Merge branch 'release/2021-10-01' of https://bitbucket.org/snaptsoft/bodyshop into release/2021-10-01 2021-09-27 15:44:21 -07:00
Patrick Fic
10f3c01677 IO-1375 SGI Add Sublet Labor Lines Calculation 2021-09-27 15:44:17 -07:00
Patrick Fic
fc68b669db Add GST to PVRT on QBO. 2021-09-27 14:49:33 -07:00
Patrick Fic
6e22091b81 IO-1382 Add ownr_co_nm to queries that were missing it. 2021-09-27 11:38:48 -07:00
Patrick Fic
545db54c14 IO-653 IO-654 Add SGI documents & regions filtering on print center. 2021-09-27 10:36:12 -07:00
Patrick Fic
aa69fef9ba IO-1379 Updated CSI key for actions menu 2021-09-27 10:22:47 -07:00
Patrick Fic
1e30642d28 Package updates for Tracker. 2021-09-27 09:49:08 -07:00
Patrick Fic
c1dfba949e IO-1349 Update Hasura to 2.0.9. 2021-09-27 09:17:34 -07:00
Patrick Fic
dfa9592755 IO-256 Add PVRT handling for QBO. 2021-09-24 17:17:23 -07:00
Patrick Fic
8f2b1f0f78 IO-256 Export receivables for QBO 2021-09-24 16:43:57 -07:00
Patrick Fic
cf91ec14c0 Merge branch 'feature/qbo' into test 2021-09-24 14:33:07 -07:00
Patrick Fic
891aa649e8 Merged in release/2021-09-24 (pull request #226)
release/2021-09-24
2021-09-24 17:44:53 +00:00
Patrick Fic
dfe0f99bea Merged in release/2021-09-24 (pull request #225)
Revert Node Version

Approved-by: Patrick Fic
2021-09-24 17:44:20 +00:00
Patrick Fic
591022c097 Revert Node Version 2021-09-24 10:43:28 -07:00
Patrick Fic
a8a0167123 Merged in release/2021-09-24 (pull request #224)
Release/2021 09 24
2021-09-24 17:20:52 +00:00
Patrick Fic
c76da54b93 IO-256 First succesful basic post of receivables. 2021-09-23 16:53:41 -07:00
Patrick Fic
186f6101ff IO-256 Begin QBO Changes for Individual code mapping on receivables. 2021-09-23 16:22:59 -07:00
Patrick Fic
e95cf0a026 Merge branch 'test' into feature/qbo 2021-09-23 11:48:56 -07:00
Patrick Fic
3eaa3a0189 Merged in release/2021-09-24 (pull request #223)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-23 17:42:34 +00:00
Patrick Fic
0ba445aad2 IO-1228 Report Center grouping change. 2021-09-23 10:38:22 -07:00
Patrick Fic
637c718b05 IO-1227 Add sold status to filter for CC 2021-09-23 10:36:27 -07:00
Patrick Fic
a01019b1b3 IO-1366 Additional Audit Trail Functionality 2021-09-23 10:29:35 -07:00
Patrick Fic
f6e2393a40 Merged in release/2021-09-24 (pull request #222)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-22 23:11:31 +00:00
Patrick Fic
715857a587 IO-1370 Remove filtering on Other Part Type 2021-09-22 08:20:25 -07:00
Patrick Fic
186ad2d7a2 IO-1365 Update Messaging Issues. 2021-09-21 17:33:39 -07:00
Patrick Fic
c64c49ab6e IO-1103 Include removed lines for bill line select. 2021-09-21 15:56:07 -07:00
Patrick Fic
3e067e2103 Merged in release/2021-09-24 (pull request #221)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-21 16:13:56 +00:00
Patrick Fic
4cc1f77b14 IO-1373 Resolve rounding issue on time tickets. 2021-09-21 08:29:16 -07:00
Patrick Fic
334dd5fe94 Merged in release/2021-09-24 (pull request #220)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-21 00:22:47 +00:00
Patrick Fic
d73f37cacc IO-952 add confirm to bill enter with discrepancy. 2021-09-20 17:22:13 -07:00
Patrick Fic
c6a7e3dcb1 IO-1299 Resolve tax ratds not saving on bill enter save and new 2021-09-20 17:11:45 -07:00
Patrick Fic
4bdc02d22c IO-1358 IO-1372 Add Timestamp to report center. 2021-09-20 16:53:51 -07:00
Patrick Fic
afa8e04008 IO-1308 Remove pagination on job lines. 2021-09-20 16:41:59 -07:00
Patrick Fic
990c1bb2bf IO-1272 Improve reconciliation table UI 2021-09-20 16:39:17 -07:00
Patrick Fic
a5a59c526c IO-1369 Update jobs query to include name. 2021-09-20 16:18:39 -07:00
Patrick Fic
2d5bef4b7b IO-1370 Add glass to parts filter list. 2021-09-20 16:12:12 -07:00
Patrick Fic
5893b1e3b3 IO-1335 Add Subject with RO to Parts Order Email 2021-09-20 14:17:18 -07:00
Patrick Fic
0af452b8a4 IO-1292 Vendor Discount Step. 2021-09-20 14:09:27 -07:00
Patrick Fic
3d82198c90 IO-1227 Sold Courtesy Car Status. 2021-09-20 14:09:09 -07:00
Patrick Fic
01bca360c7 IO-1228 IO-1282 RC Categories & Searching 2021-09-20 12:48:37 -07:00
Patrick Fic
59e994ac29 IO-1367 Open Replay Tracking Improvements 2021-09-20 12:16:37 -07:00
Patrick Fic
d5f3105341 IO-1254 Add PO Number 2021-09-20 12:02:37 -07:00
Patrick Fic
92920f69d4 IO-1191 Add CSI to reports. 2021-09-20 11:34:05 -07:00
Patrick Fic
61ac520192 IO-1364 Update profile changes for new firebase package. 2021-09-20 11:31:13 -07:00
Patrick Fic
45176cc2e2 Merged in feature/cdk-cert (pull request #219)
IO-233 Add refetch.
2021-09-17 17:22:56 +00:00
Patrick Fic
6aacce5a58 IO-233 Add refetch. 2021-09-17 10:22:20 -07:00
Patrick Fic
7a515c35d2 Merge branch 'test' into feature/qbo 2021-09-16 17:04:57 -07:00
Patrick Fic
80dc04669c Merged in feature/cdk-cert (pull request #218)
feature/cdk-cert

Approved-by: Patrick Fic
2021-09-16 23:11:17 +00:00
Patrick Fic
a702c87986 io-233 Remote ITC Settings 2021-09-16 16:08:32 -07:00
Patrick Fic
1942103985 IO-256 Start add QBO Payables 2021-09-15 16:14:01 -07:00
Patrick Fic
fd161fa9eb IO-233 Add missing MAPA/MASH/Discount calulcations. 2021-09-15 08:28:54 -07:00
Patrick Fic
44a18f4ace Merged in feature/qbo (pull request #217)
feature/qbo

Approved-by: Patrick Fic
2021-09-14 21:10:15 +00:00
Patrick Fic
3269c4f602 Merged in feature/cdk-cert (pull request #216)
feature/cdk-cert

Approved-by: Patrick Fic
2021-09-14 21:09:10 +00:00
Patrick Fic
43cbf0084a Merged in release/2021-09-17 (pull request #215)
release/2021-09-17

Approved-by: Patrick Fic
2021-09-14 21:03:40 +00:00
Patrick Fic
28b1356f76 Merge branch 'release/2021-09-17' into feature/cdk-cert 2021-09-14 14:03:11 -07:00
Patrick Fic
38456cb213 Merge branch 'release/2021-09-17' into feature/qbo 2021-09-14 14:02:18 -07:00
Patrick Fic
8c826eaaed Update to latest node. 2021-09-14 14:01:55 -07:00
Patrick Fic
f047556ab5 Merge branch 'release/2021-09-17' into feature/qbo 2021-09-14 13:59:06 -07:00
Patrick Fic
057b335e82 Merge branch 'release/2021-09-17' into feature/cdk-cert 2021-09-14 13:58:48 -07:00
Patrick Fic
4b55719f86 Merged in hotfix-2021-09-14 (pull request #214)
hotfix-2021-09-14

Approved-by: Patrick Fic
2021-09-14 16:52:50 +00:00
Patrick Fic
30c7da2bf9 Merged in hotfix-2021-09-14 (pull request #213)
IO-43 Resolve issue when no vehicle associated.

Approved-by: Patrick Fic
2021-09-14 16:41:07 +00:00
Patrick Fic
82fb4fc74c IO-43 Resolve issue when no vehicle associated. 2021-09-14 09:40:08 -07:00
Patrick Fic
151f122b8c Package updates. 2021-09-14 09:12:58 -07:00
Patrick Fic
b198ca5051 IO-233 Revert ITC Calculations. 2021-09-13 14:56:54 -07:00
Patrick Fic
d9abe84949 Merge release & add time ticket calculations. 2021-09-13 14:47:09 -07:00
Patrick Fic
5387ff207c Merged in release/2021-09-10 (pull request #212)
Release/2021 09 10
2021-09-13 19:02:26 +00:00
Patrick Fic
17f4d69e30 Merged in release/2021-09-10 (pull request #211)
Resolve translation.

Approved-by: Patrick Fic
2021-09-13 17:09:49 +00:00
Patrick Fic
77d3fc359d Resolve translation. 2021-09-13 10:09:26 -07:00
Patrick Fic
86151f3337 Merge branch 'release/2021-09-10' into feature/cdk-cert 2021-09-13 10:08:15 -07:00
Patrick Fic
ae4b5bca33 Merged in release/2021-09-10 (pull request #210)
Add missing translations.

Approved-by: Patrick Fic
2021-09-13 16:50:13 +00:00
Patrick Fic
eb120a264e Add missing translations. 2021-09-13 09:49:38 -07:00
Patrick Fic
7e3f496ea1 IO-233 CDK WIP customer changes. 2021-09-13 09:43:41 -07:00
Patrick Fic
4d33f16f13 Merged in release/2021-09-10 (pull request #209)
release/2021-09-10

Approved-by: Patrick Fic
2021-09-13 15:09:19 +00:00
Patrick Fic
d7ebefe7ab Resolve missing query. 2021-09-13 08:08:43 -07:00
Patrick Fic
8dc2197677 IO-1353 Add missing default resp. centers. 2021-09-10 15:12:00 -07:00
Patrick Fic
e3804b103b Merged in release/2021-09-10 (pull request #208)
Release/2021 09 10
2021-09-10 22:08:15 +00:00
Patrick Fic
fe993cba73 IO-233 CDK Reformatting. First successful post. 2021-09-10 14:58:09 -07:00
Patrick Fic
ef6cdf07d8 IO-1352 Remove positive hrs check on receivables export. 2021-09-10 13:09:21 -07:00
Patrick Fic
cdbde6f5fa Merged in release/2021-09-10 (pull request #207)
IO-43 Updated related ROs approach.

Approved-by: Patrick Fic
2021-09-09 22:35:46 +00:00
Patrick Fic
4059aa9875 IO-43 Updated related ROs approach. 2021-09-09 15:35:24 -07:00
Patrick Fic
22e30ae5cb IO-233 WIP CDK 2021-09-09 15:18:43 -07:00
Patrick Fic
78c883743f Merged in release/2021-09-10 (pull request #206)
IO-1350 Audatex import issues.
2021-09-09 20:10:20 +00:00
Patrick Fic
e46307e715 IO-1350 Audatex import issues. 2021-09-09 13:09:05 -07:00
Patrick Fic
f3e078b481 IO-233 Additional CDK Posting 2021-09-08 15:11:24 -07:00
Patrick Fic
c03f54b3eb Merge branch 'release/2021-09-10' into feature/cdk-cert 2021-09-08 08:28:54 -07:00
Patrick Fic
bb0234fb7d Merge branch 'test' into feature/cdk-cert 2021-09-08 08:28:27 -07:00
Patrick Fic
03f25c8c0e Merged in release/2021-09-10 (pull request #205)
release/2021-09-10

Approved-by: Patrick Fic
2021-09-07 23:53:40 +00:00
Patrick Fic
8e5005daa0 IO-1345 IO-43 Related Job linking 2021-09-07 16:27:34 -07:00
Patrick Fic
259458eec3 IO-1343 Updated tax rate check on import. 2021-09-07 09:53:55 -07:00
Patrick Fic
59e57aa274 Package updates. 2021-09-07 09:12:35 -07:00
Patrick Fic
de33bcd72b IO-1342 Revert IO-554 and add validation on jobline upsert. 2021-09-07 09:12:31 -07:00
Patrick Fic
d72472ccc3 Merged in hotifx/2021-09-06 (pull request #203)
hotifx/2021-09-06

Approved-by: Patrick Fic
2021-09-06 22:50:14 +00:00
Patrick Fic
91efd170c8 Merged in hotifx/2021-09-06 (pull request #202)
IO-1346 Resolve delivery checklist freezing.

Approved-by: Patrick Fic
2021-09-06 22:43:24 +00:00
Patrick Fic
6f1ddd51fd IO-1346 Resolve delivery checklist freezing. 2021-09-06 15:42:25 -07:00
Patrick Fic
9f48a91a29 Merged in release/2021-09-03 (pull request #201)
Remove assist tracker again.

Approved-by: Patrick Fic
2021-09-03 20:01:07 +00:00
Patrick Fic
a7e2548e14 Merged in release/2021-09-03 (pull request #200)
Remove assist tracker again.

Approved-by: Patrick Fic
2021-09-03 20:00:43 +00:00
Patrick Fic
3294faaeaa Remove assist tracker again. 2021-09-03 13:00:02 -07:00
Patrick Fic
06480159e3 Merge branch 'release/2021-09-03' into feature/qbo 2021-09-03 11:24:42 -07:00
Patrick Fic
6ce06ed5c0 Merge branch 'release/2021-09-03' into feature/cdk-cert 2021-09-03 11:24:16 -07:00
Patrick Fic
71e535388a Merged in release/2021-09-03 (pull request #199)
Release/2021 09 03
2021-09-03 18:19:02 +00:00
Patrick Fic
dbd265d368 Merged in release/2021-09-03 (pull request #198)
IO-1342 Resolve negative parts discount for negatives.

Approved-by: Patrick Fic
2021-09-02 23:31:38 +00:00
Patrick Fic
c11f182f83 IO-1342 Resolve negative parts discount for negatives. 2021-09-02 16:25:07 -07:00
Patrick Fic
5f70cfd585 Merged in release/2021-09-03 (pull request #197)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-02 22:50:50 +00:00
Patrick Fic
6e0675f28b IO-1342 Resolve negative parts discount crash on job totals. 2021-09-02 15:48:54 -07:00
Patrick Fic
96f45d2c80 IO-233 Begin Trans Headers 2021-09-02 15:30:03 -07:00
Patrick Fic
091e44f471 Merge branch 'release/2021-09-03' into feature/cdk-cert 2021-09-02 15:27:22 -07:00
Patrick Fic
00549d6a88 Revert "Remove assist tracker."
This reverts commit d940e0ee78.
2021-09-01 18:10:44 -07:00
Patrick Fic
d940e0ee78 Remove assist tracker. 2021-09-01 15:06:52 -07:00
Patrick Fic
39a38d46ee Merged in release/2021-09-03 (pull request #196)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-01 21:14:34 +00:00
Patrick Fic
71435ed75a Added patch for peerjs. 2021-09-01 14:14:20 -07:00
Patrick Fic
e7ec408b98 Merged in release/2021-09-03 (pull request #195)
Openrelay tracking change.

Approved-by: Patrick Fic
2021-09-01 20:39:45 +00:00
Patrick Fic
9306064420 Openrelay tracking change. 2021-09-01 13:38:35 -07:00
Patrick Fic
37a16edfb4 IO-233 Updated translations. 2021-09-01 13:24:19 -07:00
Patrick Fic
9ad5b9547f Merged in release/2021-09-03 (pull request #194)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-01 16:19:58 +00:00
Patrick Fic
47edb0bdf4 IO-1332 Resolve checklist prod note. 2021-09-01 09:19:11 -07:00
Patrick Fic
5db47e879c IO-1224 Adjust scoreboard Entry 2021-09-01 08:43:37 -07:00
Patrick Fic
54b46dd25e IO-256 WIP QBO. 2021-09-01 08:31:24 -07:00
Patrick Fic
4cb92c8508 Merged in release/2021-09-03 (pull request #193)
IO-1336 Resolve towing/storage missing on export.

Approved-by: Patrick Fic
2021-08-31 00:04:22 +00:00
Patrick Fic
cb76e2dcde IO-1336 Resolve issues with QB Export of towing. 2021-08-30 16:59:15 -07:00
Patrick Fic
901c64ed85 IO-1336 Resolve towing/storage missing on export. 2021-08-30 16:38:45 -07:00
Patrick Fic
f6f90d68fa Merged in release/2021-09-03 (pull request #192)
release/2021-09-03

Approved-by: Patrick Fic
2021-08-30 22:43:40 +00:00
Patrick Fic
05e295fcac IO-1334 Added shop info RBAC. 2021-08-30 15:43:10 -07:00
Patrick Fic
c97df6dc61 IO-1333 Resolve dashboard component name issue. 2021-08-30 15:34:19 -07:00
Patrick Fic
61406aafa6 IO-1302 Remove 2nd message on job close. 2021-08-30 15:28:22 -07:00
Patrick Fic
03210db711 IO-1332 Resolve production note save on intake. 2021-08-30 15:25:04 -07:00
Patrick Fic
b3a34c109a IO-539 Fix hanging confirmation. 2021-08-30 15:20:08 -07:00
Patrick Fic
81daad35d8 Package updates and firebase refactor to SDK 9. 2021-08-30 15:09:11 -07:00
Patrick Fic
529eb24d76 Merge branch 'feature/qbo' into release/2021-09-03 2021-08-30 12:57:38 -07:00
Patrick Fic
e81bf7b561 Merge in CDK Branches WIP. 2021-08-30 12:57:13 -07:00
Patrick Fic
7a35dc9b38 IO-233 Add Vehicle History 2021-08-30 12:54:43 -07:00
Patrick Fic
c72ef97b82 IO-256 Further work on QBO Receivables. 2021-08-30 10:50:56 -07:00
Patrick Fic
5284ee2ef9 IO-256 Authorization and Basic Calls 2021-08-27 15:42:32 -07:00
Patrick Fic
724c097d52 IO-256 QBO Authorization Flow. 2021-08-26 15:48:10 -07:00
Patrick Fic
3c3da178ba Merged in release/2021-08-27 (pull request #191)
Release/2021 08 27
2021-08-25 21:59:23 +00:00
Patrick Fic
db4e5d48af Merge branch 'feature/cdk-cert' into feature/qbo 2021-08-25 12:08:06 -07:00
Patrick Fic
a7cf081ed5 IO-233 Begin Wip Header Creation 2021-08-24 18:48:02 -07:00
Patrick Fic
db5b11f6d3 IO-233 Vehicle and Customer posting unit testing completed. 2021-08-24 18:14:41 -07:00
Patrick Fic
8d3d52485f Merged in release/2021-08-27 (pull request #190)
IO-1330 Resolve postal code on qb export.

Approved-by: Patrick Fic
2021-08-24 23:40:02 +00:00
Patrick Fic
edb58ebc81 IO-1330 Resolve postal code on qb export. 2021-08-24 16:39:42 -07:00
Patrick Fic
971b518e8f Merged in release/2021-08-27 (pull request #189)
IO-539 Resolve tax import for SGI.

Approved-by: Patrick Fic
2021-08-24 23:35:15 +00:00
Patrick Fic
8d9507dce1 IO-539 Resolve tax import for SGI. 2021-08-24 16:32:05 -07:00
Patrick Fic
748f8f472d IO-233 Insert DMS Vehicle 2021-08-24 15:56:10 -07:00
Patrick Fic
db2b4739c2 IO-233 Add insert Job. 2021-08-24 08:05:24 -07:00
Patrick Fic
6c12e5cb03 IO-233 Export Job Refactor 2021-08-23 19:02:05 -07:00
Patrick Fic
140e57a123 Merged in release/2021-08-27 (pull request #188)
IO-1330 Update QB Information

Approved-by: Patrick Fic
2021-08-23 21:17:06 +00:00
Patrick Fic
3ca6791939 IO-233 Added vehicle search & selection on form. 2021-08-23 10:40:10 -07:00
Patrick Fic
1c473c95a2 IO-1330 Update QB Information 2021-08-23 09:50:01 -07:00
Patrick Fic
7e145bdec7 Merged in release/2021-08-27 (pull request #187)
Remove tracker assist for CI.

Approved-by: Patrick Fic
2021-08-23 15:43:39 +00:00
Patrick Fic
44c17bd7a2 Remove tracker assist for CI. 2021-08-23 08:43:14 -07:00
Patrick Fic
c493f6e31e Merged in release/2021-08-27 (pull request #186)
Release/2021 08 27

Approved-by: Patrick Fic
2021-08-23 15:34:50 +00:00
Patrick Fic
5213b0b315 Merge branch 'test' into release/2021-08-27 2021-08-23 08:34:25 -07:00
Patrick Fic
8bfd0a1c16 IO-233 Insert DMS Vehicles 2021-08-23 08:30:30 -07:00
Patrick Fic
0541167cc5 Addition of Open Replay Tracker 2021-08-20 14:38:33 -07:00
Patrick Fic
834966ae96 Merged in release/2021-08-20 (pull request #185)
Release/2021 08 20
2021-08-20 21:01:17 +00:00
Patrick Fic
e7d813c3f3 Merged in test (pull request #184)
Merge WIP items back into CDK CErt.
2021-08-20 16:37:44 +00:00
Patrick Fic
26e47ff203 Merged in release/2021-08-20 (pull request #183)
IO-1327 Resolve dinero error on bill posting

Approved-by: Patrick Fic
2021-08-20 15:07:38 +00:00
Patrick Fic
d2b2a5399d IO-1327 Resolve dinero error on bill posting 2021-08-20 08:07:01 -07:00
Patrick Fic
5a4d6d3e8c IO-1269 2021-08-19 17:31:32 -07:00
Patrick Fic
61a5e180f4 IO-1323 Add unit number to contracts list. 2021-08-19 14:43:38 -07:00
Patrick Fic
d5b8ea3ac5 IO-1317 2021-08-19 14:24:12 -07:00
Patrick Fic
4e87ef179b Resolve craco configuration. 2021-08-19 13:45:37 -07:00
Patrick Fic
3b7c31626d IO-539 Adjust Threshold Totals 2021-08-19 08:48:30 -07:00
Patrick Fic
a57e35354d Add sentry error logging. 2021-08-18 15:57:29 -07:00
Patrick Fic
a269fd3ad8 IO-1316 Add flat_rate to time tickets & resolve issues in job costing calculations. 2021-08-18 15:39:37 -07:00
Patrick Fic
86bee9ad0d IO-1321 Mark PO Quantity as required. 2021-08-18 15:38:54 -07:00
Patrick Fic
f8b57ca9bd IO-1297 Adjust Job Costing after Invoicing 2021-08-18 14:13:11 -07:00
Patrick Fic
5f7b780195 Merged in release/2021-08-20 (pull request #182)
release/2021-08-20

Approved-by: Patrick Fic
2021-08-18 20:29:06 +00:00
Patrick Fic
ba5400d04a IO-1307 Resolve selection issues on export tables. 2021-08-18 13:18:30 -07:00
Patrick Fic
0190e737c1 IO-1297 resolve decimal issue on time ticket list. 2021-08-18 12:58:26 -07:00
Patrick Fic
db64d9b69f IO-233 WIP CDK 2021-08-17 16:22:10 -07:00
Patrick Fic
0d12ab36a9 Merged in release/2021-08-20 (pull request #181)
release/2021-08-20

Approved-by: Patrick Fic
2021-08-13 23:38:45 +00:00
Patrick Fic
8f52efbaca IO-1289 Resolve scoreboard UI issues. 2021-08-13 16:33:36 -07:00
Patrick Fic
d6dc5db185 IO-1296 Resolve missing translation. 2021-08-13 16:20:45 -07:00
Patrick Fic
58d1859640 IO-1306 Show Export Attempts on export screens. 2021-08-13 16:17:16 -07:00
Patrick Fic
dba648aea2 IO-1303 Adjust jobline status alignment. 2021-08-13 15:52:02 -07:00
Patrick Fic
12f6206f88 IO-1224 IO-1297 IO-1298 Adjust decimal place displays in various locations 2021-08-13 15:48:12 -07:00
Patrick Fic
7d9fd06b6d IO-233 CDK Get Makes & Taxes calculation. 2021-08-13 15:08:44 -07:00
Patrick Fic
d6fbf16272 Merged in feature/2021-08-13 (pull request #180)
Feature/2021 08 13
2021-08-13 19:09:00 +00:00
Patrick Fic
b68de683b0 Merged in hotfix/2021-08-12 (pull request #179)
Merged in hotfix/2021-08-12 (pull request #178)

Approved-by: Patrick Fic
2021-08-12 21:30:32 +00:00
Patrick Fic
2f9d025fbe Merged in hotfix/2021-08-12 (pull request #178)
hotfix/2021-08-12

Approved-by: Patrick Fic
2021-08-12 20:43:16 +00:00
Patrick Fic
9c1ffaba17 Merged in hotfix/2021-08-12 (pull request #177)
hotfix/2021-08-12
2021-08-12 20:42:55 +00:00
Patrick Fic
142df39cd2 Merged in hotfix/2021-08-12 (pull request #176)
Additional Checklist resolutions.

Approved-by: Patrick Fic
2021-08-12 20:21:34 +00:00
Patrick Fic
ef79ccc299 Additional Checklist resolutions. 2021-08-12 13:20:56 -07:00
Patrick Fic
6d2d96be5c Merged in hotfix/2021-08-12 (pull request #175)
Resolve delete checklist item.

Approved-by: Patrick Fic
2021-08-12 20:16:47 +00:00
Patrick Fic
8ff2a6e6c4 Resolve delete checklist item. 2021-08-12 13:16:26 -07:00
Patrick Fic
b793aa3394 Merged in hotfix/2021-08-12 (pull request #174)
hotfix/2021-08-12

Approved-by: Patrick Fic
2021-08-12 20:02:48 +00:00
Patrick Fic
be2cfb908a IO-1312 Resolve delivery checklist issue. 2021-08-12 13:01:20 -07:00
Patrick Fic
e34084b146 Merged in feature/2021-08-13 (pull request #173)
feature/2021-08-13

Approved-by: Patrick Fic
2021-08-12 16:53:13 +00:00
Patrick Fic
c1d7168260 Resolve CI issue. 2021-08-12 09:52:44 -07:00
Patrick Fic
b3a3709b72 Merged in feature/2021-08-13 (pull request #172)
IO-1286 Retry Supplement Importing fix.

Approved-by: Patrick Fic
2021-08-12 16:47:27 +00:00
Patrick Fic
dd59f3d026 IO-1286 Retry Supplement Importing fix. 2021-08-12 09:46:50 -07:00
Patrick Fic
ccb00ff391 Merged in feature/2021-08-13 (pull request #171)
feature/2021-08-13

Approved-by: Patrick Fic
2021-08-11 20:51:19 +00:00
Patrick Fic
b37970a6df IO-1300 Line Desc Null for Job costing 2021-08-11 13:50:37 -07:00
Patrick Fic
2cd7fcfbd8 Merged in hotfix/2021-08-11 (pull request #170)
hotfix/2021-08-11

Approved-by: Patrick Fic
2021-08-11 20:03:58 +00:00
Patrick Fic
77819b06fe Merged in hotfix/2021-08-11 (pull request #169)
hotfix/2021-08-11

Approved-by: Patrick Fic
2021-08-11 20:03:30 +00:00
Patrick Fic
4e936b4cff Merged in hotfix/2021-08-11 (pull request #168)
hotfix/2021-08-11

Approved-by: Patrick Fic
2021-08-11 20:03:10 +00:00
Patrick Fic
b2362a85fa Merged in hotfix/2021-08-11 (pull request #167)
hotfix/2021-08-11

Approved-by: Patrick Fic
2021-08-11 19:58:52 +00:00
Patrick Fic
f45f351678 IO-1310 resolve production list issue. 2021-08-11 12:57:46 -07:00
Patrick Fic
311171fa0a Merged in feature/2021-08-13 (pull request #166)
Merge into CDK Branch

Approved-by: Patrick Fic
2021-08-11 18:22:20 +00:00
Patrick Fic
7e05f03f4d Resolve merge conflicts. 2021-08-11 11:21:49 -07:00
Patrick Fic
76826c1e80 Merged in feature/2021-08-13 (pull request #165)
Include sentry username

Approved-by: Patrick Fic
2021-08-11 18:16:43 +00:00
Patrick Fic
18618efdc0 Include sentry username 2021-08-11 11:16:22 -07:00
Patrick Fic
d1952dfc25 Merged in feature/2021-08-13 (pull request #164)
IO-1300 Resolve null line desc.

Approved-by: Patrick Fic
2021-08-11 18:10:01 +00:00
Patrick Fic
fe5f4f2727 IO-1300 Resolve null line desc. 2021-08-11 11:09:16 -07:00
Patrick Fic
fd7c907b8f IO-233 Beging get vehicle makes 2021-08-11 11:08:03 -07:00
Patrick Fic
0273255c2c Merged in feature/2021-08-13 (pull request #163)
Feature/2021 08 13
2021-08-11 15:09:36 +00:00
Patrick Fic
e86160e530 IO-1307 Resolve table check error on export tables. 2021-08-11 08:06:51 -07:00
Patrick Fic
9f7e0d611a IO-1300 Resolve line desc to lower case error. 2021-08-11 07:59:32 -07:00
Patrick Fic
0b523efa95 IO-1304 Resolve ins co nm update on import. 2021-08-11 07:58:01 -07:00
Patrick Fic
6b64499e24 IO-233 Resolv merge issues & move allocations to ws 2021-08-10 14:30:08 -07:00
Patrick Fic
e78e628bec Merge branch 'feature/cdk' into feature/2021-08-13 2021-08-10 13:58:58 -07:00
Patrick Fic
ed8eb51c2f IO-233 CDK Allocations Summary 2021-08-10 13:42:53 -07:00
Patrick Fic
124d68ef68 IO-233 CDK Shop Config 2021-08-10 08:40:38 -07:00
Patrick Fic
3e802c1465 Merged in feature/2021-08-06 (pull request #162)
Feature/2021 08 06
2021-08-06 19:31:18 +00:00
Patrick Fic
ac18e78897 Merged in feature/2021-08-06 (pull request #161)
Resolve CI Error
2021-08-05 16:27:15 +00:00
Patrick Fic
e37ee39e88 Resolve CI Error 2021-08-05 09:25:59 -07:00
Patrick Fic
fed16a4aa3 Merged in feature/2021-08-06 (pull request #160)
Feature/2021 08 06
2021-08-05 16:18:03 +00:00
Patrick Fic
8c94dfce9e Time ticket sort update & remove io event logging. 2021-08-05 09:11:53 -07:00
Patrick Fic
69c13dd052 IO-1286 Resolve supplementing issues. 2021-08-05 09:10:09 -07:00
Patrick Fic
a5fe54164e IO-1285 Resolve production not saving sort order. 2021-08-04 09:30:53 -07:00
Patrick Fic
5ecd5a5a5c IO-1122 Add permission to text to intake checklist. 2021-08-04 09:10:23 -07:00
Patrick Fic
ed45347c23 Merged in feature/2021-07-30 (pull request #159)
feature/2021-07-30

Approved-by: Patrick Fic
2021-08-04 00:42:40 +00:00
Patrick Fic
3e0385479f IO-1234 Remove excess resp. center setup. 2021-08-03 17:28:32 -07:00
Patrick Fic
36f833be91 IO-1281 Prevent posting of tickets to closed RO 2021-08-03 16:52:06 -07:00
Patrick Fic
8fb39f9ea4 Merged in feature/2021-07-30 (pull request #158)
feature/2021-07-30

Approved-by: Patrick Fic
2021-08-03 18:46:24 +00:00
Patrick Fic
5b84ebbc25 IO-1284 Job Admin updates 2021-08-03 11:45:36 -07:00
Patrick Fic
b0ec7867b5 IO-1281 System setting for posting time tickets to closed RO 2021-08-03 11:27:03 -07:00
Patrick Fic
4e4c59ce4d IO-1262 Add SMS reminder to schedule. 2021-08-03 11:14:49 -07:00
Patrick Fic
e9bf1c05ad IO-594 Adjust Autohouse FIle Name 2021-08-03 10:56:33 -07:00
Patrick Fic
7ac1fa5abf Merged in feature/2021-07-30 (pull request #157)
Feature/2021 07 30
2021-07-30 22:25:33 +00:00
Patrick Fic
a489ac1d26 Merged in feature/2021-07-30 (pull request #156)
Remove FCM token.

Approved-by: Patrick Fic
2021-07-30 22:21:27 +00:00
Patrick Fic
4d7a7442ce Remove FCM token. 2021-07-30 15:21:04 -07:00
Patrick Fic
a19bce5a37 Merged in feature/2021-07-30 (pull request #155)
feature/2021-07-30

Approved-by: Patrick Fic
2021-07-30 22:11:48 +00:00
Patrick Fic
35782244bf Merge branch 'feature/2021-07-30' of bitbucket.org:snaptsoft/bodyshop into feature/2021-07-30 2021-07-30 15:11:21 -07:00
Patrick Fic
7407429344 Final phone resolution. 2021-07-30 15:11:16 -07:00
Patrick Fic
55c532f6e2 Merged in feature/2021-07-30 (pull request #154)
Resolve more Phone Lib issues.
2021-07-30 17:24:36 +00:00
Patrick Fic
d3c8b5d731 Resolve more Phone Lib issues. 2021-07-30 10:23:57 -07:00
Patrick Fic
cf09f98d7e Merged in feature/2021-07-30 (pull request #153)
Resolve break with Phone Package.
2021-07-30 16:46:38 +00:00
Patrick Fic
d8e8a8e4e9 Resolve break with Phone Package. 2021-07-30 09:45:59 -07:00
Patrick Fic
65210dea2f Merged in feature/2021-07-30 (pull request #152)
Add several reports to report center.

Approved-by: Patrick Fic
2021-07-29 21:06:18 +00:00
Patrick Fic
0c9b850872 Add several reports to report center. 2021-07-29 14:05:46 -07:00
Patrick Fic
99486830b7 Merged in feature/2021-07-30 (pull request #151)
feature/2021-07-30

Approved-by: Patrick Fic
2021-07-29 20:24:45 +00:00
Patrick Fic
74a62a46d3 IO-1280 Sorting on Available jobs. 2021-07-29 13:24:14 -07:00
Patrick Fic
d306041bcf IO-992 Audit trail bugfixes. 2021-07-29 13:18:28 -07:00
Patrick Fic
ae8a924cd6 IO-1273 Resolve dashboard error 2 2021-07-28 16:31:32 -07:00
Patrick Fic
6ab1b9f787 Merged in feature/2021-07-30 (pull request #150)
feature/2021-07-30

Approved-by: Patrick Fic
2021-07-28 22:27:36 +00:00
Patrick Fic
46ddc440fe IO-992 WIP Job Audits 2021-07-28 15:25:01 -07:00
Patrick Fic
6bf8eacfbd IO-1280 Resolve available jobs sort. 2021-07-28 15:24:58 -07:00
Patrick Fic
b2fa4f220d IO-1277 Protect production note on checklist. 2021-07-28 12:13:17 -07:00
Patrick Fic
2f175c304c IO-1276 Remove ability to return IH invoice. 2021-07-28 12:02:43 -07:00
Patrick Fic
79714e5708 IO-992 Job Audit additions. 2021-07-28 11:58:31 -07:00
Patrick Fic
7c5aa9c913 IO-1275 Finish appointment notes. 2021-07-28 11:34:35 -07:00
Patrick Fic
59b8bae182 IO-1275 WIP Appointment notes. 2021-07-28 11:00:02 -07:00
Patrick Fic
35323ba624 IO-1273 Graceful error on job totals. 2021-07-27 11:19:23 -07:00
Patrick Fic
8ca3741a52 IO-1274 Change Password on profilel 2021-07-26 16:54:31 -07:00
Patrick Fic
c3c021774e Merged in feature/2021-07-30 (pull request #149)
feature/2021-07-30

Approved-by: Patrick Fic
2021-07-22 23:30:47 +00:00
Patrick Fic
6b811d635b IO-992 Job Audit Logs 2021-07-22 16:29:41 -07:00
Patrick Fic
97aecd3ddc IO-594 add SSH key support for AH 2021-07-22 08:40:05 -07:00
Patrick Fic
c4fdef445e Merged in feature/2021-07-23 (pull request #148)
Feature/2021 07 23
2021-07-22 00:23:07 +00:00
Patrick Fic
e642087360 Merged in feature/2021-07-23 (pull request #147)
IO-594 Add AH Settings
2021-07-21 20:18:47 +00:00
Patrick Fic
098754125b IO-594 Add AH Settings 2021-07-21 13:11:51 -07:00
Patrick Fic
6ce2d2723b Merged in feature/2021-07-23 (pull request #146)
Feature/2021 07 23
2021-07-21 18:21:44 +00:00
Patrick Fic
ae4a864533 IO-1264 IO-1271 Report Center additions & format. 2021-07-21 11:19:23 -07:00
Patrick Fic
27d9322ced IO-594 Include AH Requested changes 2021-07-21 08:50:29 -07:00
Patrick Fic
f5003080db Removed unnecessary import. 2021-07-20 16:37:19 -07:00
Patrick Fic
79e11dda4c Merged in feature/2021-07-23 (pull request #145)
IO-594 Create schedulable AH export.
2021-07-20 23:26:16 +00:00
Patrick Fic
3b992edc21 IO-594 Create schedulable AH export. 2021-07-20 16:25:08 -07:00
Patrick Fic
f75f88840f Merged in feature/2021-07-23 (pull request #144)
Feature/2021 07 23
2021-07-20 17:56:01 +00:00
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
11cfef904b Merged in feature/2021-07-16 (pull request #143)
Feature/2021 07 16
2021-07-16 21:51:15 +00:00
Patrick Fic
a45dcd307b Merged in feature/2021-07-16 (pull request #142)
Feature/2021 07 16
2021-07-16 21:45:25 +00:00
Patrick Fic
54b483333f Revert "IO-1257 Accept special characters"
This reverts commit a8ad65000d.
2021-07-16 14:42:12 -07:00
Patrick Fic
7f4a36038e Merged in feature/2021-07-16 (pull request #141)
IO-1189 IO-1190 IO-1259 Added gen doc keys.
2021-07-16 17:15:46 +00:00
Patrick Fic
990ec1a553 IO-1189 IO-1190 IO-1259 Added gen doc keys. 2021-07-16 10:13:50 -07:00
Patrick Fic
12307cbd56 Merged in feature/2021-07-16 (pull request #140)
IO-1264 Add CSR reports
2021-07-16 16:26:46 +00:00
Patrick Fic
6bd49a461e IO-1264 Add CSR reports 2021-07-16 09:25:03 -07:00
Patrick Fic
c9ed8a9360 Merged in feature/2021-07-16 (pull request #139)
Feature/2021 07 16
2021-07-16 15:32:01 +00:00
Patrick Fic
120d6f9f5f Merged in feature/2021-07-16 (pull request #138)
feature/2021-07-16

Approved-by: Patrick Fic
2021-07-15 22:11:58 +00:00
Patrick Fic
0d30fc0e32 IO-1219 Improve display of job status. 2021-07-15 13:59:23 -07:00
Patrick Fic
6f64cb71f2 Merged in feature/2021-07-16 (pull request #137)
feature/2021-07-16
2021-07-15 18:42:45 +00: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
7a6a834998 Merged in feature/2021-07-16 (pull request #136)
feature/2021-07-16

Approved-by: Patrick Fic
2021-07-13 19:32:15 +00: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
7999895323 Merged in feature/2021-07-09 (pull request #135)
Feature/2021 07 09
2021-07-09 20:52:55 +00: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
71ef3dadc5 Merged in feature/2021-07-09 (pull request #132)
Feature/2021 07 09
2021-07-08 01:06:58 +00: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
f0d6c5e1b1 IO-233 CDK WIP 2021-07-06 09:31:01 -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
991df9c48f Merged in hotfix/2021-07-02 (pull request #128)
hotfix/2021-07-02

Approved-by: Patrick Fic
2021-07-02 20:34:59 +00: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
84b39f3d2b IO-233 WIP CDK 2021-07-02 12:53:18 -07:00
Patrick Fic
4ab0947cc8 IO-233 Add customer insert actions. 2021-06-30 16:04:01 -07:00
Patrick Fic
8cbef14ea3 Merged in hotfix/2021-06-30 (pull request #126)
Hotfix/2021 06 30
2021-06-30 20:47:05 +00: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
105ecd4221 IO-233 CDK 2021-06-30 13:41:00 -07:00
Patrick Fic
3176cfcc56 Merged in hotfix/2021-06-30 (pull request #124)
Hotfix/2021 06 30
2021-06-30 20:17:34 +00: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
a2f3d9a1b6 Remove multi print center. 2021-06-25 14:09:19 -07: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
630dacd8bf Added missing template. 2021-06-25 12:06:35 -07: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
2172cc2d04 IO-233 WS Updates and templat eadditions 2021-06-25 07:42:49 -07:00
Patrick Fic
b49555e111 IO-1211 Feature Restrictions 2021-06-23 14:15:41 -07:00
Patrick Fic
d634fcd4cf IO-1218 CLM_NO Ui fix on payments enter. 2021-06-23 13:31:25 -07:00
Patrick Fic
a7e972b3ce IO-1213 Recalc after line delete. 2021-06-23 13:22:03 -07:00
Patrick Fic
35273c64bd IO-233 Add soap request structure 2021-06-23 12:19:51 -07:00
Patrick Fic
5be2d7bd39 IO-233 WIP CDK. 2021-06-23 10:57:57 -07: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
4f6bb02ab7 IO-233 Base websocket setup for CDK. 2021-06-22 13:45:36 -07:00
Patrick Fic
5a109c5752 IO-1215 Roudning on dashboard graph. 2021-06-21 11:58:09 -07:00
Patrick Fic
756e363e92 IO-1208 Update missing translation. 2021-06-21 11:50:43 -07:00
Patrick Fic
d90a0cd0c8 IO-1212 Missing stripe hyperlinks. 2021-06-21 11:48:48 -07:00
Patrick Fic
6de9007c3a IO-1213 Recalc totals on jobline insert. 2021-06-21 09:36:03 -07:00
Patrick Fic
0b2584e2f1 IO-1130 Update schedule job modal styles. 2021-06-21 09:22:24 -07:00
Patrick Fic
1f59d114e8 IO-1186 Export customer data. 2021-06-21 09:11:28 -07:00
Patrick Fic
69ac8617da Merged in feature/2021-06-18 (pull request #115)
RO form items for checklist dates.

Approved-by: Patrick Fic
2021-06-18 20:51:47 +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
12bc4faf48 RO form items for checklist dates. 2021-06-18 13:50:13 -07: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
012f256b77 Add delivery to checklist & remove jria submit on error. 2021-06-18 11:22:49 -07: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
ae67033417 Resolve issue on payments page. 2021-06-17 10:37:29 -07:00
Patrick Fic
1489d5cd5a Minor stripe updates. 2021-06-17 08:56:12 -07: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
989c7b2ba0 Move CI to yarn. 2021-06-16 14:24:26 -07:00
Patrick Fic
1575d5e3e7 IO-1209 Part type not saving. 2021-06-16 13:27:10 -07: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
ba8c8bc976 IO-1210 Jobs Close Updates 2021-06-16 12:07:56 -07:00
Patrick Fic
145e3e5c44 Styling fixes to allow for printing of pages. 2021-06-16 11:41:33 -07:00
Patrick Fic
914a7e3c7b IO-306 Dashboard monthly employee efficiency 2021-06-16 10:51:53 -07: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
f20ef2d11d IO-1209 Jobline presets. 2021-06-15 16:41:24 -07:00
Patrick Fic
471df3b659 IO-306 Furhter dashboard development 2021-06-15 15:00:07 -07:00
Patrick Fic
b12ad405c3 IO-594 additional autohouse improvements. 2021-06-15 13:32:17 -07: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
a42da5b6da IO-306 Further development of dashboard. 2021-06-14 19:37:17 -07:00
Patrick Fic
db76992c70 IO-306 Creation of dashboard. 2021-06-14 16:00:58 -07: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
3ab31c8bee IO-1205 IO-1207 IO-1204 Minor UI Updates. 2021-06-14 10:44:05 -07:00
Patrick Fic
8d74ef275e IO-1202 CC List hyperlink 2021-06-14 09:58:40 -07:00
Patrick Fic
5b9640a1de IO-1185 Adjust contract find to use plate. 2021-06-14 09:55:09 -07:00
Patrick Fic
f8c1087360 IO-1177 Resolve deleting of raw files. 2021-06-14 09:50:30 -07:00
Patrick Fic
2368aeb5e8 Merged in feature/2021-06-18 (pull request #106)
Feature/2021 06 18
2021-06-11 17:04:23 +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
bb2d62a11c Package updates. 2021-06-11 08:57:50 -07: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
75606559c6 Update documents transformations + crisp. 2021-06-10 14:16:02 -07: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
a4b58b4bd9 Removed console logs & pakage updates 2021-06-09 11:33:31 -07:00
Patrick Fic
41c30fe704 IO-1117 IO-1087 2021-06-09 10:52:33 -07:00
Patrick Fic
7745848961 IO-1126 Consistent edit icons. 2021-06-09 10:38:50 -07:00
Patrick Fic
7a8e8de724 IO-1124 Archive message 2021-06-09 09:59:49 -07:00
Patrick Fic
a45bf6d959 IO-541 Payments table allow for closed 2021-06-08 17:17:20 -07:00
Patrick Fic
c6df38e753 IO-1059 Auto validation for bill on exported job. 2021-06-08 17:14:47 -07:00
Patrick Fic
0fa214f029 IO-117 Deleting docuemnts server side. 2021-06-08 16:59:16 -07:00
Patrick Fic
ef06e67c9f Merged in hotfix/2021-06-08 (pull request #102)
Add rounding for depreciation.
2021-06-08 23:36:22 +00:00
Patrick Fic
9ddb83a761 Merged in hotfix/2021-06-08 (pull request #101)
Emergency Hotfix 2021-06-8 - Add rounding for depreciation.
2021-06-08 22:57:09 +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
87d2618020 Add rounding for depreciation. 2021-06-08 15:41:24 -07:00
Patrick Fic
bd2f22f059 WIP Deleting 2021-06-08 15:37:24 -07: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
66f98656b0 IO-541 allow payments on detail action 2021-06-07 13:20:44 -07:00
Patrick Fic
e801a03984 IO-1059 Prevent posting bills to closed jobs. 2021-06-07 13:16:31 -07:00
Patrick Fic
979ba1c142 IO-557 Send documents in emails. 2021-06-07 12:27:14 -07:00
Patrick Fic
784c58e295 Merged in feature/2020-06-04 (pull request #98)
Feature/2020 06 04
2021-06-04 20:21:40 +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
fb5a146dee IO-1182 Restrict manual posting to in house vendor. 2021-06-04 09:26:15 -07:00
Patrick Fic
a8b555b773 IO-1140 Crisp will only set email for valid emails & release notes styles updates. 2021-06-04 08:21:40 -07: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
aa60424eae IO-1180 Resolved sorting issues on All Jobs Page. 2021-06-03 10:27:45 -07:00
Patrick Fic
95ba0e1f8a IO-1140 Added release notes integration. 2021-06-03 10:02:04 -07:00
Patrick Fic
afeb2b94cd Merged in feature/2020-06-04 (pull request #95)
Feature/2020 06 04
2021-06-02 23:19:55 +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
786d76bb73 Upload documents with unique time stamp. 2021-06-02 13:00:38 -07:00
Patrick Fic
8427ea208b IO-1169 Download only images. 2021-06-02 10:48:01 -07: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
Patrick Fic
b897795c27 Move MAPA/MASH to parts profit centers 2021-06-02 10:08:51 -07:00
Patrick Fic
9dfff36edf IO-1174 Adjust bill delete export log FK 2021-06-02 09:13:42 -07:00
Patrick Fic
9d4f98d3ee IO-1167 Resolve sorting on payments table. 2021-06-02 09:07:29 -07:00
Patrick Fic
8770e95ee3 IO-1116 Notes Multiline display 2021-06-02 09:00:18 -07:00
Patrick Fic
9ff9baa78c IO-1096 Added order by to Parts Order 2021-06-02 08:49:10 -07:00
Patrick Fic
28326f2628 IO-1176 Move duplication totals to server side. 2021-06-01 16:49:54 -07:00
Patrick Fic
7c35a2e790 Merged in test (pull request #92)
Test
2021-06-01 21:58:40 +00:00
Patrick Fic
61c57e1866 Merged in hotfix/2021-06-01 (pull request #91)
Hotfix/2021 06 01
2021-06-01 21:47:20 +00:00
Patrick Fic
488d1be1cc Merged in hotfix/2021-06-01 (pull request #90)
Fix typo.
2021-06-01 21:45:25 +00:00
Patrick Fic
11d3e3c7ad Fix typo. 2021-06-01 14:44:11 -07:00
Patrick Fic
a61b4a47cf Merged in hotfix/2021-06-01 (pull request #89)
IO-1178 Resolve minor bugs.
2021-06-01 21:37:22 +00:00
Patrick Fic
0108135fe9 IO-1178 Resolve minor bugs. 2021-06-01 14:36:28 -07:00
Patrick Fic
372b7e8e30 Merged in hotfix/2021-06-01 (pull request #88)
Additional changes to translations.
2021-06-01 19:17:38 +00:00
Patrick Fic
74b3ceec63 Additional changes to translations. 2021-06-01 12:16:23 -07:00
Patrick Fic
144fe06e60 Merged in hotfix/2021-06-01 (pull request #87)
Emergency Hotfix for line markup issues. IO-1178
2021-06-01 19:15:47 +00:00
Patrick Fic
bef01385b2 Attempt to result PR conflicts. 2021-06-01 12:15:08 -07:00
Patrick Fic
fa3ccf0fea Resolve language conflicts. 2021-06-01 12:06:48 -07:00
Patrick Fic
d2bb7121cb IO-1178 Resolve line markup issues. 2021-06-01 11:52:09 -07:00
Patrick Fic
2dbcd203ce Merged in feature/2020-06-04 (pull request #86)
Feature/2020 06 04
2021-06-01 18:06:51 +00:00
Patrick Fic
1eb65dbc43 Rename all reports from source to ins_co 2021-06-01 10:51:27 -07:00
Patrick Fic
b066e08511 Resolved nesting issue in report center. 2021-06-01 10:24:47 -07:00
Patrick Fic
214d07f2ef Merged in feature/2020-06-04 (pull request #81)
Feature/2020 06 04 - Batch 2
2021-06-01 01:17:54 +00:00
Patrick Fic
16746dd2a0 Merged in hotfix/2021-05-31 (pull request #85)
Emergency fix #2 for GST Registrant.
2021-06-01 01:11:37 +00:00
Patrick Fic
bcec0cd902 Merged in hotfix/2021-05-31 (pull request #84)
Emergency fix #2 for GST Registrant.
2021-06-01 01:08:12 +00:00
Patrick Fic
6faf69a875 Emergency fix #2 for GST Registrant. 2021-05-31 18:07:42 -07:00
Patrick Fic
6f229a859b Merged in hotfix/2021-05-31 (pull request #83)
Emergency hotfix for GST registrant.
2021-06-01 00:54:34 +00:00
Patrick Fic
092904377f Merged in hotfix/2021-05-31 (pull request #82)
Emergency hotfix for GST registrant.
2021-06-01 00:53:00 +00:00
Patrick Fic
7277688de4 Emergency hotfix for GST registrant. 2021-05-31 17:51:11 -07:00
Patrick Fic
9e5ff432d7 Remove JIRA Widget. 2021-05-31 14:30:59 -07:00
Patrick Fic
5817daa4b3 IO-1175 Allow for null vin. 2021-05-31 14:20:27 -07:00
Patrick Fic
51292f50dc IO-1176 Duplicate job inversion. 2021-05-31 14:19:15 -07:00
Patrick Fic
b5e9e75751 IO-1175 Vehicle not required metadata changes. 2021-05-31 14:18:57 -07:00
Patrick Fic
f778b964fd ENV Changes. 2021-05-31 13:56:54 -07:00
Patrick Fic
06c14d2742 Merged in feature/2020-06-04 (pull request #80)
Feature/2020 06 04 - Batch 1 of updates
2021-05-31 20:16:13 +00:00
Patrick Fic
7441288cf5 IO-1173 Missing Reports 2021-05-31 13:11:04 -07:00
Patrick Fic
7524cdf0b1 IO-1166 Job Costing Summaries updates. 2021-05-31 12:21:27 -07:00
Patrick Fic
46dff9f52c IO-1163 Compatibility for HEIC. 2021-05-31 12:21:11 -07:00
Patrick Fic
afb0c85e9f IO-1167 IO-775 Minor bug fixes. 2021-05-31 10:56:13 -07:00
Patrick Fic
af6bb18db2 IO-1138 Additional offline check. 2021-05-31 10:50:28 -07:00
Patrick Fic
c28d4c15a0 IO-1144 Image Editor Changes. 2021-05-31 10:45:06 -07:00
Patrick Fic
0c167a1833 Merged in test (pull request #79)
Update 2021-06-04 Feature Branch with latest fixes.
2021-05-31 15:14:08 +00:00
Patrick Fic
f7938df5e4 WIP Photo Editor 2021-05-28 18:18:09 -07:00
Patrick Fic
5717677339 Merged in hotfix/2020-05-28 (pull request #78)
Hotfix/2020 05 28 - Production Deployment
2021-05-28 18:28:52 +00:00
Patrick Fic
cffe9cd4f6 Merged in feature/2020-06-04 (pull request #77)
Added error handling to Crisp.
2021-05-28 04:15:09 +00:00
Patrick Fic
5da8c77b3a Added error handling to Crisp. 2021-05-27 21:13:36 -07:00
Patrick Fic
3cc652d113 Merged in feature/2020-06-04 (pull request #76)
Major Features - Adding Crisp to Test
2021-05-28 03:20:57 +00:00
Patrick Fic
7d82fb8f04 Added Crisp scripts. 2021-05-27 20:16:50 -07:00
Patrick Fic
f1cbc9f775 Merged in hotfix/2020-05-28 (pull request #75)
Hotfix to test for IO-1152.
2021-05-27 23:50:08 +00:00
Patrick Fic
58c6ba2f87 IO-1152 Add missing fields to query. 2021-05-27 16:49:21 -07:00
Patrick Fic
0fbd4b495b Merged in hotfix/2020-05-28 (pull request #74)
Hotfix to test for IO-1152.
2021-05-27 23:25:55 +00:00
Patrick Fic
343274e1e2 IO-1152 Missed in last commit. 2021-05-27 16:25:07 -07:00
Patrick Fic
bc729c0f8c Merged in hotfix/2020-05-28 (pull request #73)
Hotfix to test for IO-1152.
2021-05-27 23:23:10 +00:00
Patrick Fic
09ce5ac892 IO-1152 Resolve parts markup issue. 2021-05-27 16:21:54 -07:00
Patrick Fic
2536b0b986 Merged in hotfix/2020-05-28 (pull request #72)
Hotfix/2020 05 28 deployment to Test.
2021-05-27 21:59:43 +00:00
Patrick Fic
c8506387f6 IO-1131 - Remove version #. 2021-05-27 14:58:23 -07:00
Patrick Fic
79f2c7dd3d IO-1086 confirmation on profile update. 2021-05-27 14:11:16 -07:00
Patrick Fic
d91a83a137 IO-1157 Add mapa/mash costs to costing. 2021-05-27 14:04:52 -07:00
Patrick Fic
0b21b8d976 IO-1138 Offline detection. 2021-05-27 13:32:57 -07:00
Patrick Fic
842cb54867 IO-1153 Phonebook Validation 2021-05-27 13:15:58 -07:00
Patrick Fic
a5628188d8 IO-1171 Null vehicle link on detail page. 2021-05-27 11:35:58 -07:00
Patrick Fic
1b0e37be45 IO-998 Resolve media rename issues for non images. 2021-05-27 08:49:43 -07:00
Patrick Fic
48ecfe0d98 Media File improvements + separate DEV testing instance for cloudinary. 2021-05-26 16:45:59 -07:00
Patrick Fic
b5b4a3a4f9 IO-1080 Standard audit note language. 2021-05-26 14:16:17 -07:00
Patrick Fic
585d585cd3 Merge branch 'hotfix/2020-05-26' into hotfix/2020-05-28 2021-05-26 14:10:04 -07:00
Patrick Fic
42356c4e99 Merged in hotfix/2020-05-26 (pull request #71)
Deployment of Hotfix/2020 05 26
2021-05-26 18:24:58 +00:00
Patrick Fic
cd9aeba9f7 Merged in hotfix/2020-05-26 (pull request #70)
Hotfix/2020 05 26 - Additional changes & retest
2021-05-26 17:45:20 +00:00
Patrick Fic
8a7a5f35c1 IO-1095 unrequire fields on vehicle create. 2021-05-26 10:34:32 -07:00
Patrick Fic
b24c5e7cf1 IO-1128 Change labor allocations table coloring. 2021-05-26 10:19:43 -07:00
Patrick Fic
e8b7e2f0b9 IO-1152 Add in part mark up for BC Economical claims. 2021-05-26 09:06:05 -07:00
Patrick Fic
b75e14e4f5 Merged in hotfix/2020-05-26 (pull request #69)
Hotfix/2020 05 26 to Testing Env.
2021-05-25 23:33:23 +00:00
Patrick Fic
3e59c60477 IO-1168 Resolve payments export all null error. 2021-05-25 16:31:09 -07:00
Patrick Fic
e89b4fe2a4 IO-1137 Download zip files with appropriate name. 2021-05-25 16:30:29 -07:00
Patrick Fic
73b0542b62 IO-1148 Prefill scheduled completion on intake if not present. 2021-05-25 14:58:50 -07:00
Patrick Fic
c9812c36c0 IO-1129 MIssing color on schedule modal. 2021-05-25 14:47:16 -07:00
Patrick Fic
d62a2c0aaf IO-1095 Remove required fields vehicle/owner. 2021-05-25 14:42:59 -07:00
Patrick Fic
ee15f063ce IO-1158 Change Ins Co Nm to select 2021-05-25 11:21:55 -07:00
Patrick Fic
5f0a683ec1 IO-1161 Line Modal Hours to precision 1. 2021-05-25 09:58:46 -07:00
Patrick Fic
6ae9de7df3 Merged in test (pull request #68)
Release Hotfix for 2021-05-21
2021-05-21 22:18:54 +00:00
Patrick Fic
19aa41ab5b Merged in hotfix/2021-05-21 (pull request #67)
IO-1156 Update order for temporary docs.
2021-05-21 21:04:39 +00:00
Patrick Fic
0494b3a9a6 IO-1156 Update order for temporary docs. 2021-05-21 14:02:25 -07:00
Patrick Fic
01d9dc9033 Added commit # to server api. 2021-05-21 13:47:11 -07:00
Patrick Fic
47b8d8d8b2 Merged in hotfix/2021-05-21 (pull request #66)
IO-1139 Adjust to 1 decimal.
2021-05-21 20:21:07 +00:00
Patrick Fic
6e3453cd90 IO-1139 Adjust to 1 decimal. 2021-05-21 13:18:57 -07:00
Patrick Fic
4e10451bea Merged in hotfix/2021-05-21 (pull request #65)
Hotfix/2021 05 21
2021-05-21 20:10:57 +00:00
Patrick Fic
81d5c8f449 IO-1139 Total hours to 2 decimals. 2021-05-21 13:08:59 -07:00
Patrick Fic
79fa71fc84 IO-1160 Target Touch Time Highlighting. 2021-05-21 12:44:08 -07:00
Patrick Fic
f6378daa89 IO-1151 IO-1159 Calculated Repair & Label Updates 2021-05-21 12:37:54 -07:00
Patrick Fic
f2a4eb1b65 IO-1101 Fix PAP marking of lines. 2021-05-21 09:38:30 -07:00
Patrick Fic
2d19c35177 IO-1147 Default tax rates on parts for manual jobs. 2021-05-21 09:31:55 -07:00
Patrick Fic
9787f7e377 Merged in feature/IOS-7-documents-should-upload-in-chronol (pull request #64)
IOS-7 Added takenat to documents and uploads.
2021-05-21 14:38:28 +00:00
Patrick Fic
3abac3e7ac Merged master into test 2021-05-20 14:58:05 +00:00
Patrick Fic
fddf75b40b IOS-7 Added takenat to documents and uploads. 2021-05-19 17:52:24 -07:00
Patrick Fic
a7878243ee Merged in test (pull request #62)
IO-1145 Remove force token refresh.
2021-05-19 23:31:57 +00:00
727 changed files with 113854 additions and 57018 deletions

1
.gitignore vendored
View File

@@ -112,3 +112,4 @@ firebase/.env
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log

View File

@@ -1 +1 @@
client_max_body_size 15M;
client_max_body_size 50M;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ hooks.js:
module.exports = [
{
path: "/pull",
command: "git pull && npm i",
command: "git pull && yarn && pm2 restart 0",
cwd: "/home/ubuntu/io/",
method: "post",
},

View File

@@ -28,7 +28,7 @@ import JobsShow from "../jobs/jobs.show";
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
headers: {
"x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`,
"x-hasura-admin-secret": `Dev-BodyShopApp!`,
// 'Authorization': `Bearer xxxx`,
},
});
@@ -67,7 +67,7 @@ const client = new ApolloClient({
// uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
// cache: new InMemoryCache(),
// headers: {
// "x-hasura-admin-secret": `Dev-BodyShopAppBySnaptSoftware!`,
// "x-hasura-admin-secret": `Dev-BodyShopApp!`,
// // 'Authorization': `Bearer xxxx`,
// },
// });

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,25 @@
// craco.config.js
const TerserPlugin = require("terser-webpack-plugin");
const CracoLessPlugin = require("craco-less");
const SentryWebpackPlugin = require("@sentry/webpack-plugin");
module.exports = {
plugins: [
{
plugin: SentryWebpackPlugin,
options: {
// sentry-cli configuration
authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software",
project: "imexonline",
release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration
include: ".",
ignore: ["node_modules", "webpack.config.js"],
},
},
{
plugin: CracoLessPlugin,
options: {
@@ -12,7 +28,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
@@ -51,4 +69,5 @@ module.exports = {
},
}),
},
devtool: "source-map",
};

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.44.1 (20200629.0846)
-->
<!-- Title: G Pages: 1 -->
<svg width="43pt" height="43pt"
viewBox="0.00 0.00 43.20 43.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(21.6 21.6)">
<title>G</title>
<polygon fill="#111111" stroke="transparent" points="-21.6,21.6 -21.6,-21.6 21.6,-21.6 21.6,21.6 -21.6,21.6"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 613 B

42958
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,80 +2,95 @@
"name": "bodyshop",
"version": "0.1.1",
"private": true,
"proxy": "http://localhost:5000",
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.3.17",
"@craco/craco": "^6.1.2",
"@fingerprintjs/fingerprintjs": "^3.1.2",
"@lourenci/react-kanban": "^2.1.0",
"@sentry/react": "^6.3.6",
"@sentry/tracing": "^6.3.6",
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.14.0",
"@tanem/react-nprogress": "^3.0.65",
"antd": "^4.15.5",
"@apollo/client": "^3.4.17",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@openreplay/tracker": "^3.4.7",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.14.3",
"@sentry/tracing": "^6.14.3",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.21.1",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.16.13",
"apollo-link-logger": "^2.0.0",
"axios": "^0.21.1",
"craco-less": "^1.17.1",
"dinero.js": "^1.8.1",
"dotenv": "^9.0.2",
"axios": "^0.24.0",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"firebase": "^8.6.0",
"graphql": "^15.5.0",
"i18next": "^20.2.2",
"i18next-browser-languagedetector": "^6.1.1",
"jsoneditor": "^9.4.1",
"exifr": "^7.1.3",
"firebase": "^9.4.1",
"graphql": "^16.0.1",
"i18next": "^21.4.2",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.7",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.17",
"logrocket": "^1.2.0",
"libphonenumber-js": "^1.9.42",
"logrocket": "^2.1.1",
"markerjs2": "^2.17.0",
"moment-business-days": "^1.2.0",
"phone": "^2.4.21",
"phone": "^3.1.9",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.0",
"query-string": "^7.0.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.33.2",
"react-big-calendar": "^0.38.1",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.1",
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-i18next": "^11.8.15",
"react-icons": "^4.2.0",
"react-number-format": "^4.5.5",
"react-redux": "^7.2.4",
"react-resizable": "^3.0.1",
"react-router-dom": "^5.2.0",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.14.2",
"react-icons": "^4.3.1",
"react-number-format": "^4.8.0",
"react-redux": "^7.2.6",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.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",
"recharts": "^2.1.6",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.32.13",
"styled-components": "^5.3.0",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^1.1.2",
"workbox-background-sync": "^6.1.5",
"workbox-broadcast-update": "^6.1.5",
"workbox-cacheable-response": "^6.1.5",
"workbox-core": "^6.1.5",
"workbox-expiration": "^6.1.5",
"workbox-google-analytics": "^6.1.5",
"workbox-navigation-preload": "^6.1.5",
"workbox-precaching": "^6.1.5",
"workbox-range-requests": "^6.1.5",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"workbox-streams": "^6.1.5"
"reselect": "^4.1.2",
"sass": "^1.43.4",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
"workbox-core": "^6.3.0",
"workbox-expiration": "^6.3.0",
"workbox-google-analytics": "^6.3.0",
"workbox-navigation-preload": "^6.3.0",
"workbox-precaching": "^6.3.0",
"workbox-range-requests": "^6.3.0",
"workbox-routing": "^6.3.0",
"workbox-strategies": "^6.3.0",
"workbox-streams": "^6.3.0"
},
"scripts": {
"postinstall": "patch-package",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"build:test": "env-cmd -f .env.test yarn run build",
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` react-scripts build",
"test": "craco test",
"eject": "react-scripts eject",
@@ -100,6 +115,8 @@
]
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.18.3",
"patch-package": "^6.4.7",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"
}

File diff suppressed because one or more lines are too long

View File

@@ -8,13 +8,61 @@
<meta name="description" content="ImEX Online" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<link rel="apple-touch-icon" href="logo192.png" />
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
(function () {
d = document;
s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<script>
!(function () {
"use strict";
var e = [
"debug",
"destroy",
"do",
"help",
"identify",
"is",
"off",
"on",
"ready",
"render",
"reset",
"safe",
"set",
];
if (window.noticeable)
console.warn("Noticeable SDK code snippet loaded more than once");
else {
var n = (window.noticeable = window.noticeable || []);
function t(e) {
return function () {
var t = Array.prototype.slice.call(arguments);
return t.unshift(e), n.push(t), n;
};
}
!(function () {
for (var o = 0; o < e.length; o++) {
var r = e[o];
n[r] = t(r);
}
})(),
(function () {
var e = document.createElement("script");
(e.async = !0), (e.src = "https://sdk.noticeable.io/l.js");
var n = document.head;
n.insertBefore(e, n.firstChild);
})();
}
})();
</script>
<!-- <script
data-jsd-embedded
data-key="51adb36e-ee16-46b1-a4c6-4b6d5fcd8530"
data-base-url="https://jsd-widget.atlassian.com"
src="https://jsd-widget.atlassian.com/assets/embed.js"
></script> -->
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/

View File

@@ -1,19 +1,52 @@
import { ApolloProvider } from "@apollo/client";
//import trackerRedux from "@openreplay/tracker-redux";
import Tracker from "@openreplay/tracker";
import trackerGraphQL from "@openreplay/tracker-graphql";
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import LogRocket from "logrocket";
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 trackerAssist from "@openreplay/tracker-assist";
import { getCurrentUser } from "../firebase/firebase.utils";
import client from "../utils/GraphQLClient";
import App from "./App";
import { useTranslation } from "react-i18next";
moment.locale("en-US");
export const tracker = new Tracker({
projectKey: "trDmOZlEXUpjGsMtHroA",
ingestPoint: "https://replay.imex.online/ingest",
...(process.env.NODE_ENV === null || process.env.NODE_ENV === "development"
? { __DISABLE_SECURE_MODE: true }
: {}),
// beaconSize: 10485760,
onStart: async ({ sessionID }) => {
const user = await getCurrentUser();
if (user) tracker.setUserID(user.email);
},
});
// tracker.use(
// trackerAssist({ confirmText: "Technical support is about to assist you." })
// ); // check the list of available options below
export const recordGraphQL = tracker.use(trackerGraphQL());
tracker.start();
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
export const factory = SplitSdk({
core: {
authorizationKey: process.env.REACT_APP_SPLIT_API,
key: "anon",
},
});
export default function AppContainer() {
const { t } = useTranslation();
return (
<ApolloProvider client={client}>
<ConfigProvider
@@ -28,7 +61,9 @@ export default function AppContainer() {
}}
>
<GlobalLoadingBar />
<App />
<SplitFactory factory={factory}>
<App />
</SplitFactory>
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -1,19 +1,23 @@
import { Button, Result } from "antd";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
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";
import { checkUserSession } from "../redux/user/user.actions";
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")
);
@@ -27,25 +31,58 @@ const MobilePaymentContainer = lazy(() =>
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
});
export function App({ checkUserSession, currentUser }) {
export function App({ checkUserSession, currentUser, online, setOnline }) {
useEffect(() => {
if (!navigator.onLine) {
setOnline(false);
}
checkUserSession();
}, [checkUserSession]);
}, [checkUserSession, setOnline]);
//const b = Grid.useBreakpoint();
// console.log("Breakpoints:", b);
const { t } = useTranslation();
window.addEventListener("offline", function (e) {
setOnline(false);
});
window.addEventListener("online", function (e) {
setOnline(true);
});
if (currentUser.authorized === null) {
return <LoadingSpinner message={t("general.labels.loggingin")} />;
}
if (!online)
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
{t("general.actions.refresh")}
</Button>
}
/>
);
return (
<Switch>
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
@@ -62,7 +99,7 @@ export function App({ checkUserSession, currentUser }) {
<Route exact path="/csi/:surveyId" component={CsiPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route exact path="/about" component={AboutPage} />
<Route exact path="/disclaimer" component={DisclaimerPage} />
</ErrorBoundary>
<ErrorBoundary>
<Route
@@ -85,6 +122,13 @@ export function App({ checkUserSession, currentUser }) {
component={TechPageContainer}
/>
</ErrorBoundary>
<ErrorBoundary>
<PrivateRoute
isAuthorized={currentUser.authorized}
path="/edit"
component={DocumentEditorContainer}
/>
</ErrorBoundary>
</Suspense>
</Switch>
);

View File

@@ -118,3 +118,19 @@
.production-list-min-height {
min-height: 19px;
}
#noticeable-widget {
iframe {
z-index: 2 !important;
}
}
.react-kanban-column {
background-color: #ddd !important;
}
.production-list-table {
td.ant-table-column-sort {
background: unset;
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,25 +1,9 @@
import Axios from "axios";
import React from "react";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
export default function Test() {
const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize", { userId: "1234" });
console.log("handleQbSignIn -> result", result.data);
// window.open(result.data, "_blank", "toolbar=0,location=0,menubar=0");
var parameters = "location=1,width=800,height=650";
parameters +=
",left=" +
(window.screen.width - 800) / 2 +
",top=" +
(window.screen.height - 650) / 2;
// Launch Popup
window.open(result.data, "connectPopup", parameters);
};
return (
<div>
<button onClick={handleQbSignIn}>Sign Into Qb.</button>
<QboAuthorizeComponent />
</div>
);
}

View File

@@ -9,8 +9,25 @@ import PayableExportAll from "../payable-export-all-button/payable-export-all-bu
import { DateFormatter } from "../../utils/DateFormatter";
import queryString from "query-string";
import { logImEXEvent } from "../../firebase/firebase.utils";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
export default function AccountingPayablesTableComponent({ loading, bills }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -109,6 +126,17 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
<Checkbox disabled checked={record.is_credit_memo} />
),
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
@@ -121,6 +149,7 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
billId={record.id}
disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills}
/>
</div>
),
@@ -154,6 +183,9 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
loadingCallback={setTransInProgress}
completedCallback={setSelectedBills}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

View File

@@ -5,11 +5,29 @@ import { Link } from "react-router-dom";
import { logImEXEvent } from "../../firebase/firebase.utils";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { alphaSort, dateSort } from "../../utils/sorters";
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
import { PaymentsExportAllButton } from "../payments-export-all-button/payments-export-all-button.component";
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
export default function AccountingPayablesTableComponent({
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({
bodyshop,
loading,
payments,
}) {
@@ -41,19 +59,12 @@ export default function AccountingPayablesTableComponent({
title: t("payments.fields.date"),
dataIndex: "date",
key: "date",
sorter: (a, b) => alphaSort(a.date, b.date),
sorter: (a, b) => dateSort(a.date, b.date),
sortOrder:
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
render: (text, record) => <DateFormatter>{record.date}</DateFormatter>,
},
{
title: t("payments.fields.date"),
dataIndex: "date",
key: "date",
sorter: (a, b) => alphaSort(a.date, b.date),
sortOrder:
state.sortedInfo.columnKey === "date" && state.sortedInfo.order,
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
@@ -61,7 +72,7 @@ export default function AccountingPayablesTableComponent({
ellipsis: true,
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "ownr_ln" && state.sortedInfo.order,
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.job.owner ? (
<Link to={"/manage/owners/" + record.job.owner.id}>
@@ -115,7 +126,17 @@ export default function AccountingPayablesTableComponent({
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
),
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
@@ -127,6 +148,7 @@ export default function AccountingPayablesTableComponent({
paymentId={record.id}
disabled={transInProgress || !!record.exportedat}
loadingCallback={setTransInProgress}
setSelectedPayments={setSelectedPayments}
/>
),
},
@@ -159,6 +181,9 @@ export default function AccountingPayablesTableComponent({
loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

View File

@@ -8,7 +8,26 @@ import { alphaSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingReceivablesTableComponent);
export function AccountingReceivablesTableComponent({
bodyshop,
loading,
jobs,
}) {
const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -114,17 +133,28 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
);
},
},
{
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
sorter: (a, b) => a.clm_total - b.clm_total,
render: (text, record) => (
<Space wrap>
<JobExportButton
jobId={record.id}
disabled={!!record.date_exported}
setSelectedJobs={setSelectedJobs}
/>
<Link to={`/manage/jobs/${record.id}/close`}>
<Button>{t("jobs.labels.viewallocations")}</Button>
@@ -169,12 +199,17 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
<Card
extra={
<Space wrap>
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
)}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input.Search
value={state.search}
onChange={handleSearch}

View File

@@ -26,6 +26,8 @@ import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-bu
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -33,6 +35,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
@@ -40,7 +44,10 @@ export default connect(
mapDispatchToProps
)(BillDetailEditcontainer);
export function BillDetailEditcontainer({ setPartsOrderContext }) {
export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
}) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
const { t } = useTranslation();
@@ -134,6 +141,12 @@ export function BillDetailEditcontainer({ setPartsOrderContext }) {
});
await Promise.all(updates);
insertAuditTrail({
jobid: bill.jobid,
billid: search.billid,
operation: AuditTrailMapping.billupdated(bill.invoice_number),
});
await refetch();
form.setFieldsValue(transformData(data));
form.resetFields();

View File

@@ -11,13 +11,17 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
const mapStateToProps = createStructuredSelector({
@@ -27,6 +31,8 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
function BillEnterModalContainer({
@@ -34,6 +40,7 @@ function BillEnterModalContainer({
toggleModalVisible,
bodyshop,
currentUser,
insertAuditTrail,
}) {
const [form] = Form.useForm();
const { t } = useTranslation();
@@ -43,7 +50,31 @@ function BillEnterModalContainer({
const [loading, setLoading] = useState(false);
const client = useApolloClient();
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
const handleFinish = async (values) => {
let totals = CalculateBillTotal(values);
if (totals.discrepancy.getAmount() !== 0) {
if (!(await confirmDialog(t("bills.labels.savewithdiscrepancy")))) {
return;
}
}
setLoading(true);
const { upload, location, ...remainingValues } = values;
@@ -81,8 +112,9 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
console.log("adjustmentsToInsert", adjustmentsToInsert);
const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them.
@@ -115,7 +147,12 @@ function BillEnterModalContainer({
message: JSON.stringify(jobUpdate.errors),
}),
});
return;
}
insertAuditTrail({
jobid: values.jobid,
operation: AuditTrailMapping.jobmodifylbradj(),
});
}
if (!!r1.errors) {
@@ -171,9 +208,15 @@ function BillEnterModalContainer({
});
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
});
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ billlines: [] });
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
} else {
toggleModalVisible();
}
@@ -191,23 +234,6 @@ function BillEnterModalContainer({
if (enterAgain) form.submit();
}, [enterAgain, form]);
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
useEffect(() => {
if (billEnterModal.visible) {
form.setFieldsValue(formValues);

View File

@@ -41,6 +41,7 @@ export function BillFormComponent({
loadLines,
billEdit,
disableInvNumber,
job,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -50,6 +51,10 @@ export function BillFormComponent({
setDiscount(opt.discount);
};
useEffect(() => {
if (job) form.validateFields(["is_credit_memo"]);
}, [job, form]);
useEffect(() => {
if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
const vendorId = form.getFieldValue("vendorid");
@@ -89,7 +94,7 @@ export function BillFormComponent({
<JobSearchSelect
disabled={billEdit || disabled}
convertedOnly
// notExported={false}
notExported={false}
onBlur={() => {
if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
@@ -106,6 +111,18 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
!getFieldValue(["isinhouse"]) &&
value === bodyshop.inhousevendorid
) {
return Promise.reject(t("bills.validation.manualinhouse"));
}
return Promise.resolve();
},
}),
]}
>
<VendorSearchSelect
@@ -175,6 +192,22 @@ export function BillFormComponent({
label={t("bills.fields.is_credit_memo")}
name="is_credit_memo"
valuePropName="checked"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) &&
(value === false || !value)
) {
return Promise.reject(t("bills.labels.onlycmforinvoiced"));
}
return Promise.resolve();
},
}),
]}
>
<Switch />
</Form.Item>

View File

@@ -34,6 +34,7 @@ export function BillFormContainer({
}
loadLines={loadLines}
lineData={lineData ? lineData.joblines : []}
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
/>

View File

@@ -72,9 +72,11 @@ export function BillEnterModalLinesComponent({
quantity: opt.part_qty || 1,
actual_price: opt.cost,
cost_center: opt.part_type
? responsibilityCenters.defaults.costs[
? responsibilityCenters.defaults &&
(responsibilityCenters.defaults.costs[
opt.part_type
] || null
] ||
null)
: null,
};
}
@@ -154,18 +156,7 @@ export function BillEnterModalLinesComponent({
setFieldsValue({
billlines: getFieldsValue("billlines").billlines.map(
(item, idx) => {
console.log("Checking", index, idx);
if (idx === index) {
console.log(
"Found and setting.",
!!item.actual_cost
? item.actual_cost
: Math.round(
(parseFloat(e.target.value) * (1 - discount) +
Number.EPSILON) *
100
) / 100
);
return {
...item,
actual_cost: !!item.actual_cost
@@ -500,9 +491,9 @@ const EditableCell = ({
labelCol={{ span: 0 }}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
{additional && additional(record, record.key)}
{additional && additional(record, record.name)}
</Space>
</td>
);
@@ -514,7 +505,7 @@ const EditableCell = ({
name={dataIndex}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
</td>
);

View File

@@ -1,13 +1,8 @@
import Dinero from "dinero.js";
export const CalculateBillTotal = (invoice) => {
const {
total,
billlines,
federal_tax_rate,
local_tax_rate,
state_tax_rate,
} = invoice;
const { total, billlines, federal_tax_rate, local_tax_rate, state_tax_rate } =
invoice;
//TODO Determine why this recalculates so many times.
let subtotal = Dinero({ amount: 0 });
@@ -20,8 +15,7 @@ export const CalculateBillTotal = (invoice) => {
billlines.forEach((i) => {
if (!!i) {
const itemTotal = Dinero({
amount:
Math.round(((i.actual_cost || 0) * 100 + Number.EPSILON) * 100) / 100,
amount: Math.round((i.actual_cost || 0) * 100),
}).multiply(i.quantity || 1);
subtotal = subtotal.add(itemTotal);

View File

@@ -12,7 +12,18 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
disabled={disabled}
ref={ref}
showSearch
optionFilterProp="line_desc"
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
return (
(option.line_desc &&
option.line_desc
.toLowerCase()
.includes(inputValue.toLowerCase())) ||
(option.oem_partno &&
option.oem_partno.toLowerCase().includes(inputValue.toLowerCase()))
);
}}
notFoundContent={"Removed."}
{...restProps}
>
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
@@ -21,14 +32,19 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
{options
? options.map((item) => (
<Option
disabled={item.removed}
key={item.id}
value={item.id}
cost={item.act_price ? item.act_price : 0}
part_type={item.part_type}
line_desc={item.line_desc}
part_qty={item.part_qty}
oem_partno={item.oem_partno}
style={{
...(item.removed ? { textDecoration: "line-through" } : {}),
}}
>
{`${item.line_desc}${
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}`}
</Option>

View File

@@ -47,7 +47,9 @@ export function BillMarkForReexportButton({ bodyshop, authLevel, bill }) {
});
if (!result.errors) {
notification["success"]({ message: t("bills.successes.save") });
notification["success"]({
message: t("bills.successes.reexport"),
});
} else {
notification["error"]({
message: t("bills.errors.saving", {

View File

@@ -1,10 +1,11 @@
import { EyeFilled, SyncOutlined } from "@ant-design/icons";
import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Checkbox, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
@@ -14,6 +15,7 @@ import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
//jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -26,6 +28,7 @@ const mapDispatchToProps = (dispatch) => ({
});
export function BillsListTableComponent({
bodyshop,
job,
billsQuery,
handleOnRowClick,
@@ -47,12 +50,14 @@ export function BillsListTableComponent({
<Space wrap>
{showView && (
<Button onClick={() => handleOnRowClick(record)}>
<EyeFilled />
<EditFilled />
</Button>
)}
<BillDeleteButton bill={record} />
<Button
disabled={record.is_credit_memo}
disabled={
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
onClick={() =>
setPartsOrderContext({
actions: {},

View File

@@ -1,10 +1,11 @@
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";
import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import GlobalSearch from "../global-search/global-search.component";
import "./breadcrumbs.styles.scss";
const mapStateToProps = createStructuredSelector({
@@ -13,24 +14,29 @@ const mapStateToProps = createStructuredSelector({
export function BreadCrumbs({ breadcrumbs }) {
return (
<div className="breadcrumb-container imex-flex-row">
<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>
</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 />
</Col>
</Row>
);
}
export default connect(mapStateToProps, null)(BreadCrumbs);

View File

@@ -1,17 +1,15 @@
import { useSubscription } from "@apollo/client";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { CONVERSATION_LIST_SUBSCRIPTION } from "../../graphql/conversations.queries";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import ChatAffixComponent from "./chat-affix.component";
import { Affix } from "antd";
import "./chat-affix.styles.scss";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
chatVisible: selectChatVisible,
@@ -31,22 +29,20 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
return (
<Affix className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
<div>
{bodyshop && bodyshop.messagingservicesid ? (
<ChatAffixComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
) : null}
</div>
</Affix>
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
{bodyshop && bodyshop.messagingservicesid ? (
<ChatAffixComponent
conversationList={(data && data.conversations) || []}
unreadCount={
(data &&
data.conversations.reduce((acc, val) => {
return (acc = acc + val.messages_aggregate.aggregate.count);
}, 0)) ||
0
}
/>
) : null}
</div>
);
}
export default connect(mapStateToProps, null)(ChatAffixContainer);

View File

@@ -1,6 +1,11 @@
.chat-affix {
position: absolute;
position: fixed;
left: 2vw;
bottom: 2vh;
z-index: 999;
-webkit-box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
-moz-box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
box-shadow: 0px 0px 2px 0px rgba(69, 69, 69, 1);
}
.chat-affix-open {

View File

@@ -0,0 +1,28 @@
import { useMutation } from "@apollo/client";
import { Button } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { TOGGLE_CONVERSATION_ARCHIVE } from "../../graphql/conversations.queries";
export default function ChatArchiveButton({ conversation }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const [updateConversation] = useMutation(TOGGLE_CONVERSATION_ARCHIVE);
const handleToggleArchive = async () => {
setLoading(true);
await updateConversation({
variables: { id: conversation.id, archived: !conversation.archived },
});
setLoading(false);
};
return (
<Button onClick={handleToggleArchive} loading={loading} type="primary">
{conversation.archived
? t("messaging.labels.unarchive")
: t("messaging.labels.archive")}
</Button>
);
}

View File

@@ -8,6 +8,7 @@ import { selectSelectedConversation } from "../../redux/messaging/messaging.sele
import PhoneFormatter from "../../utils/PhoneFormatter";
import "./chat-conversation-list.styles.scss";
import { useTranslation } from "react-i18next";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -60,13 +61,18 @@ export function ChatConversationListComponent({
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
)}

View File

@@ -1,24 +1,23 @@
import { Space } from "antd";
import React from "react";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) {
return (
<div>
<div className="imex-flex-row">
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []
}
/>
<ChatTagRoContainer conversation={conversation || []} />
</div>
<div className="imex-flex-row">
<PhoneNumberFormatter>
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
</div>
</div>
<Space wrap>
<PhoneNumberFormatter>
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []
}
/>
<ChatTagRoContainer conversation={conversation || []} />
<ChatArchiveButton conversation={conversation} />
</Space>
);
}

View File

@@ -1,4 +1,5 @@
import Icon from "@ant-design/icons";
import { Tooltip } from "antd";
import i18n from "i18next";
import moment from "moment";
import React, { useEffect, useRef } from "react";
@@ -9,6 +10,7 @@ import {
CellMeasurerCache,
List,
} from "react-virtualized";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import "./chat-message-list.styles.scss";
export default function ChatMessageListComponent({ messages }) {
@@ -85,17 +87,22 @@ export default function ChatMessageListComponent({ messages }) {
const MessageRender = (message) => {
return (
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div key={idx} style={{ display: "flex", justifyContent: "center" }}>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
<Tooltip title={DateTimeFormatter({ children: message.created_at })}>
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div
key={idx}
style={{ display: "flex", justifyContent: "center" }}
>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
</Tooltip>
);
};

View File

@@ -20,7 +20,6 @@ export function ChatNewConversation({ openChatByPhone }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values :>> ", values);
openChatByPhone({ phone_num: values.phoneNumber });
form.resetFields();
};

View File

@@ -1,5 +1,5 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Col, Row, Typography } from "antd";
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { Col, Row, Tooltip, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -31,6 +31,9 @@ export function ChatPopupComponent({
{t("messaging.labels.messaging")}
</Typography.Title>
<ChatNewConversation />
<Tooltip title={t("messaging.labels.recentonly")}>
<InfoCircleOutlined />
</Tooltip>
</div>
<ShrinkOutlined
onClick={() => toggleChatVisible()}

View File

@@ -1,5 +1,5 @@
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
import { Select, Empty } from "antd";
import { Select, Empty, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -13,27 +13,27 @@ export default function ChatTagRoComponent({
const { t } = useTranslation();
return (
<div>
<Select
showSearch
autoFocus
style={{
width: 300,
}}
placeholder={t("general.labels.search")}
filterOption={false}
onSearch={handleSearch}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
>
{roOptions.map((item, idx) => (
<Select.Option key={item.id || idx}>
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
item.ownr_ln || ""
} ${item.ownr_co_nm || ""}`}
</Select.Option>
))}
</Select>
<Space flex>
<div style={{ width: "15rem" }}>
<Select
showSearch
autoFocus
dropdownMatchSelectWidth
placeholder={t("general.labels.search")}
filterOption={false}
onSearch={handleSearch}
onSelect={handleInsertTag}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
>
{roOptions.map((item, idx) => (
<Select.Option key={item.id || idx}>
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
item.ownr_ln || ""
} ${item.ownr_co_nm || ""}`}
</Select.Option>
))}
</Select>
</div>
{loading ? <LoadingOutlined /> : null}
{loading ? (
@@ -41,6 +41,6 @@ export default function ChatTagRoComponent({
) : (
<CloseCircleOutlined onClick={() => setVisible(false)} />
)}
</div>
</Space>
);
}

View File

@@ -53,6 +53,7 @@ export function ContractConvertToRo({
const billingLines = [];
if (contractLength > 0)
billingLines.push({
manual_line:true,
unq_seq: 1,
line_no: 1,
line_ref: 1,
@@ -70,6 +71,7 @@ export function ContractConvertToRo({
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
if (mileageDiff > 0) {
billingLines.push({
manual_line:true,
unq_seq: 2,
line_no: 2,
line_ref: 2,
@@ -86,6 +88,7 @@ export function ContractConvertToRo({
if (values.refuelqty > 0) {
billingLines.push({
manual_line:true,
unq_seq: 3,
line_no: 3,
line_ref: 3,
@@ -101,6 +104,7 @@ export function ContractConvertToRo({
}
if (values.applyCleanupCharge) {
billingLines.push({
manual_line:true,
unq_seq: 4,
line_no: 4,
line_ref: 4,
@@ -117,6 +121,7 @@ export function ContractConvertToRo({
if (contract.damagewaiver) {
//Add for cleanup fee.
billingLines.push({
manual_line:true,
unq_seq: 5,
line_no: 5,
line_ref: 5,

View File

@@ -42,7 +42,6 @@ export default function ContractFormComponent({
<ContractStatusSelector />
</Form.Item>
)}
<Form.Item
label={t("contracts.fields.start")}
name="start"

View File

@@ -17,7 +17,7 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
return (
<div>
<Form.Item name="fleet" label={t("courtesycars.fields.fleetnumber")}>
<Form.Item name="plate" label={t("courtesycars.fields.plate")}>
<Input />
</Form.Item>
<Form.Item

View File

@@ -44,8 +44,8 @@ export function ContractsFindModalContainer({
callSearch({
variables: {
fleet:
(values.fleet && values.fleet !== "" && values.fleet) || undefined,
plate:
(values.plate && values.plate !== "" && values.plate) || undefined,
time: values.time,
},
});

View File

@@ -83,8 +83,10 @@ export function ContractsList({
render: (text, record) => (
<Link to={`/manage/courtesycars/${record.courtesycar.id}`}>{`${
record.courtesycar.year
} ${record.courtesycar.make} ${record.courtesycar.model} ${
record.courtesycar.plate ? `(${record.courtesycar.plate})` : ""
} ${record.courtesycar.make} ${record.courtesycar.model}${
record.courtesycar.plate ? ` (${record.courtesycar.plate})` : ""
}${
record.courtesycar.fleetnumber ? ` (${record.courtesycar.fleetnumber})` : ""
}`}</Link>
),
},

View File

@@ -31,6 +31,9 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
<Option value="courtesycars.status.out">
{t("courtesycars.status.out")}
</Option>
<Option value="courtesycars.status.sold">
{t("courtesycars.status.sold")}
</Option>
</Select>
);
};

View File

@@ -45,6 +45,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
text: t("courtesycars.status.out"),
value: "courtesycars.status.out",
},
{
text: t("courtesycars.status.sold"),
value: "courtesycars.status.sold",
},
],
onFilter: (value, record) => value.includes(record.status),
sortOrder:
@@ -90,13 +94,12 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
// sorter: (a, b) => alphaSort(a.model, b.model),
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
render: (text, record) => (
<div>
{record.cccontracts.length === 1
? record.cccontracts[0].job.ro_number
: null}
</div>
),
render: (text, record) =>
record.cccontracts.length === 1 ? (
<Link to={`/manage/jobs/${record.cccontracts[0].job.id}`}>
{record.cccontracts[0].job.ro_number}
</Link>
) : null,
},
];

View File

@@ -0,0 +1,166 @@
import { Card } from "antd";
import _ from "lodash";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import {
Bar,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyEmployeeEfficiency({
data,
...cardProps
}) {
const { t } = useTranslation();
if (!data) return null;
if (!data.monthly_employee_efficiency)
return <DashboardRefreshRequired {...cardProps} />;
const ticketsByDate = _.groupBy(data.monthly_employee_efficiency, (item) =>
moment(item.date).format("YYYY-MM-DD")
);
const listOfDays = Utils.ListOfDaysInCurrentMonth();
const chartData = listOfDays.reduce((acc, val) => {
//Sum up the current day.
let dailyHrs;
if (!!ticketsByDate[val]) {
dailyHrs = ticketsByDate[val].reduce(
(dayAcc, dayVal) => {
return {
actual: dayAcc.actual + dayVal.actualhrs,
productive: dayAcc.actual + dayVal.productivehrs,
};
},
{ actual: 0, productive: 0 }
);
} else {
dailyHrs = { actual: 0, productive: 0 };
}
const dailyEfficiency =
((dailyHrs.productive - dailyHrs.actual) / dailyHrs.productive + 1) * 100;
const theValue = {
date: moment(val).format("DD"),
...dailyHrs,
dailyEfficiency: isNaN(dailyEfficiency) ? 0 : dailyEfficiency.toFixed(1),
accActual:
acc.length > 0
? acc[acc.length - 1].accActual + dailyHrs.actual
: dailyHrs.actual,
accProductive:
acc.length > 0
? acc[acc.length - 1].accProductive + dailyHrs.productive
: dailyHrs.productive,
accEfficiency: 0,
};
theValue.accEfficiency = (
((theValue.accProductive - theValue.accActual) /
(theValue.accProductive || 1) +
1) *
100
).toFixed(1);
return [...acc, theValue];
}, []);
return (
<Card
title={t("dashboard.titles.monthlyemployeeefficiency")}
{...cardProps}
>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis
yAxisId="left"
orientation="left"
stroke="#8884d8"
unit=" hrs"
/>
<YAxis
yAxisId="right"
orientation="right"
stroke="#82ca9d"
unit="%"
/>
<Tooltip />
<Legend />
<Line
yAxisId="right"
name="Accumulated Efficiency"
type="monotone"
unit="%"
dataKey="accEfficiency"
stroke="#152228"
connectNulls
// activeDot={{ r: 8 }}
/>
<Line
name="Daily Efficiency"
yAxisId="right"
unit="%"
type="monotone"
connectNulls
dataKey="dailyEfficiency"
stroke="#d31717"
/>
<Bar
name="Actual Hours"
dataKey="actual"
yAxisId="left"
unit=" hrs"
//stackId="day"
barSize={20}
fill="#102568"
/>
<Bar
name="Productive Hours"
dataKey="productive"
yAxisId="left"
unit=" hrs"
//stackId="day"
barSize={20}
fill="#017664"
/>
</ComposedChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyEmployeeEfficiencyGql = `
monthly_employee_efficiency: timetickets(where: {_and: [{date: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}},{date: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}} ]}) {
actualhrs
productivehrs
employeeid
employee {
first_name
last_name
}
date
}
`;

View File

@@ -0,0 +1,163 @@
import { Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../../utils/sorters";
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
import Dinero from "dinero.js";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [costingData, setcostingData] = useState(null);
const [searchText, setSearchText] = useState("");
const [state, setState] = useState({
sortedInfo: {},
});
useEffect(() => {
async function getCostingData() {
if (data && data.monthly_sales) {
setLoading(true);
const response = await axios.post("/job/costingmulti", {
jobids: data.monthly_sales.map((x) => x.id),
});
setcostingData(response.data);
setLoading(false);
}
}
getCostingData();
}, [data]);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const columns = [
{
title: t("bodyshop.fields.responsibilitycenter"),
dataIndex: "cost_center",
key: "cost_center",
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
},
{
title: t("jobs.labels.sales"),
dataIndex: "sales",
key: "sales",
sorter: (a, b) =>
parseFloat(a.sales.substring(1)) - parseFloat(b.sales.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "sales" && state.sortedInfo.order,
},
{
title: t("jobs.labels.costs"),
dataIndex: "costs",
key: "costs",
sorter: (a, b) =>
parseFloat(a.costs.substring(1)) - parseFloat(b.costs.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "costs" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gpdollars"),
dataIndex: "gpdollars",
key: "gpdollars",
sorter: (a, b) =>
parseFloat(a.gpdollars.substring(1)) -
parseFloat(b.gpdollars.substring(1)),
sortOrder:
state.sortedInfo.columnKey === "gpdollars" && state.sortedInfo.order,
},
{
title: t("jobs.labels.gppercent"),
dataIndex: "gppercent",
key: "gppercent",
sorter: (a, b) =>
parseFloat(a.gppercent.slice(0, -1) || 0) -
parseFloat(b.gppercent.slice(0, -1) || 0),
sortOrder:
state.sortedInfo.columnKey === "gppercent" && state.sortedInfo.order,
},
];
const filteredData =
searchText === ""
? (costingData && costingData.allCostCenterData) || []
: costingData.allCostCenterData.filter((d) =>
(d.cost_center || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase())
);
return (
<Card
title={t("dashboard.titles.monthlyjobcosting")}
extra={
<Space wrap>
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
}
{...cardProps}
>
<LoadingSkeleton loading={loading}>
<div style={{ height: "100%" }}>
<Table
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
scroll={{ x: true, y: "calc(100% - 4em)" }}
rowKey="id"
style={{ height: "100%" }}
dataSource={filteredData}
summary={() => (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.totalSales
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.totalCost
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{Dinero(
costingData &&
costingData.allSummaryData &&
costingData.allSummaryData.gpdollars
).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
)}
/>
</div>
</LoadingSkeleton>
</Card>
);
}

View File

@@ -0,0 +1,163 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const laborData = {};
data.monthly_sales.forEach((job) => {
job.joblines.forEach((jobline) => {
if (!jobline.mod_lbr_ty) return;
if (!laborData[jobline.mod_lbr_ty])
laborData[jobline.mod_lbr_ty] = Dinero();
laborData[jobline.mod_lbr_ty] = laborData[jobline.mod_lbr_ty].add(
Dinero({
amount: Math.round(
(job[`rate_${jobline.mod_lbr_ty.toLowerCase()}`] || 0) * 100
),
}).multiply(jobline.mod_lb_hrs || 0)
);
});
});
const chartData = Object.keys(laborData).map((key) => {
return {
name: t(`joblines.fields.lbr_types.${key.toUpperCase()}`),
value: laborData[key].getAmount() / 100,
color: pieColor(key.toUpperCase()),
};
});
return (
<Card title={t("dashboard.titles.monthlylaborsales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;
const pieColor = (type) => {
if (type === "LAA") return "lightgreen";
else if (type === "LAB") return "dodgerblue";
else if (type === "LAD") return "aliceblue";
else if (type === "LAE") return "seafoam";
else if (type === "LAG") return "chartreuse";
else if (type === "LAF") return "magenta";
else if (type === "LAM") return "gold";
else if (type === "LAR") return "crimson";
else if (type === "LAU") return "slategray";
else if (type === "LA1") return "slategray";
else if (type === "LA2") return "slategray";
else if (type === "LA3") return "slategray";
else if (type === "LA4") return "slategray";
return "slategray";
};
const renderActiveShape = (props) => {
//const RADIAN = Math.PI / 180;
const {
cx,
cy,
//midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
fill,
payload,
// percent,
value,
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
// // const sx = cx + (outerRadius + 10) * cos;
// const sy = cy + (outerRadius + 10) * sin;
// const mx = cx + (outerRadius + 30) * cos;
// const my = cy + (outerRadius + 30) * sin;
// //const ex = mx + (cos >= 0 ? 1 : -1) * 22;
// const ey = my;
//const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
</g>
);
};
// <path
// d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
// stroke={fill}
// fill="none"
// />;
// <text
// x={ex + (cos >= 0 ? 1 : -1) * 12}
// y={ey}
// textAnchor={textAnchor}
// fill="#333"
// >
// {payload.name}
// </text>
// <text
// x={ex + (cos >= 0 ? 1 : -1) * 12}
// y={ey}
// dy={18}
// textAnchor={textAnchor}
// fill="#999"
// >
// {Dinero({ amount: Math.round(value * 100) }).toFormat()}
// </text>

View File

@@ -0,0 +1,136 @@
import { Card } from "antd";
import Dinero from "dinero.js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
const { t } = useTranslation();
const [activeIndex, setActiveIndex] = useState(0);
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const partData = {};
data.monthly_sales.forEach((job) => {
job.joblines.forEach((jobline) => {
if (!jobline.part_type) return;
if (!partData[jobline.part_type]) partData[jobline.part_type] = Dinero();
partData[jobline.part_type] = partData[jobline.part_type].add(
Dinero({ amount: Math.round((jobline.act_price || 0) * 100) }).multiply(
jobline.part_qty || 0
)
);
});
});
const chartData = Object.keys(partData).map((key) => {
return {
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
value: partData[key].getAmount() / 100,
color: pieColor(key.toUpperCase()),
};
});
return (
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<PieChart margin={0} padding={0}>
<Pie
data={chartData}
activeIndex={activeIndex}
activeShape={renderActiveShape}
cx="50%"
cy="50%"
innerRadius="60%"
// outerRadius={80}
fill="#8884d8"
dataKey="value"
onMouseEnter={(throwaway, index) => setActiveIndex(index)}
>
{chartData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;
const pieColor = (type) => {
if (type === "PAA") return "darkgreen";
else if (type === "PAC") return "green";
else if (type === "PAE") return "gold";
else if (type === "PAG") return "seafoam";
else if (type === "PAL") return "chartreuse";
else if (type === "PAM") return "magenta";
else if (type === "PAN") return "crimson";
else if (type === "PAO") return "gold";
else if (type === "PAP") return "crimson";
else if (type === "PAR") return "indigo";
else if (type === "PAS") return "dodgerblue";
else if (type === "PASL") return "dodgerblue";
return "slategray";
};
const renderActiveShape = (props) => {
// const RADIAN = Math.PI / 180;
const {
cx,
cy,
// midAngle,
innerRadius,
outerRadius,
startAngle,
endAngle,
fill,
payload,
// percent,
value,
} = props;
// const sin = Math.sin(-RADIAN * midAngle);
// const cos = Math.cos(-RADIAN * midAngle);
// const sx = cx + (outerRadius + 10) * cos;
//const sy = cy + (outerRadius + 10) * sin;
// const mx = cx + (outerRadius + 30) * cos;
//const my = cy + (outerRadius + 30) * sin;
// const ex = mx + (cos >= 0 ? 1 : -1) * 22;
// const ey = my;
// const textAnchor = cos >= 0 ? "start" : "end";
return (
<g>
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
{payload.name}
</text>
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
</g>
);
};

View File

@@ -2,29 +2,30 @@ import { Card } from "antd";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import _ from "lodash";
import {
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis
Area,
Bar,
CartesianGrid,
ComposedChart,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import Dinero from "dinero.js";
import * as Utils from "../../scoreboard-targets-table/scoreboard-targets-table.util";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
const { t } = useTranslation();
if (!data) return null;
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
const jobsByDate = {
"2020-07-5": [{ clm_total: 1224 }],
"2020-07-8": [{ clm_total: 987 }, { clm_total: 8755 }],
"2020-07-12": [{ clm_total: 684 }, { clm_total: 12022 }],
"2020-07-21": [{ clm_total: 15000 }],
"2020-07-28": [{ clm_total: 122 }, { clm_total: 4522 }],
};
const jobsByDate = _.groupBy(data.monthly_sales, (item) =>
moment(item.date_invoiced).format("YYYY-MM-DD")
);
const listOfDays = Utils.ListOfDaysInCurrentMonth();
@@ -33,17 +34,21 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
let dailySales;
if (!!jobsByDate[val]) {
dailySales = jobsByDate[val].reduce((dayAcc, dayVal) => {
return dayAcc + dayVal.clm_total;
}, 0);
return dayAcc.add(
Dinero((dayVal.job_totals && dayVal.job_totals.totals.subtotal) || 0)
);
}, Dinero());
} else {
dailySales = 0;
dailySales = Dinero();
}
const theValue = {
date: moment(val).format("D dd"),
dailySales,
date: moment(val).format("DD"),
dailySales: dailySales.getAmount() / 100,
accSales:
acc.length > 0 ? acc[acc.length - 1].accSales + dailySales : dailySales,
acc.length > 0
? acc[acc.length - 1].accSales + dailySales.getAmount() / 100
: dailySales.getAmount() / 100,
};
return [...acc, theValue];
@@ -51,32 +56,40 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
return (
<Card title={t("dashboard.titles.monthlyrevenuegraph")} {...cardProps}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Area
type="monotone"
name="Accumulated Sales"
dataKey="accSales"
fill="#8884d8"
stroke="#8884d8"
/>
<Bar
name="Daily Sales"
dataKey="dailySales"
//stackId="day"
barSize={20}
fill="#413ea0"
/>
</ComposedChart>
</ResponsiveContainer>
<div style={{ height: "100%" }}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
data={chartData}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip
formatter={(value, name, props) => value && value.toFixed(2)}
/>
<Legend />
<Area
type="monotone"
name="Accumulated Sales"
dataKey="accSales"
fill="#3CB371"
stroke="#3CB371"
/>
<Bar
name="Daily Sales"
dataKey="dailySales"
//stackId="day"
barSize={20}
fill="#413ea0"
/>
</ComposedChart>
</ResponsiveContainer>
</div>
</Card>
);
}
export const DashboardMonthlyRevenueGraphGql = `
`;

View File

@@ -1,30 +1,47 @@
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import { Card, Statistic } from "antd";
import Dinero from "dinero.js";
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardProjectedMonthlySales({ data, ...cardProps }) {
const { t } = useTranslation();
const aboveTargetMonthlySales = false;
if (!data) return null;
if (!data.projected_monthly_sales)
return <DashboardRefreshRequired {...cardProps} />;
const dollars =
data.projected_monthly_sales &&
data.projected_monthly_sales.reduce(
(acc, val) =>
acc.add(
Dinero(
val.job_totals &&
val.job_totals.totals &&
val.job_totals.totals.subtotal
)
),
Dinero()
);
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.projectedmonthlysales")}
value={222000.0}
precision={2}
prefix={
<div>
{aboveTargetMonthlySales ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetMonthlySales ? "green" : "red" }}
/>
<Card title={t("dashboard.titles.projectedmonthlysales")} {...cardProps}>
<Statistic value={dollars.toFormat()} />
</Card>
);
}
export const DashboardProjectedMonthlySalesGql = `
projected_monthly_sales: jobs(where: {_or: [{_and: [{date_invoiced: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}, {_and: [{scheduled_completion: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {scheduled_completion: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}]}) {
id
date_invoiced
job_totals
}
`;

View File

@@ -0,0 +1,25 @@
import { SyncOutlined } from "@ant-design/icons";
import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function DashboardRefreshRequired(props) {
const { t } = useTranslation();
return (
<Card {...props}>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
textOverflow: "ellipsis",
}}
>
<SyncOutlined style={{ fontSize: "300%", margin: "1rem" }} />
<div>{t("dashboard.errors.refreshrequired")}</div>
</div>
</Card>
);
}

View File

@@ -1,33 +1,27 @@
import React from "react";
import { Card, Statistic } from "antd";
import Dinero from "dinero.js";
import React from "react";
import { useTranslation } from "react-i18next";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardTotalProductionDollars({
data,
...cardProps
}) {
const { t } = useTranslation();
const aboveTargetProductionDollars = false;
if (!data) return null;
if (!data.production_jobs) return <DashboardRefreshRequired {...cardProps} />;
const dollars =
data.production_jobs &&
data.production_jobs.reduce(
(acc, val) =>
acc.add(Dinero(val.job_totals && val.job_totals.totals.subtotal)),
Dinero()
);
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productiondollars")}
value={175000.0}
precision={2}
prefix={
<div>
{aboveTargetProductionDollars ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
$
</div>
}
valueStyle={{ color: aboveTargetProductionDollars ? "green" : "red" }}
/>
<Card title={t("dashboard.labels.dollarsinproduction")} {...cardProps}>
<Statistic value={dollars.toFormat()} />
</Card>
);
}

View File

@@ -1,19 +1,63 @@
import { Card, Space, Statistic } from "antd";
import React from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import { ArrowDownOutlined, ArrowUpOutlined } from "@ant-design/icons";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../../redux/user/user.selectors";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardTotalProductionHours({ data, ...cardProps }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashboardTotalProductionHours);
export function DashboardTotalProductionHours({
bodyshop,
data,
...cardProps
}) {
const { t } = useTranslation();
const aboveTargetHours = true;
if (!data) return null;
if (!data.production_jobs) return <DashboardRefreshRequired {...cardProps} />;
const hours =
data.production_jobs &&
data.production_jobs.reduce(
(acc, val) => {
return {
body: acc.body + val.labhrs.aggregate.sum.mod_lb_hrs,
ref: acc.ref + val.larhrs.aggregate.sum.mod_lb_hrs,
total:
acc.total +
val.labhrs.aggregate.sum.mod_lb_hrs +
val.larhrs.aggregate.sum.mod_lb_hrs,
};
},
{ body: 0, ref: 0, total: 0 }
);
const aboveTargetHours = hours.total >= bodyshop.prodtargethrs;
return (
<Card {...cardProps}>
<Statistic
title={t("dashboard.titles.productionhours")}
value={750}
prefix={aboveTargetHours ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
/>
<Card {...cardProps} title={t("dashboard.titles.prodhrssummary")}>
<Space wrap style={{ flex: 1 }}>
<Statistic
title={t("dashboard.labels.bodyhrs")}
value={hours.body.toFixed(1)}
/>
<Statistic
title={t("dashboard.labels.refhrs")}
value={hours.ref.toFixed(1)}
/>
<Statistic
title={t("dashboard.labels.prodhrs")}
value={hours.total.toFixed(1)}
valueStyle={{ color: aboveTargetHours ? "green" : "red" }}
/>
</Space>
</Card>
);
}
export const DashboardTotalProductionHoursGql = ``;

View File

@@ -1,185 +1,352 @@
// import Icon from "@ant-design/icons";
// import { Button, Dropdown, Menu, notification } from "antd";
// import React, { useState } from "react";
// import { useMutation, useQuery } from "@apollo/client";
// import { Responsive, WidthProvider } from "react-grid-layout";
// import { useTranslation } from "react-i18next";
// import { MdClose } from "react-icons/md";
// import { connect } from "react-redux";
// import { createStructuredSelector } from "reselect";
// import { logImEXEvent } from "../../firebase/firebase.utils";
// import { QUERY_DASHBOARD_DETAILS } from "../../graphql/bodyshop.queries";
// import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
// import {
// selectBodyshop,
// selectCurrentUser,
// } from "../../redux/user/user.selectors";
// import AlertComponent from "../alert/alert.component";
// import DashboardMonthlyRevenueGraph from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
// import DashboardProjectedMonthlySales from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
// import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
// import DashboardTotalProductionHours from "../dashboard-components/total-production-hours/total-production-hours.component";
// import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
// //Combination of the following:
// // /node_modules/react-grid-layout/css/styles.css
// // /node_modules/react-resizable/css/styles.css
// import "./dashboard-grid.styles.css";
// import "./dashboard-grid.styles.scss";
import Icon, { SyncOutlined } from "@ant-design/icons";
import { gql, useMutation, useQuery } from "@apollo/client";
import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
import i18next from "i18next";
import _ from "lodash";
import moment from "moment";
import React, { useState } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import { useTranslation } from "react-i18next";
import { MdClose } from "react-icons/md";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_DASHBOARD_LAYOUT } from "../../graphql/user.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import DashboardMonthlyEmployeeEfficiency, {
DashboardMonthlyEmployeeEfficiencyGql,
} from "../dashboard-components/monthly-employee-efficiency/monthly-employee-efficiency.component";
import DashboardMonthlyJobCosting from "../dashboard-components/monthly-job-costing/monthly-job-costing.component";
import DashboardMonthlyLaborSales from "../dashboard-components/monthly-labor-sales/monthly-labor-sales.component";
import DashboardMonthlyPartsSales from "../dashboard-components/monthly-parts-sales/monthly-parts-sales.component";
import DashboardMonthlyRevenueGraph, {
DashboardMonthlyRevenueGraphGql,
} from "../dashboard-components/monthly-revenue-graph/monthly-revenue-graph.component";
import DashboardProjectedMonthlySales, {
DashboardProjectedMonthlySalesGql,
} from "../dashboard-components/pojected-monthly-sales/projected-monthly-sales.component";
import DashboardTotalProductionDollars from "../dashboard-components/total-production-dollars/total-production-dollars.component";
import DashboardTotalProductionHours, {
DashboardTotalProductionHoursGql,
} from "../dashboard-components/total-production-hours/total-production-hours.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
//Combination of the following:
// /node_modules/react-grid-layout/css/styles.css
// /node_modules/react-resizable/css/styles.css
import "./dashboard-grid.styles.scss";
import { GenerateDashboardData } from "./dashboard-grid.utils";
// const ResponsiveReactGridLayout = WidthProvider(Responsive);
const ResponsiveReactGridLayout = WidthProvider(Responsive);
// const mapStateToProps = createStructuredSelector({
// currentUser: selectCurrentUser,
// bodyshop: selectBodyshop,
// });
// const mapDispatchToProps = (dispatch) => ({
// //setUserLanguage: language => dispatch(setUserLanguage(language))
// });
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
// export function DashboardGridComponent({ currentUser, bodyshop }) {
// const { loading, error, data } = useQuery(QUERY_DASHBOARD_DETAILS);
// const { t } = useTranslation();
// const [state, setState] = useState({
// layout: bodyshop.associations[0].user.dashboardlayout || [
// { i: "ProductionDollars", x: 0, y: 0, w: 2, h: 2 },
// // { i: "ProductionHours", x: 2, y: 0, w: 2, h: 2 },
// ],
// });
// const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
export function DashboardGridComponent({ currentUser, bodyshop }) {
const { t } = useTranslation();
const [state, setState] = useState({
...(bodyshop.associations[0].user.dashboardlayout
? bodyshop.associations[0].user.dashboardlayout
: { items: [], layout: {}, layouts: [] }),
});
// const handleLayoutChange = async (newLayout) => {
// logImEXEvent("dashboard_change_layout");
// setState({ ...state, layout: newLayout });
// const result = await updateLayout({
// variables: { email: currentUser.email, layout: newLayout },
// });
const { loading, error, data, refetch } = useQuery(
createDashboardQuery(state)
);
// if (!!result.errors) {
// notification["error"]({
// message: t("dashboard.errors.updatinglayout", {
// message: JSON.stringify(result.errors),
// }),
// });
// }
// };
const [updateLayout] = useMutation(UPDATE_DASHBOARD_LAYOUT);
// const handleRemoveComponent = (key) => {
// logImEXEvent("dashboard_remove_component", { name: key });
const handleLayoutChange = async (layout, layouts) => {
logImEXEvent("dashboard_change_layout");
// const idxToRemove = state.layout.findIndex((i) => i.i === key);
// const newLayout = state.layout;
// newLayout.splice(idxToRemove, 1);
// handleLayoutChange(newLayout);
// };
setState({ ...state, layout, layouts });
// const handleAddComponent = (e) => {
// logImEXEvent("dashboard_add_component", { name: e });
const result = await updateLayout({
variables: {
email: currentUser.email,
layout: { ...state, layout, layouts },
},
});
if (!!result.errors) {
notification["error"]({
message: t("dashboard.errors.updatinglayout", {
message: JSON.stringify(result.errors),
}),
});
}
};
const handleRemoveComponent = (key) => {
logImEXEvent("dashboard_remove_component", { name: key });
const idxToRemove = state.items.findIndex((i) => i.i === key);
// handleLayoutChange([
// ...state.layout,
// {
// i: e.key,
// x: (state.layout.length * 2) % (state.cols || 12),
// y: Infinity, // puts it at the bottom
// w: componentList[e.key].w || 2,
// h: componentList[e.key].h || 2,
// },
// ]);
// };
const items = _.cloneDeep(state.items);
// const onBreakpointChange = (breakpoint, cols) => {
// setState({ ...state, breakpoint: breakpoint, cols: cols });
// };
items.splice(idxToRemove, 1);
setState({ ...state, items });
};
// const existingLayoutKeys = state.layout.map((i) => i.i);
// const addComponentOverlay = (
// <Menu onClick={handleAddComponent}>
// {Object.keys(componentList).map((key) => (
// <Menu.Item
// key={key}
// value={key}
// disabled={existingLayoutKeys.includes(key)}
// >
// {componentList[key].label}
// </Menu.Item>
// ))}
// </Menu>
// );
const handleAddComponent = (e) => {
logImEXEvent("dashboard_add_component", { name: e });
setState({
...state,
items: [
...state.items,
{
i: e.key,
x: (state.items.length * 2) % (state.cols || 12),
y: 99, // puts it at the bottom
w: componentList[e.key].w || 2,
h: componentList[e.key].h || 2,
},
],
});
};
// if (error) return <AlertComponent message={error.message} type="error" />;
const dashboarddata = React.useMemo(
() => GenerateDashboardData(data),
[data]
);
const existingLayoutKeys = state.items.map((i) => i.i);
const addComponentOverlay = (
<Menu onClick={handleAddComponent}>
{Object.keys(componentList).map((key) => (
<Menu.Item
key={key}
value={key}
disabled={existingLayoutKeys.includes(key)}
>
{componentList[key].label}
</Menu.Item>
))}
</Menu>
);
// return (
// <div>
// <Dropdown overlay={addComponentOverlay} trigger={["click"]}>
// <Button>{t("dashboard.actions.addcomponent")}</Button>
// </Dropdown>
// <ResponsiveReactGridLayout
// className="layout"
// breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
// cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
// width="100%"
// onLayoutChange={handleLayoutChange}
// onBreakpointChange={onBreakpointChange}
// >
// {state.layout.map((item, index) => {
// const TheComponent = componentList[item.i].component;
// return (
// <div key={item.i} data-grid={item}>
// <LoadingSkeleton loading={loading}>
// <Icon
// component={MdClose}
// key={item.i}
// style={{
// position: "absolute",
// zIndex: "2",
// right: ".25rem",
// top: ".25rem",
// cursor: "pointer",
// }}
// onClick={() => handleRemoveComponent(item.i)}
// />
// <TheComponent
// className="dashboard-card"
// size="small"
// style={{ height: "100%", width: "100%" }}
// />
// </LoadingSkeleton>
// </div>
// );
// })}
// </ResponsiveReactGridLayout>
// </div>
// );
// }
if (error) return <AlertComponent message={error.message} type="error" />;
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(DashboardGridComponent);
return (
<div>
<PageHeader
extra={
<Space>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Dropdown overlay={addComponentOverlay} trigger={["click"]}>
<Button>{t("dashboard.actions.addcomponent")}</Button>
</Dropdown>
</Space>
}
/>
// const componentList = {
// ProductionDollars: {
// label: "Production Dollars",
// component: DashboardTotalProductionDollars,
// w: 2,
// h: 1,
// },
// ProductionHours: {
// label: "Production Hours",
// component: DashboardTotalProductionHours,
// w: 2,
// h: 1,
// },
// ProjectedMonthlySales: {
// label: "Projected Monthly Sales",
// component: DashboardProjectedMonthlySales,
// w: 2,
// h: 1,
// },
// MonthlyRevenueGraph: {
// label: "Monthly Sales Graph",
// component: DashboardMonthlyRevenueGraph,
// w: 2,
// h: 2,
// },
// };
<ResponsiveReactGridLayout
className="layout"
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
width="100%"
layouts={state.layouts}
onLayoutChange={handleLayoutChange}
// onBreakpointChange={onBreakpointChange}
>
{state.items.map((item, index) => {
const TheComponent = componentList[item.i].component;
return (
<div
key={item.i}
data-grid={{
...item,
minH: componentList[item.i].minH || 1,
minW: componentList[item.i].minW || 1,
}}
>
<LoadingSkeleton loading={loading}>
<Icon
component={MdClose}
key={item.i}
style={{
position: "absolute",
zIndex: "2",
right: ".25rem",
top: ".25rem",
cursor: "pointer",
}}
onClick={() => handleRemoveComponent(item.i)}
/>
<TheComponent className="dashboard-card" data={dashboarddata} />
</LoadingSkeleton>
</div>
);
})}
</ResponsiveReactGridLayout>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DashboardGridComponent);
const componentList = {
ProductionDollars: {
label: i18next.t("dashboard.titles.productiondollars"),
component: DashboardTotalProductionDollars,
gqlFragment: null,
w: 1,
h: 1,
minW: 2,
minH: 1,
},
ProductionHours: {
label: i18next.t("dashboard.titles.productionhours"),
component: DashboardTotalProductionHours,
gqlFragment: DashboardTotalProductionHoursGql,
w: 3,
h: 1,
minW: 3,
minH: 1,
},
ProjectedMonthlySales: {
label: i18next.t("dashboard.titles.projectedmonthlysales"),
component: DashboardProjectedMonthlySales,
gqlFragment: DashboardProjectedMonthlySalesGql,
w: 2,
h: 1,
minW: 2,
minH: 1,
},
MonthlyRevenueGraph: {
label: i18next.t("dashboard.titles.monthlyrevenuegraph"),
component: DashboardMonthlyRevenueGraph,
gqlFragment: DashboardMonthlyRevenueGraphGql,
w: 4,
h: 2,
minW: 4,
minH: 2,
},
MonthlyJobCosting: {
label: i18next.t("dashboard.titles.monthlyjobcosting"),
component: DashboardMonthlyJobCosting,
gqlFragment: null,
minW: 6,
minH: 3,
w: 6,
h: 3,
},
MonthlyPartsSales: {
label: i18next.t("dashboard.titles.monthlypartssales"),
component: DashboardMonthlyPartsSales,
gqlFragment: null,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
MonthlyLaborSales: {
label: i18next.t("dashboard.titles.monthlylaborsales"),
component: DashboardMonthlyLaborSales,
gqlFragment: null,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
MonthlyEmployeeEfficency: {
label: i18next.t("dashboard.titles.monthlyemployeeefficiency"),
component: DashboardMonthlyEmployeeEfficiency,
gqlFragment: DashboardMonthlyEmployeeEfficiencyGql,
minW: 2,
minH: 2,
w: 2,
h: 2,
},
};
const createDashboardQuery = (state) => {
const componentBasedAdditions =
state &&
Array.isArray(state.layout) &&
state.layout
.map((item, index) => componentList[item.i].gqlFragment || "")
.join("");
return gql`
query QUERY_DASHBOARD_DETAILS {
${componentBasedAdditions || ""}
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
.startOf("month")
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.format("YYYY-MM-DD")}"}}]}) {
id
date_invoiced
job_totals
rate_la1
rate_la2
rate_la3
rate_la4
rate_laa
rate_lab
rate_lad
rate_lae
rate_laf
rate_lag
rate_lam
rate_lar
rate_las
rate_lau
rate_ma2s
rate_ma2t
rate_ma3s
rate_mabl
rate_macs
rate_mahw
rate_mapa
rate_mash
rate_matd
joblines(where: { removed: { _eq: false } }) {
id
mod_lbr_ty
mod_lb_hrs
act_price
part_qty
part_type
}
}
production_jobs: jobs(where: { inproduction: { _eq: true } }) {
id
ro_number
ins_co_nm
job_totals
joblines(where: { removed: { _eq: false } }) {
id
mod_lbr_ty
mod_lb_hrs
act_price
part_qty
part_type
}
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(where: { mod_lbr_ty: { _eq: "LAR" } }) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
}
`;
};

View File

@@ -1,128 +0,0 @@
.react-resizable {
position: relative;
}
.react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+");
background-position: bottom right;
padding: 0 3px 3px 0;
}
.react-resizable-handle-sw {
bottom: 0;
left: 0;
cursor: sw-resize;
transform: rotate(90deg);
}
.react-resizable-handle-se {
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-resizable-handle-nw {
top: 0;
left: 0;
cursor: nw-resize;
transform: rotate(180deg);
}
.react-resizable-handle-ne {
top: 0;
right: 0;
cursor: ne-resize;
transform: rotate(270deg);
}
.react-resizable-handle-w,
.react-resizable-handle-e {
top: 50%;
margin-top: -10px;
cursor: ew-resize;
}
.react-resizable-handle-w {
left: 0;
transform: rotate(135deg);
}
.react-resizable-handle-e {
right: 0;
transform: rotate(315deg);
}
.react-resizable-handle-n,
.react-resizable-handle-s {
left: 50%;
margin-left: -10px;
cursor: ns-resize;
}
.react-resizable-handle-n {
top: 0;
transform: rotate(225deg);
}
.react-resizable-handle-s {
bottom: 0;
transform: rotate(45deg);
}
.react-grid-layout {
position: relative;
transition: height 200ms ease;
}
.react-grid-item {
transition: all 200ms ease;
transition-property: left, top;
}
.react-grid-item.cssTransforms {
transition-property: transform;
}
.react-grid-item.resizing {
z-index: 1;
will-change: width, height;
}
.react-grid-item.react-draggable-dragging {
transition: none;
z-index: 3;
will-change: transform;
}
.react-grid-item.dropping {
visibility: hidden;
}
.react-grid-item.react-grid-placeholder {
background: red;
opacity: 0.2;
transition-duration: 100ms;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.react-grid-item > .react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-grid-item > .react-resizable-handle::after {
content: "";
position: absolute;
right: 3px;
bottom: 3px;
width: 5px;
height: 5px;
border-right: 2px solid rgba(0, 0, 0, 0.4);
border-bottom: 2px solid rgba(0, 0, 0, 0.4);
}
.react-resizable-hide > .react-resizable-handle {
display: none;
}

View File

@@ -1,12 +1,154 @@
.dashboard-card {
// background-color: green;
.react-resizable {
position: relative;
}
.react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+");
background-position: bottom right;
padding: 0 3px 3px 0;
}
.react-resizable-handle-sw {
bottom: 0;
left: 0;
cursor: sw-resize;
transform: rotate(90deg);
}
.react-resizable-handle-se {
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-resizable-handle-nw {
top: 0;
left: 0;
cursor: nw-resize;
transform: rotate(180deg);
}
.react-resizable-handle-ne {
top: 0;
right: 0;
cursor: ne-resize;
transform: rotate(270deg);
}
.react-resizable-handle-w,
.react-resizable-handle-e {
top: 50%;
margin-top: -10px;
cursor: ew-resize;
}
.react-resizable-handle-w {
left: 0;
transform: rotate(135deg);
}
.react-resizable-handle-e {
right: 0;
transform: rotate(315deg);
}
.react-resizable-handle-n,
.react-resizable-handle-s {
left: 50%;
margin-left: -10px;
cursor: ns-resize;
}
.react-resizable-handle-n {
top: 0;
transform: rotate(225deg);
}
.react-resizable-handle-s {
bottom: 0;
transform: rotate(45deg);
}
.react-grid-layout {
position: relative;
transition: height 200ms ease;
}
.react-grid-item {
transition: all 200ms ease;
transition-property: left, top;
}
.react-grid-item.cssTransforms {
transition-property: transform;
}
.react-grid-item.resizing {
z-index: 1;
will-change: width, height;
}
.react-grid-item.react-draggable-dragging {
transition: none;
z-index: 3;
will-change: transform;
}
.react-grid-item.dropping {
visibility: hidden;
}
.react-grid-item.react-grid-placeholder {
background: red;
opacity: 0.2;
transition-duration: 100ms;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.react-grid-item > .react-resizable-handle {
position: absolute;
width: 20px;
height: 20px;
bottom: 0;
right: 0;
cursor: se-resize;
}
.react-grid-item > .react-resizable-handle::after {
content: "";
position: absolute;
right: 3px;
bottom: 3px;
width: 5px;
height: 5px;
border-right: 2px solid rgba(0, 0, 0, 0.4);
border-bottom: 2px solid rgba(0, 0, 0, 0.4);
}
.react-resizable-hide > .react-resizable-handle {
display: none;
}
.dashboard-card {
height: 100%;
width: 100%;
.ant-card-body {
// background-color: red;
height: 100%;
height: 80%;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
// // background-color: red;
// height: 90%;
// width: 100%;
// padding: 8px;
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
}
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
.ant-table {
height: 100%;
.ant-table-container {
height: 100%;
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
export function GenerateDashboardData(data) {
return data;
}

View File

@@ -0,0 +1,135 @@
import { Button, Card, Table, Typography } from "antd";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
import { SyncOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsAllocationsSummary);
export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => {
if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}, [socket, socket.connected, jobId]);
const columns = [
{
title: t("jobs.fields.dms.center"),
dataIndex: "center",
key: "center",
},
{
title: t("jobs.fields.dms.sale"),
dataIndex: "sale",
key: "sale",
render: (text, record) => Dinero(record.sale).toFormat(),
},
{
title: t("jobs.fields.dms.cost"),
dataIndex: "cost",
key: "cost",
render: (text, record) => Dinero(record.cost).toFormat(),
},
{
title: t("jobs.fields.dms.sale_dms_acctnumber"),
dataIndex: "sale_dms_acctnumber",
key: "sale_dms_acctnumber",
render: (text, record) =>
record.profitCenter && record.profitCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.cost_dms_acctnumber"),
dataIndex: "cost_dms_acctnumber",
key: "cost_dms_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_acctnumber,
},
{
title: t("jobs.fields.dms.dms_wip_acctnumber"),
dataIndex: "dms_wip_acctnumber",
key: "dms_wip_acctnumber",
render: (text, record) =>
record.costCenter && record.costCenter.dms_wip_acctnumber,
},
];
return (
<Card
title={title}
extra={
<Button
onClick={() => {
socket.emit("cdk-calculate-allocations", jobId, (ack) =>
setAllocationsSummary(ack)
);
}}
>
<SyncOutlined />
</Button>
}
>
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}
summary={() => {
const totals = allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{
// totals.totalCost.toFormat()
}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
);
}}
/>
</Card>
);
}

View File

@@ -0,0 +1,105 @@
import { useLazyQuery } from "@apollo/client";
import { Button, Input, Modal, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkVehicles);
export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
const [visible, setVisible] = useState(false);
const [selectedModel, setSelectedModel] = useState(null);
const { t } = useTranslation();
const [callSearch, { loading, error, data }] =
useLazyQuery(SEARCH_DMS_VEHICLES);
const columns = [
{
title: t("vehicles.fields.v_make_desc"),
dataIndex: "make",
key: "make",
},
{
title: t("vehicles.fields.v_model_desc"),
dataIndex: "model",
key: "model",
},
{
title: t("jobs.fields.dms.dms_make"),
dataIndex: "makecode",
key: "makecode",
},
{
title: t("jobs.fields.dms.dms_model"),
dataIndex: "modelcode",
key: "modelcode",
},
];
return (
<div>
<Modal
width={"90%"}
visible={visible}
onCancel={() => setVisible(false)}
onOk={() => {
form.setFieldsValue({
dms_make: selectedModel.makecode,
dms_model: selectedModel.modelcode,
});
setVisible(false);
}}
>
{error && <AlertComponent error={error.message} />}
<Table
title={() => (
<Input.Search
onSearch={(val) => callSearch({ variables: { search: val } })}
placeholder={t("general.labels.search")}
/>
)}
columns={columns}
loading={loading}
rowKey="id"
dataSource={data ? data.search_dms_vehicles : []}
onRow={(record) => {
return {
onClick: () => setSelectedModel(record),
};
}}
rowSelection={{
onSelect: (record) => {
setSelectedModel(record);
},
type: "radio",
selectedRowKeys: [selectedModel && selectedModel.id],
}}
/>
</Modal>
<Button
onClick={() => {
setVisible(true);
callSearch({
variables: {
search: job && job.v_model_desc && job.v_model_desc.substr(0, 3),
},
});
}}
>
{t("jobs.actions.dms.findmakemodelcode")}
</Button>
</div>
);
}

View File

@@ -0,0 +1,34 @@
import { Button } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch);
export function DmsCdkMakesRefetch({ bodyshop, form, socket }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const handleRefetch = async () => {
setLoading(true);
await axios.post("/cdk/getvehicles", {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id,
});
setLoading(false);
};
return (
<Button loading={loading} onClick={handleRefetch}>
{t("jobs.actions.dms.refetchmakesmodels")}
</Button>
);
}

View File

@@ -0,0 +1,169 @@
import { Button, Table, Col, Checkbox } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { socket } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsCustomerSelector);
export function DmsCustomerSelector({ bodyshop }) {
const { t } = useTranslation();
const [customerList, setcustomerList] = useState([]);
const [visible, setVisible] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [dmsType, setDmsType] = useState("cdk");
socket.on("cdk-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("cdk");
setcustomerList(customerList);
});
socket.on("pbs-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("pbs");
setcustomerList(customerList);
console.log(
"🚀 ~ file: dms-customer-selector.component.jsx ~ line 37 ~ socket.on ~ customerList",
customerList
);
});
const onUseSelected = () => {
setVisible(false);
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
setSelectedCustomer(null);
};
const onUseGeneric = () => {
setVisible(false);
socket.emit(
"cdk-selected-customer",
bodyshop.cdk_configuration.generic_customer_number
);
setSelectedCustomer(null);
};
const onCreateNew = () => {
setVisible(false);
socket.emit("cdk-selected-customer", null);
setSelectedCustomer(null);
};
const cdkColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: ["id", "value"],
key: "id",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["name1", "fullName"],
key: "name1",
sorter: (a, b) =>
alphaSort(a.name1 && a.name1.fullName, b.name1 && b.name1.fullName),
},
{
title: t("jobs.fields.dms.address"),
//dataIndex: ["name2", "fullName"],
key: "address",
render: (record, value) =>
`${record.address && record.address.addressLine[0]}, ${
record.address && record.address.city
} ${record.address && record.address.stateOrProvince} ${
record.address && record.address.postalCode
}`,
},
];
const pbsColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: "ContactId",
key: "ContactId",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
key: "name1",
sorter: (a, b) => alphaSort(a.LastName, b.LastName),
render: (text, record) =>
`${record.FirstName || ""} ${record.LastName || ""}`,
},
{
title: t("jobs.fields.dms.address"),
key: "address",
render: (record, value) =>
`${record.Address}, ${record.City} ${record.State} ${record.ZipCode}`,
},
];
if (!visible) return null;
return (
<Col span={24}>
<Table
title={() => (
<div>
<Button onClick={onUseSelected} disabled={!selectedCustomer}>
{t("jobs.actions.dms.useselected")}
</Button>
<Button
onClick={onUseGeneric}
disabled={
!(
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.generic_customer_number
)
}
>
{t("jobs.actions.dms.usegeneric")}
</Button>
<Button onClick={onCreateNew}>
{t("jobs.actions.dms.createnewcustomer")}
</Button>
</div>
)}
pagination={{ position: "top" }}
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
rowKey={(record) =>
dmsType === "cdk" ? record.id.value : record.ContactId
}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (record) => {
setSelectedCustomer(
dmsType === "cdk" ? record.id.value : record.ContactId
);
},
type: "radio",
selectedRowKeys: [selectedCustomer],
}}
/>
</Col>
);
}

View File

@@ -0,0 +1,55 @@
import { Divider, Space, Tag, Timeline } from "antd";
import moment from "moment";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsLogEvents);
export function DmsLogEvents({ socket, logs, bodyshop }) {
return (
<Timeline pending reverse={true}>
{logs.map((log, idx) => (
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
<Space wrap align="start" style={{}}>
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:mm:ss")}</span>
<Divider type="vertical" />
<span>{log.message}</span>
</Space>
</Timeline.Item>
))}
</Timeline>
);
}
function LogLevelHierarchy(level) {
switch (level) {
case "TRACE":
return "pink";
case "DEBUG":
return "orange";
case "INFO":
return "blue";
case "WARNING":
return "yellow";
case "ERROR":
return "red";
default:
return 0;
}
}

View File

@@ -0,0 +1,342 @@
import { DeleteFilled } from "@ant-design/icons";
import {
Button,
Card,
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Statistic,
Typography,
} from "antd";
import Dinero from "dinero.js";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
export function DmsPostForm({ bodyshop, socket, job }) {
const [form] = Form.useForm();
const { t } = useTranslation();
const handlePayerSelect = (value, index) => {
form.setFieldsValue({
payers: form.getFieldValue("payers").map((payer, mapIndex) => {
if (index !== mapIndex) return payer;
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find((i) => i.name === value);
if (!cdkPayer) return payer;
return {
...cdkPayer,
dms_acctnumber: cdkPayer.dms_acctnumber,
controlnumber: job && job[cdkPayer.control_type],
};
}),
});
};
const handleFinish = (values) => {
socket.emit(`${determineDmsType(bodyshop)}-export-job`, {
jobid: job.id,
txEnvelope: values,
});
};
return (
<Card title={t("jobs.labels.dms.postingform")}>
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{
story: t("jobs.labels.dms.defaultstory", {
ro_number: job.ro_number,
area_of_damage: job.area_of_damage && job.area_of_damage.impact1,
}).substr(0, 239),
}}
>
<LayoutFormRow grow>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="kmin"
label={t("jobs.fields.kmin")}
initialValue={job && job.kmin}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
<Form.Item
name="kmout"
label={t("jobs.fields.kmout")}
initialValue={job && job.kmout}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
</LayoutFormRow>
{bodyshop.cdk_dealerid && (
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</LayoutFormRow>
)}
<Form.Item
name="story"
label={t("jobs.fields.dms.story")}
rules={[
{
required: true,
},
]}
>
<Input.TextArea maxLength={240} />
</Form.Item>
<Divider />
<Form.List name={["payers"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space wrap>
<Form.Item
label={t("jobs.fields.dms.payer.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
},
]}
>
<Select
style={{ minWidth: "15rem" }}
onSelect={(value) => handlePayerSelect(value, index)}
>
{bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.map((payer) => (
<Select.Option key={payer.name}>
{payer.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.dms_acctnumber")}
key={`${index}dms_acctnumber`}
name={[field.name, "dms_acctnumber"]}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.amount")}
key={`${index}amount`}
name={[field.name, "amount"]}
rules={[
{
required: true,
},
]}
>
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.controlnumber")}
key={`${index}controlnumber`}
name={[field.name, "controlnumber"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
const payers = form.getFieldValue("payers");
const row = payers && payers[index];
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find(
(i) => i && row && i.name === row.name
);
return (
<div>
{cdkPayer &&
t(`jobs.fields.${cdkPayer.control_type}`)}
</div>
);
}}
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</Space>
</Form.Item>
))}
<Form.Item>
<Button
disabled={!(fields.length < 3)}
onClick={() => {
if (fields.length < 3) add();
}}
style={{ width: "100%" }}
>
{t("jobs.actions.dms.addpayer")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item shouldUpdate>
{() => {
//Perform Calculation to determine discrepancy.
let totalAllocated = Dinero();
const payers = form.getFieldValue("payers");
payers &&
payers.forEach((payer) => {
totalAllocated = totalAllocated.add(
Dinero({ amount: Math.round((payer?.amount || 0) * 100) })
);
});
const totals =
socket.allocationsSummary &&
socket.allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
const discrep = totals
? totals.totalSale.subtract(totalAllocated)
: Dinero();
return (
<Space size="large" wrap align="center">
<Statistic
title={t("jobs.labels.subtotal")}
value={(totals ? totals.totalSale : Dinero()).toFormat()}
/>
<Typography.Title>-</Typography.Title>
<Statistic
title={t("jobs.labels.dms.totalallocated")}
value={totalAllocated.toFormat()}
/>
<Typography.Title>=</Typography.Title>
<Statistic
title={t("jobs.labels.dms.notallocated")}
valueStyle={{
color: discrep.getAmount() === 0 ? "green" : "red",
}}
value={discrep.toFormat()}
/>
<Button
disabled={
!socket.allocationsSummary || discrep.getAmount() !== 0
}
htmlType="submit"
>
{t("jobs.actions.dms.post")}
</Button>
</Space>
);
}}
</Form.Item>
</Form>
</Card>
);
}

View File

@@ -0,0 +1,111 @@
//import "tui-image-editor/dist/tui-image-editor.css";
import { Result } from "antd";
import * as markerjs2 from "markerjs2";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { GenerateSrcUrl } from "../jobs-documents-gallery/job-documents.utility";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function DocumentEditorComponent({ currentUser, bodyshop, document }) {
const imgRef = useRef(null);
const [loading, setLoading] = useState(false);
const [uploaded, setuploaded] = useState(false);
const markerArea = useRef(null);
const { t } = useTranslation();
const triggerUpload = useCallback(
async (dataUrl) => {
setLoading(true);
handleUpload(
{
filename: `${document.key.split("/").pop()}-${Date.now()}.jpg`,
file: await b64toBlob(dataUrl),
onSuccess: () => {
setLoading(false);
setuploaded(true);
},
onError: () => setLoading(false),
},
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: document.jobid,
//billId: billId,
tagsArray: ["edited"],
//callback: callbackAfterUpload,
}
);
},
[bodyshop, currentUser, document]
);
useEffect(() => {
if (imgRef.current !== null) {
// create a marker.js MarkerArea
markerArea.current = new markerjs2.MarkerArea(imgRef.current);
// attach an event handler to assign annotated image back to our image element
markerArea.current.addCloseEventListener((closeEvent) => {});
markerArea.current.addRenderEventListener((dataUrl) => {
imgRef.current.src = dataUrl;
markerArea.current.close();
triggerUpload(dataUrl);
});
// launch marker.js
markerArea.current.renderAtNaturalSize = true;
markerArea.current.renderImageType = "image/jpeg";
markerArea.current.renderImageQuality = 1;
//markerArea.current.settings.displayMode = "inline";
markerArea.current.show();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [triggerUpload]);
async function b64toBlob(url) {
const res = await fetch(url);
return await res.blob();
}
return (
<div>
{!loading && !uploaded && (
<img
ref={imgRef}
src={GenerateSrcUrl(document)}
alt="sample"
crossOrigin="anonymous"
style={{ maxWidth: "90vw", maxHeight: "90vh" }}
/>
)}
{loading && <LoadingSpinner message={t("documents.labels.uploading")} />}
{uploaded && (
<Result
status="success"
title={t("documents.successes.edituploaded")}
/>
)}
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DocumentEditorComponent);

View File

@@ -0,0 +1,59 @@
import { useQuery } from "@apollo/client";
import { Result } from "antd";
import queryString from "query-string";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation } from "react-router";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { GET_DOCUMENT_BY_PK } from "../../graphql/documents.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import DocumentEditor from "./document-editor.component";
const mapDispatchToProps = (dispatch) => ({
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
});
export default connect(null, mapDispatchToProps)(DocumentEditorContainer);
export function DocumentEditorContainer({ setBodyshop }) {
//Get the image details for the image to be saved.
//Get the document id from the search string.
const { documentId } = queryString.parse(useLocation().search);
const { t } = useTranslation();
const {
loading: loadingShop,
error: errorShop,
data: dataShop,
} = useQuery(QUERY_BODYSHOP, {
fetchPolicy: "network-only",
});
useEffect(() => {
if (dataShop) setBodyshop(dataShop.bodyshops[0]);
}, [dataShop, setBodyshop]);
const { loading, error, data } = useQuery(GET_DOCUMENT_BY_PK, {
variables: { documentId },
skip: !documentId,
});
if (loading || loadingShop) return <LoadingSpinner />;
if (error || errorShop)
return (
<AlertComponent
message={error.message || errorShop.message}
type="error"
/>
);
if (!data || !data.documents_by_pk)
return <Result status="404" title={t("general.errors.notfound")} />;
return (
<div>
<DocumentEditor document={data ? data.documents_by_pk : null} />
</div>
);
}

View File

@@ -5,6 +5,8 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_NEW_DOCUMENT } from "../../graphql/documents.queries";
import { axiosAuthInterceptorId } from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient";
import exifr from "exifr";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
//Required to prevent headers from getting set and rejected from Cloudinary.
@@ -12,15 +14,18 @@ var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
export const handleUpload = (ev, context) => {
console.log("Handling Upload", ev);
logImEXEvent("document_upload", { filetype: ev.file.type });
const { onError, onSuccess, onProgress } = ev;
const { bodyshop, jobId } = context;
let key = `${bodyshop.id}/${jobId}/${ev.file.name.replace(/\.[^/.]+$/, "")}`;
let extension = ev.file.name.split(".").pop();
const fileName = ev.file.name || ev.filename;
let key = `${bodyshop.id}/${jobId}/${fileName.replace(
/\.[^/.]+$/,
""
)}-${new Date().getTime()}`;
let extension = fileName.split(".").pop();
uploadToCloudinary(
key,
extension,
@@ -54,7 +59,7 @@ export const uploadToCloudinary = async (
// let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;
//Get the signed url.
console.log("fileType", fileType);
const upload_preset = fileType.startsWith("video")
? "incoming_upload_video"
: "incoming_upload";
@@ -67,7 +72,6 @@ export const uploadToCloudinary = async (
});
if (signedURLResponse.status !== 200) {
console.log("Error Getting Signed URL", signedURLResponse.statusText);
if (!!onError) onError(signedURLResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.getpresignurl", {
@@ -85,6 +89,7 @@ export const uploadToCloudinary = async (
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
},
};
const formData = new FormData();
formData.append("file", file);
@@ -105,13 +110,8 @@ export const uploadToCloudinary = async (
...options,
}
);
console.log("Upload Response", cloudinaryUploadResponse.data);
if (cloudinaryUploadResponse.status !== 200) {
console.log(
"Error uploading to cloudinary.",
cloudinaryUploadResponse.statusText
);
if (!!onError) onError(cloudinaryUploadResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.insert", {
@@ -122,6 +122,16 @@ export const uploadToCloudinary = async (
}
//Insert the document with the matching key.
let takenat;
if (fileType.includes("image")) {
try {
const exif = await exifr.parse(file);
takenat = exif && exif.DateTimeOriginal;
} catch (error) {
console.log("Unable to parse image file for EXIF Data");
}
}
const documentInsert = await client.mutate({
mutation: INSERT_NEW_DOCUMENT,
variables: {
@@ -132,9 +142,10 @@ export const uploadToCloudinary = async (
uploaded_by: uploaded_by,
key: key,
type: fileType,
extension: extension,
extension: cloudinaryUploadResponse.data.format || extension,
bodyshopid: bodyshop.id,
size: cloudinaryUploadResponse.data.bytes || file.size,
takenat,
},
],
},
@@ -166,6 +177,7 @@ export const uploadToCloudinary = async (
}
};
//Also needs to be updated in media JS and mobile app.
export function DetermineFileType(filetype) {
if (!filetype) return "auto";
else if (filetype.startsWith("image")) return "image";

View File

@@ -0,0 +1,54 @@
import { useQuery } from "@apollo/client";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import { selectEmailConfig } from "../../redux/email/email.selectors";
import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
emailConfig: selectEmailConfig,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EmailDocumentsComponent);
export function EmailDocumentsComponent({
emailConfig,
selectedMediaState,
}) {
const { t } = useTranslation();
const [selectedMedia, setSelectedMedia] = selectedMediaState;
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
variables: {
jobId: emailConfig.jobid,
},
skip: !emailConfig.jobid,
});
return (
<div>
{loading && <LoadingSpinner />}
{error && <AlertComponent message={error.message} type="error" />}
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
) : null}
{data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
</div>
);
}

View File

@@ -1,14 +1,71 @@
import { UploadOutlined } from "@ant-design/icons";
import { Card, Divider, Form, Input, Select, Upload } from "antd";
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
import {
Divider,
Form,
Input,
Select,
Tabs,
Upload,
Space,
Menu,
Dropdown,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import EmailDocumentsComponent from "../email-documents/email-documents.component";
import _ from "lodash";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EmailOverlayComponent);
export default function EmailOverlayComponent({ form }) {
export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const email = item.props.value;
form.setFieldsValue({ to: _.uniq([...form.getFieldValue("to"), email]) });
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.employees
.filter((e) => e.user_email)
.map((e, idx) => (
<Menu.Item value={e.user_email} key={idx}>
{`${e.first_name} ${e.last_name}`}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<div>
<Form.Item
label={t("emails.fields.to")}
label={
<Space>
{t("emails.fields.to")}
<Dropdown overlay={menu}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
<UserAddOutlined />
</a>
</Dropdown>
</Space>
}
name="to"
rules={[
{
@@ -36,6 +93,8 @@ export default function EmailOverlayComponent({ form }) {
</Form.Item>
<Divider>{t("emails.labels.preview")}</Divider>
<strong>{t("emails.labels.pdfcopywillbeattached")}</strong>
<Form.Item shouldUpdate>
{() => {
return (
@@ -52,34 +111,38 @@ export default function EmailOverlayComponent({ form }) {
}}
</Form.Item>
<Card title={t("emails.labels.attachments")}>
<Form.Item
name="fileList"
valuePropName="fileList"
getValueFromEvent={(e) => {
console.log("Upload event:", e);
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}}
>
<Upload.Dragger
beforeUpload={Upload.LIST_IGNORE}
multiple
listType="picture-card"
<Tabs>
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<EmailDocumentsComponent selectedMediaState={selectedMediaState} />
</Tabs.TabPane>
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
<Form.Item
name="fileList"
valuePropName="fileList"
getValueFromEvent={(e) => {
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}}
>
<>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
</p>
</>
</Upload.Dragger>
</Form.Item>
</Card>
<Upload.Dragger
beforeUpload={Upload.LIST_IGNORE}
multiple
listType="picture-card"
>
<>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
</p>
</>
</Upload.Dragger>
</Form.Item>
</Tabs.TabPane>
</Tabs>
</div>
);
}

View File

@@ -43,9 +43,17 @@ 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 = {
from: {
name: `${currentUser.displayName} @ ${bodyshop.shopname}`,
name: currentUser.displayName
? `${currentUser.displayName} @ ${bodyshop.shopname}`
: bodyshop.shopname,
address: EmailSettings.fromAddress,
},
ReplyTo: {
@@ -56,17 +64,18 @@ export function EmailOverlayContainer({
const handleFinish = async (values) => {
logImEXEvent("email_send_from_modal");
console.log(`values`, values);
const attachments = [];
await asyncForEach(values.fileList, async (f) => {
const t = {
ContentType: f.type,
Filename: f.name,
Base64Content: (await toBase64(f.originFileObj)).split(",")[1],
};
attachments.push(t);
});
//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);
// });
setSending(true);
try {
@@ -74,15 +83,34 @@ export function EmailOverlayContainer({
...defaultEmailFrom,
...values,
html: rawHtml,
attachments: 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,
});
notification["success"]({ message: t("emails.successes.sent") });
toggleEmailOverlayVisible();
} catch (error) {
console.log(JSON.stringify(error));
notification["error"]({
message: t("emails.errors.notsent", { message: error.message }),
});
@@ -93,13 +121,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:
@@ -111,6 +148,18 @@ export function EmailOverlayContainer({
html: response.data,
fileList: [],
});
if (
bodyshop.md_email_cc[emailConfig.template.name] &&
bodyshop.md_email_cc[emailConfig.template.name].length > 0
) {
form.setFieldsValue({
cc: [
...(form.getFieldValue("cc") || []),
...bodyshop.md_email_cc[emailConfig.template.name],
],
});
}
setLoading(false);
};
@@ -137,7 +186,12 @@ export function EmailOverlayContainer({
<LoadingSpinner message={t("emails.labels.generatingemail")} />
</div>
)}
{!loading && <EmailOverlayComponent form={form} />}
{!loading && (
<EmailOverlayComponent
form={form}
selectedMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
</Form>
</Modal>
);
@@ -155,8 +209,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

@@ -7,10 +7,12 @@ import { selectCurrentUser } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
@@ -19,14 +21,24 @@ export function EmailTestComponent({ currentUser, setEmailOptions }) {
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values", values);
GenerateDocument(
{
name: values.key,
variables: {
...(values.start
? {
start: moment(values.start).startOf("day").format("YYYY-MM-DD"),
}
: {}),
...(values.end
? { end: moment(values.end).endOf("day").format("YYYY-MM-DD") }
: {}),
...(values.start
? { starttz: moment(values.start).startOf("day") }
: {}),
...(values.end ? { endtz: moment(values.end).endOf("day") } : {}),
...(values.id ? { id: values.id } : {}),
...(values.start ? { start: values.start } : {}),
...(values.end ? { end: values.end } : {}),
},
},
{

View File

@@ -5,9 +5,15 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { tracker } from "../../App/App.container";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -31,24 +37,41 @@ class ErrorBoundary extends React.Component {
componentDidCatch(error, info) {
console.log("Exception Caught by Error Boundary.", error, info);
this.setState({ ...this.state, error, info });
tracker.event("error_boundary", error, true);
}
handleErrorSubmit = () => {
const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
window.$crisp.push([
"do",
"message:send",
[
"text",
`I hit the following error: \n\n
${this.state.error.message}\n\n
${this.state.error.stack}\n\n
URL:${window.location} as ${this.props.currentUser.email} for ${
this.props.bodyshop && this.props.bodyshop.name
}
`,
],
]);
----
System Generated Log:
${this.state.error.message}
${this.state.error.stack}
`;
window.$crisp.push(["do", "chat:open"]);
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
const URL = `https://bodyshop.atlassian.net/servicedesk/customer/portal/3/group/8/create/26?summary=123&description=${encodeURI(
errorDescription
)}&customfield_10049=${window.location}&email=${
this.props.currentUser.email
}`;
console.log(`URL`, URL);
window.open(URL, "_blank");
// ----
// System Generated Log:
// ${this.state.error.message}
// ${this.state.error.stack}
// `;
// const URL = `https://bodyshop.atlassian.net/servicedesk/customer/portal/3/group/8/create/26?summary=123&description=${encodeURI(
// errorDescription
// )}&customfield_10049=${window.location}&email=${
// this.props.currentUser.email
// }`;
// console.log(`URL`, URL);
// window.open(URL, "_blank");
};
render() {
@@ -57,6 +80,23 @@ ${this.state.error.stack}
if (this.state.hasErrored === true) {
logImEXEvent("error_boundary_rendered", { error, info });
window.$crisp.push([
"set",
"session:event",
[
[
[
"error_boundary",
{
error: this.state.error.message,
stack: this.state.error.stack,
},
"red",
],
],
],
]);
return (
<div>
<Result
@@ -74,7 +114,7 @@ ${this.state.error.stack}
{t("general.actions.refresh")}
</Button>
<Button onClick={this.handleErrorSubmit}>
{t("general.actions.submitticket")}
{t("general.actions.senderrortosupport")}
</Button>
</Space>
}

View File

@@ -2,7 +2,7 @@ import { withApollo } from "@apollo/client/react/hoc";
import React, { Component } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent, messaging } from "../../firebase/firebase.utils";
//import { logImEXEvent, messaging } from "../../firebase/firebase.utils";
import { selectCurrentUser } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
@@ -15,21 +15,20 @@ const mapDispatchToProps = (dispatch) => ({
class FcmNotificationComponent extends Component {
async componentDidMount() {
//const { client, currentUser } = this.props;
if (!!!messaging) return; //Skip all of the notification functionality if the firebase SDK could not start.
messaging
.requestPermission()
.then(async function () {
// const token = await messaging.getToken();
// client.mutate({
// mutation: UPDATE_FCM_TOKEN,
// variables: { authEmail: currentUser.email, token: { [token]: true } },
// });
})
.catch(function (err) {
console.log("Unable to get permission to notify.", err);
logImEXEvent("fcm_permission_denied", { message: err });
});
// if (!!!messaging) return; //Skip all of the notification functionality if the firebase SDK could not start.
// messaging
// .requestPermission()
// .then(async function () {
// // const token = await messaging.getToken();
// // client.mutate({
// // mutation: UPDATE_FCM_TOKEN,
// // variables: { authEmail: currentUser.email, token: { [token]: true } },
// // });
// })
// .catch(function (err) {
// console.log("Unable to get permission to notify.", err);
// logImEXEvent("fcm_permission_denied", { message: err });
// });
}
render() {

View File

@@ -0,0 +1,50 @@
import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
function FeatureWrapper({
bodyshop,
featureName,
noauth,
children,
...restProps
}) {
const { t } = useTranslation();
if (HasFeatureAccess({ featureName, bodyshop })) return children;
return (
noauth || (
<AlertComponent
message={t("general.messages.nofeatureaccess")}
type="warning"
/>
)
);
}
export function HasFeatureAccess({ featureName, bodyshop }) {
return (
bodyshop.features.allAccess ||
moment(bodyshop.features[featureName]).isAfter(moment())
);
}
export default connect(mapStateToProps, null)(FeatureWrapper);
/*
dashboard
production-board
scoreboard
csi
tech-console
mobile-imaging
*/

View File

@@ -21,7 +21,7 @@ export const PhoneItemFormatterValidation = (getFieldValue, name) => ({
return Promise.resolve();
} else {
const p = parsePhoneNumber(value, "CA");
if (p.isValid()) {
if (p && p.isValid()) {
return Promise.resolve();
} else {
return Promise.reject(i18n.t("general.validation.invalidphone"));

View File

@@ -11,9 +11,8 @@ import AlertComponent from "../alert/alert.component";
export default function GlobalSearch() {
const { t } = useTranslation();
const [callSearch, { loading, error, data }] = useLazyQuery(
GLOBAL_SEARCH_QUERY
);
const [callSearch, { loading, error, data }] =
useLazyQuery(GLOBAL_SEARCH_QUERY);
const executeSearch = (v) => {
if (v && v.variables.search && v.variables.search !== "") callSearch(v);
@@ -38,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 || ""
@@ -46,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>
),
@@ -63,13 +62,16 @@ export default function GlobalSearch() {
}`,
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space wrap split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />} wrap>
<span>{`${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
owner.ownr_co_nm || ""
}`}</span>
<PhoneNumberFormatter>
{owner.ownr_ph1}
</PhoneNumberFormatter>
<PhoneNumberFormatter>
{owner.ownr_ph2}
</PhoneNumberFormatter>
</Space>
</Link>
),
@@ -86,14 +88,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>
),
@@ -108,11 +110,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>
),
@@ -127,7 +130,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>
@@ -147,7 +150,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>
@@ -166,10 +169,9 @@ export default function GlobalSearch() {
return (
<AutoComplete
dropdownMatchSelectWidth={"false"}
options={options}
onSearch={handleSearch}
allowClear
placeholder={t("general.labels.globalsearch")}
>
<Input.Search loading={loading} />
</AutoComplete>

View File

@@ -3,10 +3,12 @@ import Icon, {
BarChartOutlined,
CarFilled,
ClockCircleFilled,
DashboardFilled,
DollarCircleFilled,
ExportOutlined,
FieldTimeOutlined,
FileAddFilled,
FileAddOutlined,
FileFilled,
GlobalOutlined,
HomeFilled,
@@ -14,6 +16,7 @@ import Icon, {
LineChartOutlined,
PaperClipOutlined,
PhoneOutlined,
QuestionCircleFilled,
ScheduleOutlined,
SettingOutlined,
TeamOutlined,
@@ -43,13 +46,16 @@ import {
} from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { signOutStart } from "../../redux/user/user.actions";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import GlobalSearch from "../global-search/global-search.component";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
recentItems: selectRecentItems,
selectedHeader: selectSelectedHeader,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -67,6 +73,7 @@ const mapDispatchToProps = (dispatch) => ({
function Header({
handleMenuClick,
currentUser,
bodyshop,
selectedHeader,
signOutStart,
setBillEnterContext,
@@ -78,12 +85,11 @@ function Header({
const { t } = useTranslation();
return (
<Layout.Header style={{ display: "flex", alignItems: "center" }}>
<Layout.Header>
<Menu
mode="horizontal"
//theme="light"
theme={"dark"}
style={{ flex: 1 }}
selectedKeys={[selectedHeader]}
onClick={handleMenuClick}
subMenuCloseDelay={0.3}
@@ -95,6 +101,7 @@ function Header({
<Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
</Menu.Item>
<Menu.SubMenu
key="jobssubmenu"
icon={<Icon component={FaCarCrash} />}
title={t("menus.header.jobs")}
>
@@ -109,12 +116,14 @@ function Header({
{t("menus.header.availablejobs")}
</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="newjob" icon={<FileAddOutlined />}>
<Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
</Menu.Item>
<Menu.Divider key="div1" />
<Menu.Item key="alljobs" icon={<UnorderedListOutlined />}>
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Divider key="div2" />
<Menu.Item key="productionlist" icon={<ScheduleOutlined />}>
<Link to="/manage/production/list">
{t("menus.header.productionlist")}
@@ -125,13 +134,13 @@ function Header({
{t("menus.header.productionboard")}
</Link>
</Menu.Item>
<Menu.Divider />
<Menu.Divider key="div3" />
<Menu.Item key="scoreboard" icon={<LineChartOutlined />}>
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
key="customers"
icon={<UserOutlined />}
title={t("menus.header.customers")}
>
@@ -143,6 +152,7 @@ function Header({
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
key="ccs"
icon={<CarFilled />}
title={t("menus.header.courtesycars")}
>
@@ -163,6 +173,7 @@ function Header({
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
key="accounting"
icon={<DollarCircleFilled />}
title={t("menus.header.accounting")}
>
@@ -184,7 +195,7 @@ function Header({
>
{t("menus.header.enterbills")}
</Menu.Item>
<Menu.Divider />
<Menu.Divider key="div4" />
<Menu.Item key="allpayments" icon={<BankFilled />}>
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
</Menu.Item>
@@ -196,11 +207,11 @@ function Header({
context: null,
});
}}
icon={<Icon component={FaCreditCard} />}
>
<Icon component={FaCreditCard} />
{t("menus.header.enterpayment")}
</Menu.Item>
<Menu.Divider />
<Menu.Divider key="div5" />
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
<Link to="/manage/timetickets">
@@ -219,9 +230,10 @@ function Header({
>
{t("menus.header.entertimeticket")}
</Menu.Item>
<Menu.Divider />
<Menu.Divider key="div6" />
<Menu.SubMenu
key="accountingexport"
title={t("menus.header.export")}
icon={<ExportOutlined />}
>
@@ -230,16 +242,26 @@ function Header({
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
)}
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
)}
<Menu.Item key="export-logs">
<Link to="/manage/accounting/exportlogs">
{t("menus.header.export-logs")}
@@ -255,11 +277,17 @@ function Header({
{t("menus.header.temporarydocs")}
</Link>
</Menu.Item>
<Menu.SubMenu title={t("menus.header.shop")} icon={<SettingOutlined />}>
<Menu.SubMenu
key="shopsubmenu"
title={t("menus.header.shop")}
icon={<SettingOutlined />}
>
<Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}>
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
</Menu.Item>
<Menu.Item key="dashboard" icon={<DashboardFilled />}>
<Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
</Menu.Item>
<Menu.Item
key="reportcenter"
icon={<BarChartOutlined />}
@@ -285,17 +313,27 @@ function Header({
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu
style={{ float: "right" }}
key="user"
title={
currentUser.displayName ||
currentUser.email ||
t("general.labels.unknown")
}
>
<Menu.Item danger onClick={() => signOutStart()}>
<Menu.Item key="signout" danger onClick={() => signOutStart()}>
{t("user.actions.signout")}
</Menu.Item>
<Menu.Item
key="help"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
>
{t("menus.header.help")}
</Menu.Item>
<Menu.Item
key="rescue"
onClick={() => {
window.open("https://imexrescue.com/", "_blank");
}}
@@ -309,6 +347,7 @@ function Header({
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
</Menu.Item>
<Menu.SubMenu
key="langselecter"
title={
<span>
<GlobalOutlined />
@@ -327,7 +366,7 @@ function Header({
</Menu.Item>
</Menu.SubMenu>
</Menu.SubMenu>
<Menu.SubMenu style={{ float: "right" }} title={<ClockCircleFilled />}>
<Menu.SubMenu key="recent" title={<ClockCircleFilled />}>
{recentItems.map((i, idx) => (
<Menu.Item key={idx}>
<Link to={i.url}>{i.label}</Link>
@@ -335,9 +374,6 @@ function Header({
))}
</Menu.SubMenu>
</Menu>
<div>
<GlobalSearch />
</div>
</Layout.Header>
);
}

View File

@@ -10,15 +10,11 @@ export default function HelpRescue() {
var bodyFormData = new FormData();
bodyFormData.append("Code", code);
bodyFormData.append("hostederrorhandling", 1);
const res1 = await fetch(
"https://secure.logmeinrescue.com/Customer/Code.aspx",
{
mode: "no-cors",
method: "POST",
body: bodyFormData,
}
);
console.log("handleClick -> res1", await res1.text());
await fetch("https://secure.logmeinrescue.com/Customer/Code.aspx", {
mode: "no-cors",
method: "POST",
body: bodyFormData,
});
};
return (
@@ -40,7 +36,6 @@ export default function HelpRescue() {
method="post"
id="logmeinsupport"
onSubmit={(...props) => {
console.log(`props`, props);
alert();
}}
>

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