Compare commits

...

450 Commits

Author SHA1 Message Date
Allan Carr
5310866302 IO-3484 Shop Config Acct Section Move
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2026-01-02 13:40:50 -08:00
Allan Carr
e3ab229ac5 IO-3484 Shop Config Accounting Section Move
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-12-31 15:39:21 -08:00
Dave Richer
4a7bb07345 Merged in hotfix/missing-dms-migration (pull request #2738)
hotfix/missing-dms-migration - Add Back DMS_ID
2025-12-19 20:13:11 +00:00
Dave
01fec9fa79 hotfix/missing-dms-migration - Add Back DMS_ID 2025-12-19 15:12:24 -05:00
Dave Richer
2f88d613c3 Merged in release/2025-12-19-mini (pull request #2732)
Release/2025 12 19 mini - IO-3467 IO-3468 IO-3402 IO-3473
2025-12-19 19:14:30 +00:00
Dave Richer
c9467b3982 Merged in feature/IO-3402-Import-Add-Notifiers (pull request #2734)
feature/IO-3402-Import-Add-Notifiers - Fix Normalize
2025-12-19 19:12:04 +00:00
Dave
ca1a456312 feature/IO-3402-Import-Add-Notifiers - Fix Normalize 2025-12-19 14:10:15 -05:00
Dave
ca4c48bd5c release/2025-12-19-mini - Merge 2025-12-19 12:41:23 -05:00
Dave Richer
e5fd5c8bcb Merged in feature/IO-3468-sentry-exceptions (pull request #2731)
Feature/IO-3468 sentry exceptions
2025-12-19 17:39:23 +00:00
Dave Richer
46945a24a7 Merged in feature/IO-3467-report-center-filter-ui (pull request #2730)
IO-3467 Resolve UI on report center filter.
2025-12-19 17:35:47 +00:00
Dave Richer
be746500a6 Merged in feature/IO-3402-Import-Add-Notifiers (pull request #2729)
Feature/IO-3402 Import Add Notifiers
2025-12-19 17:35:12 +00:00
Dave
71c6d9fa94 IO-3473 trim user input 2025-12-19 12:07:30 -05:00
Dave
c010665ea9 feature/IO-3402-Import-Add-Notifiers - Fix 2025-12-18 18:26:02 -05:00
Dave
6d94ce7e5c feature/IO-3468-Senty-Exceptions - Fix unused import 2025-12-18 13:52:55 -05:00
Dave
d6fba12cd9 feature/IO-3402-Import-Add-Notifiers - Fix Auto Notifiers 2025-12-18 13:27:24 -05:00
Patrick Fic
182a8d59ab IO-3468 Add sentry exceptions & minor nul coalesce fixes. 2025-12-16 10:28:00 -08:00
Patrick Fic
f1847ef650 IO-3467 Resolve UI on report center filter. 2025-12-15 08:37:32 -08:00
Allan Carr
6ea1c291e6 Merged in release/2025-12-19 (pull request #2703)
Release/2025 12 19
2025-12-12 02:36:57 +00:00
Allan Carr
05d5c96491 Merged in feature/IO-3462-Project-Mexico-Mod (pull request #2701)
IO-3462 Project Mexico Mod

Approved-by: Dave Richer
2025-12-10 20:18:14 +00:00
Allan Carr
35a566cbe5 IO-3462 Project Mexico Mod
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-12-10 09:52:04 -08:00
Dave Richer
f12e40e4c6 Merged in feature/IO-3461-Fix-EULA (pull request #2698)
feature/IO-3461-Fix-Eula
2025-12-09 22:15:22 +00:00
Dave
bb4e671c83 feature/IO-3461-Fix-Eula 2025-12-09 17:13:59 -05:00
Dave Richer
d1637d2432 Merged in release/2025-12-05 (pull request #2696)
Release/2025 12 05 into master-AIO - IO-3450 IO-3452 IO-3262 - IO-3456 IO-3262
2025-12-06 01:48:37 +00:00
Allan Carr
1c79628613 Merged in feature/IO-3262-Tech-Console-Job-Clock-Out (pull request #2692)
IO-3262 Correction for v_year in Project Mexico

Approved-by: Dave Richer
2025-12-05 19:00:38 +00:00
Allan Carr
521a7084b7 IO-3262 Correction for v_year in Project Mexico
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-12-05 09:48:32 -08:00
Dave Richer
77268d5f5b Merged in feature/IO-3457-job-lifecyle-tags (pull request #2689)
feature/IO-3457-Job-Lifecycle-Tags
2025-12-04 21:23:27 +00:00
Dave
1b3abf17ec feature/IO-3457-Job-Lifecycle-Tags 2025-12-04 16:22:41 -05:00
Dave Richer
3cfd445894 Merged in feature/IO-3456-Broken-Image-Path (pull request #2687)
feature/IO-3456-Broken-Image - Fix issue
2025-12-04 19:22:03 +00:00
Dave
b510eec9aa feature/IO-3456-Broken-Image - Fix issue 2025-12-04 14:20:58 -05:00
Allan Carr
e5eac0933f Merged in feature/IO-3262-Tech-Console-Job-Clock-Out (pull request #2685)
IO-3262 Add email address to Usage Report

Approved-by: Dave Richer
2025-12-03 19:04:08 +00:00
Allan Carr
a3c71fdfc0 IO-3262 Add email address to Usage Report
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-12-03 10:28:20 -08:00
Allan Carr
78750d3d96 Merged in feature/IO-3262-Tech-Console-Job-Clock-Out (pull request #2682)
IO-3262 Tech Console Job Clock Out

Approved-by: Dave Richer
2025-12-02 18:59:41 +00:00
Allan Carr
90edf94fee IO-3262 Tech Console Job Clock Out
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-12-01 12:50:11 -08:00
Allan Carr
065fb72677 Merged in feature/IO-3452-Documents-Adjustments (pull request #2678)
IO-3452 Documents Adjustments

Approved-by: Dave Richer
2025-11-28 15:49:40 +00:00
Allan Carr
ddc6141480 IO-3452 Documents Adjustments
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-27 13:15:26 -08:00
Allan Carr
7bc137fa79 Merged in feature/IO-3450-Additional-Crisp-Segments (pull request #2675)
Feature/IO-3450 Additional Crisp Segments

Approved-by: Dave Richer
2025-11-26 16:30:07 +00:00
Allan Carr
dafe9de753 IO-3450 Grammer Correction
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-25 18:10:10 -08:00
Allan Carr
78a8474a24 IO-3450 Additional Crisp Segments
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-25 17:56:19 -08:00
Dave Richer
123066f1cd Merged in release/2025-11-21 (pull request #2671)
Release/2025 11 21 into Master-AIO - IO-3435 IO-3445 IO-3440 IO-3446
2025-11-21 19:19:15 +00:00
Allan Carr
a153cca3c0 Merged in feature/IO-3440-Payment-By-Date-Excel (pull request #2669)
IO-3440 Payment By Date - Excel
2025-11-21 00:26:54 +00:00
Allan Carr
35c7c32c8e IO-3440 Payment By Date - Excel
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-20 16:29:05 -08:00
Allan Carr
6d6b64ebc3 Merged in feature/IO-3446-Kaizen-Datapump-Extension (pull request #2667)
IO-3446 Kaizen Datapump Extension

Approved-by: Dave Richer
2025-11-20 19:53:47 +00:00
Allan Carr
c954695d3c IO-3446 Kaizen Datapump Extension
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-19 19:43:55 -08:00
Patrick Fic
338d8e2136 Merged in feature/media-analytics-logging (pull request #2663)
Add unique/dupe columns to media analytics.

Approved-by: Dave Richer
2025-11-19 19:26:00 +00:00
Allan Carr
6674206b4f Merged in feature/IO-3440-Payment-By-Date-Excel (pull request #2665)
IO-3440 Payment By Date - Excel

Approved-by: Dave Richer
2025-11-19 19:16:31 +00:00
Allan Carr
c46ad521d1 Merged in feature/IO-3445-RBAC-BILL-ENTER (pull request #2664)
IO-3445 RBAC Bill:Enter

Approved-by: Dave Richer
2025-11-19 19:13:26 +00:00
Allan Carr
66e5bec4d8 IO-3440 Payment By Date - Excel
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-18 17:05:42 -08:00
Allan Carr
0d3161ef84 IO-3445 RBAC Bill:Enter
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-18 16:47:26 -08:00
Patrick Fic
1cd11bdc18 Add unique/dupe columns to media analytics. 2025-11-17 16:42:39 -08:00
Patrick Fic
9cce2696e2 Merge branch 'master-AIO' into feature/media-analytics-logging 2025-11-17 16:31:04 -08:00
Patrick Fic
508d32d2d9 Merged in feature/media-analytics-logging (pull request #2661)
Add indexes for media analytics.
2025-11-12 04:36:26 +00:00
Patrick Fic
cccc307862 Add indexes for media analytics. 2025-11-11 20:35:59 -08:00
Patrick Fic
0772139a60 Merged in feature/media-analytics-logging (pull request #2659)
Add trigger to remove fk violations for media analytics.
2025-11-10 23:38:58 +00:00
Patrick Fic
70028c8be6 Add trigger to remove fk violations for media analytics. 2025-11-10 15:38:17 -08:00
Allan Carr
3dc22bfdab Merged in feature/IO-3435-SpeedPrint-Filtering-in-Config (pull request #2657)
IO-3435 SpeedPrint Filtering in Config

Approved-by: Dave Richer
2025-11-10 19:31:55 +00:00
Allan Carr
f3ee421030 IO-3435 SpeedPrint Filtering in Config
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-10 11:23:49 -08:00
Dave Richer
6b41d6f2a2 Merged in release/2025-11-07 (pull request #2654)
Release/2025-11-07 into master-AIO - IO-3428 IO-3430 IO-3433 IO-3432 IO-3429
2025-11-08 01:50:57 +00:00
Patrick Fic
b8fed77f43 Merged in feature/media-analytics-logging (pull request #2655)
Schema changes to floats.
2025-11-07 20:16:20 +00:00
Patrick Fic
3f5614d77e Schema changes to floats. 2025-11-07 12:15:45 -08:00
Patrick Fic
6c5c4bd333 Merged in feature/media-analytics-logging (pull request #2652)
Add shop ID to jobs get for LMS.
2025-11-06 22:03:52 +00:00
Patrick Fic
43c1eef70c Add shop ID to jobs get for LMS. 2025-11-06 14:03:19 -08:00
Patrick Fic
b8d97d9821 Merged in feature/media-analytics-logging (pull request #2651)
Add API route for media analytics, and updates to database schema.
2025-11-06 21:58:14 +00:00
Patrick Fic
6843441b17 Add API route for media analytics, and updates to database schema. 2025-11-06 13:57:39 -08:00
Dave Richer
409e04ed0e Merged in feature/IO-3429-Remove-Data-Dog (pull request #2649)
feature/IO-3429-Remove-Data-Dog - Remove Datadog
2025-11-06 18:04:17 +00:00
Dave
95c7872b34 feature/IO-3429-Remove-Data-Dog - Remove Datadog 2025-11-06 13:02:33 -05:00
Allan Carr
91bf5c8d0f Merged in feature/IO-3433-CC-Info-in-Job-Statuses (pull request #2645)
IO-3433 Extend CC Info in Job Status

Approved-by: Dave Richer
2025-11-06 17:53:39 +00:00
Allan Carr
3660fb1b1b Merged in feature/IO-3432-Admin-Clerk (pull request #2647)
IO-3432 Admin Clerk

Approved-by: Dave Richer
2025-11-06 17:52:48 +00:00
Allan Carr
7573286163 IO-3432 Admin Clerk
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-05 18:08:12 -08:00
Patrick Fic
34ab42c0ad Merged in feature/media-analytics-logging (pull request #2646)
Hasura  for media analytics
2025-11-05 23:09:00 +00:00
Patrick Fic
a3c3f60d2a Hasura for media analytics 2025-11-05 15:07:48 -08:00
Allan Carr
8147bc76fd IO-3433 Extend CC Info in Job Status
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-04 13:05:53 -08:00
Allan Carr
9ef1022311 Merged in feature/IO-3430-Additional-Costs (pull request #2643)
IO-3430 Additional Cost Items

Approved-by: Dave Richer
2025-11-04 19:29:15 +00:00
Allan Carr
3c0e62ffac IO-3430 Additional Cost Items
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-11-04 08:57:23 -08:00
Allan Carr
a9a0415501 Merged in feature/IO-3428-Media-Selector (pull request #2641)
IO-3428 Media Selector

Approved-by: Dave Richer
2025-11-03 17:11:14 +00:00
Allan Carr
fbaf47b89b IO-3428 Media Selector
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-31 15:13:29 -07:00
Dave Richer
907f291f90 Merged in release/2025-10-24 (pull request #2640)
Release/2025 10 24 into master-AIO - IO-3412 - IO-3398
2025-10-25 01:41:09 +00:00
Patrick Fic
abce19530f Merged in feature/IO-3398-bill-line-select-display (pull request #2638)
IO-3398 Bill line search select will now show price in select value.
2025-10-23 18:49:29 +00:00
Patrick Fic
1fd9b68320 IO-3398 Bill line search select will now show price in select value. 2025-10-23 11:48:35 -07:00
Patrick Fic
cff1afe605 Merged in feature/IO-3412-costing-tax-rate-typo (pull request #2637)
IO-3412 Resolve tax rate typo in job costing.
2025-10-23 16:21:03 +00:00
Patrick Fic
b337309f94 IO-3412 Resolve tax rate typo in job costing. 2025-10-23 09:17:15 -07:00
Patrick Fic
77f041b0f1 Merged in feature/IO-3407-us-qbd-tax-adj-quantity (pull request #2636)
IO-3407 Remove tax code ref for adjustment as well.
2025-10-17 22:09:11 +00:00
Patrick Fic
68be8670b4 IO-3407 Remove tax code ref for adjustment as well. 2025-10-17 15:06:58 -07:00
Patrick Fic
745c429f08 Merged in feature/IO-3407-us-qbd-tax-adj-quantity (pull request #2635)
IO-3407 Resolve quantity required for QBD sales tax adj.
2025-10-17 21:57:23 +00:00
Patrick Fic
cc9e4740de IO-3407 Resolve quantity required for QBD sales tax adj. 2025-10-17 14:56:44 -07:00
Dave Richer
06ebcbaa07 Merged in release/2025-10-17 (pull request #2633)
Release/2025 10 17  into Master-AIO - IO-3373 - IO-3404 - IO-3385
2025-10-17 18:59:37 +00:00
Dave
42427c4569 release/2025-10-17 - Refine Job Totals Logging (switch from error to warn for USA key issues, normalize case on logging level) 2025-10-17 14:56:21 -04:00
Dave Richer
d5f921ed35 Merged in feature/IO-3385-Remove-CASL-From-Rome (pull request #2631)
feature/IO-3385-Remove-CASL-From-Rome - Remove CASL Report from Rome Customers.
2025-10-15 15:48:13 +00:00
Dave
043471fdbc feature/IO-3385-Remove-CASL-From-Rome - Remove CASL Report from Rome Customers. 2025-10-15 11:46:51 -04:00
Dave Richer
54850e8ee2 Merged in feature/IO-3404-Seamless-Logout (pull request #2629)
feature/IO-3404-Seamless-Logout - Implement Seamless Logout
2025-10-15 15:33:29 +00:00
Dave
199ddc7d9e feature/IO-3404-Seamless-Logout - Implement Seamless Logout 2025-10-15 11:31:34 -04:00
Dave Richer
13a44b9a59 Merged in hotfix/2025-10-10 (pull request #2628)
Hotfix/2025 10 10 - Change BullMQ Key Delimiters based on system logs.

Approved-by: Patrick Fic
2025-10-11 01:35:51 +00:00
Dave Richer
e3337bacea Merged in hotfix/2025-10-10 (pull request #2626)
Hotfix/2025 10 10
2025-10-10 20:05:08 +00:00
Dave
8e6c809fc6 Update 2025-10-10 16:02:56 -04:00
Dave
41afedd02c Change Delemiters in BullMQ, was using an invalid : 2025-10-10 15:58:32 -04:00
Allan Carr
29df829120 Merged in feature/IO-3373-Dashboard-Component-Redux-Fix (pull request #2622)
IO-3373 Dashboard Component Refresh Cyclic object fix

Approved-by: Dave Richer
2025-10-10 16:08:21 +00:00
Allan Carr
11e0c3e507 Merged in hotfix/2025-10-09 (pull request #2624)
Hotfix/2025 10 09
2025-10-09 17:57:20 +00:00
Allan Carr
6c1c7c9c2c Merged in feature/IO-3386-carfax-rps (pull request #2623)
Feature/IO-3386 carfax rps
2025-10-09 17:55:21 +00:00
Allan Carr
d2b6054e60 Merged in feature/IO-3386-carfax-rps (pull request #2620)
IO-3368 CARFAX Adjustments for OP_CODE [PENDING APPROVAL]
2025-10-09 17:28:31 +00:00
Allan Carr
7004ed9880 IO-3373 Dashboard Component Refresh Cyclic object fix
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-09 09:13:03 -07:00
Allan Carr
36dfed80fb IO-3386 CARFAX RPS adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-08 19:31:46 -07:00
Dave Richer
e3df22160b Merged in release/2025-10-17 (pull request #2621)
Release/2025 10 17 into master-AIO - IO-3330, IO-3356, IO-3365, IO-3368, IO-3373, IO-3386, IO-3388, IO-3389, IO-3390, IO-3395
2025-10-08 01:22:16 +00:00
Allan Carr
0ccfe6f3aa IO-3368 CARFAX Adjustments for OP_CODE
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-07 13:02:44 -07:00
Allan Carr
34d70f6a18 Merged in feature/IO-3373-Dashboard-Component-Redux-Fix (pull request #2618)
IO-3373 Remove Cache Eviction

Approved-by: Dave Richer
2025-10-07 18:40:51 +00:00
Allan Carr
fc1bf213c7 Merged in feature/IO-3395-Postback-Payment-Date (pull request #2614)
IO-3395 Postback Payment Date

Approved-by: Dave Richer
2025-10-07 18:40:28 +00:00
Allan Carr
4dc72986d0 IO-3373 Remove Cache Eviction
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-07 11:07:12 -07:00
Allan Carr
f19b9cb8e1 IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-07 10:59:10 -07:00
Allan Carr
c9ade68dc9 Merged in feature/IO-3386-carfax-rps (pull request #2610)
IO-3386 Cleanup To extension

Approved-by: Dave Richer
2025-10-07 16:16:15 +00:00
Allan Carr
0d247a38d2 Merged in feature/IO-3373-Dashboard-Component-Redux-Fix (pull request #2609)
IO-3373 Dashboard Component Redux Fix

Approved-by: Dave Richer
2025-10-07 16:15:49 +00:00
Dave Richer
595ffa02ba Merged in feature/IO-3390-parts-management-2 (pull request #2615)
feature/IO-3390-Parts-Management-2 - Add Job status patch route, Bodyshop Patch route, remove PAO from simplified parts filter.
2025-10-07 16:13:24 +00:00
Dave
d573335eb0 feature/IO-3390-Parts-Management-2 - Add Job status patch route, Bodyshop Patch route, remove PAO from simplified parts filter. 2025-10-07 12:11:53 -04:00
Allan Carr
bdd5056c9a IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-06 21:35:31 -07:00
Allan Carr
4f6db827e7 IO-3395 Postback Payment Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-06 17:50:19 -07:00
Patrick Fic
e50bbc3bcc Merged in hotfix/2025-10-06-remove-amplitude (pull request #2613)
Hotfix/2025 10 06 remove amplitude
2025-10-06 15:35:46 +00:00
Patrick Fic
15792cb0ef Merged in hotfix/2025-10-06-remove-amplitude (pull request #2612)
Hotfix/2025 10 06 remove amplitude
2025-10-06 15:33:23 +00:00
Patrick Fic
856a24d496 Remove console log. 2025-10-06 08:33:01 -07:00
Patrick Fic
74d6bcc004 Remove amplitude. 2025-10-06 08:19:43 -07:00
Allan Carr
2af95de353 IO-3386 Cleanup To extension
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-03 12:13:42 -07:00
Allan Carr
7ffb2c1aad IO-3373 Dashboard Component Redux Fix
This properly saves the components with out triggering a redux flush

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-03 12:11:09 -07:00
Allan Carr
cf3f94bf98 Merged in feature/IO-3386-carfax-rps (pull request #2606)
Feature/IO-3386 carfax rps

Approved-by: Patrick Fic
2025-10-03 18:29:39 +00:00
Allan Carr
4899297539 IO-3386 CARFAX RPS
Add Brad Rhoades to Report

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-03 11:24:10 -07:00
Allan Carr
0bd8e862e4 Merged in IO-3389-Parts-Scanning-Part-# (pull request #2605)
IO-3389 Parts Scanning with Part # Update

Approved-by: Patrick Fic
2025-10-03 17:54:21 +00:00
Allan Carr
4afafef98f Merged in feature/IO-3368-QB-Bill-Accumulator (pull request #2607)
Feature/IO-3368 QB Bill Accumulator

Approved-by: Patrick Fic
2025-10-03 17:53:38 +00:00
Allan Carr
48a3220c49 Merged in feature/IO-3365-Bills-Filtering-Server-Side (pull request #2608)
IO-3365 Bills Filtering Server Side

Approved-by: Patrick Fic
2025-10-03 17:51:22 +00:00
Allan Carr
31529fad80 IO-3365 Bills Filtering Server Side
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-02 23:43:25 -07:00
Allan Carr
7bc105ea46 IO-3368 QB Bill Accumulator
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-02 21:26:41 -07:00
Allan Carr
ac4fcf1694 IO-3368 QB Bill Accumulator
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-02 21:22:35 -07:00
Allan Carr
678a87f55d Merge branch 'release/2025-10-17' into feature/IO-3386-carfax-rps
Signed-off-by: Allan Carr <allan@imexsystems.ca>

# Conflicts:
#	hasura/metadata/cron_triggers.yaml
2025-10-02 18:36:42 -07:00
Allan Carr
9e977d9a58 IO-3386 CARFAX RPS
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-02 18:34:40 -07:00
Allan Carr
43f822e6ad Merge branch 'master-AIO' into feature/IO-3386-carfax-rps 2025-10-02 18:32:46 -07:00
Allan Carr
7239698a21 IO-3389 Parts Scanning with Part # Update
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-02 15:49:31 -07:00
Patrick Fic
46be1fa889 Merged in feature/IO-3356-pbs-ro-posting (pull request #2577)
Feature/IO-3356 pbs ro posting

Approved-by: Dave Richer
2025-10-02 20:27:48 +00:00
Patrick Fic
40a8036472 Adjust logging statements. 2025-10-02 13:26:12 -07:00
Patrick Fic
22970ac149 Remove additional RO posting. 2025-10-02 13:20:15 -07:00
Dave Richer
63eeab888f Merged in feature/IO-3388-parts-queue-fix (pull request #2603)
feature/IO-3388 - Parts Queue Fix
2025-10-02 16:56:37 +00:00
Dave
367ced88d8 feature/IO-3388 - Parts Queue Fix 2025-10-02 12:55:50 -04:00
Allan Carr
01664c52ec Merged in hotfix/2025-10-01 (pull request #2602)
IO-3384 Mitchell OP14 Import USA
2025-10-02 01:03:15 +00:00
Allan Carr
7f67b01c87 Merged in feature/IO-3387-Mitchell-OP14-Import-USA (pull request #2599)
IO-3384 Mitchell OP14 Import USA

Approved-by: Dave Richer
2025-10-01 23:33:56 +00:00
Allan Carr
e66d52784f Merged in feature/IO-3387-Mitchell-OP14-Import-USA (pull request #2600)
IO-3384 Mitchell OP14 Import USA

Approved-by: Dave Richer
2025-10-01 23:33:36 +00:00
Allan Carr
a75969097e IO-3384 Mitchell OP14 Import USA
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-10-01 16:30:54 -07:00
Patrick Fic
5e308f13d3 IO-3386 2025-10-01 14:47:32 -07:00
Patrick Fic
93e137c84e Final changes for pbs. 2025-10-01 13:59:11 -07:00
Allan Carr
36d4a02be0 Merged in feature/IO-3330-CARFAX-Adjustments (pull request #2598)
IO-3330 CARFAX Adjustment

Approved-by: Dave Richer
2025-10-01 18:23:01 +00:00
Allan Carr
31367f278e IO-3330 CARFAX Adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-29 10:08:11 -07:00
Allan Carr
a384fd5072 Merged in release/2025-09-26 (pull request #2597)
IO-3330 CARFAX Adjustment
2025-09-27 06:17:33 +00:00
Allan Carr
490c66a9cb Merged in feature/IO-3330-CARFAX-Adjustments (pull request #2595)
IO-3330 CARFAX Adjustment
2025-09-27 06:13:25 +00:00
Allan Carr
d38dab0738 IO-3330 CARFAX Adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-26 23:14:10 -07:00
Dave Richer
37624d385f Merged in release/2025-09-26 (pull request #2592)
Release/2025 09 26 into master-AIO - IO-3365 - IO-3373 - IO-3325 - IO-3377 - IO-3330 - IO-3076
2025-09-27 01:35:06 +00:00
Allan Carr
d565934288 IO-3330 Carfax Adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-26 14:06:35 -07:00
Allan Carr
5d03574d65 Merged in feature/IO-3330-CARFAX-Adjustments (pull request #2593)
IO-3330 Carfax Adjustment
2025-09-26 21:05:19 +00:00
Patrick Fic
34f7c115b4 Merged in feature/IO-3076-update-usage-report-cron (pull request #2590)
IO-3076 auto trigger report on wed
2025-09-26 15:27:36 +00:00
Patrick Fic
3ac74df504 IO-3076 auto trigger report on wed 2025-09-26 08:26:09 -07:00
Patrick Fic
9ff3311579 Add vehicle query. 2025-09-26 08:19:57 -07:00
Allan Carr
d5f13f750f Merged in feature/IO-3330-CARFAX-Adjustments (pull request #2588)
IO-3330 CARFAX Adjustments

Approved-by: Dave Richer
2025-09-26 00:14:47 +00:00
Allan Carr
b9ddac36a9 IO-3330 CARFAX Adjustments
remove voided jobs

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-25 16:30:02 -07:00
Allan Carr
c982dde1f5 IO-3330 CARFAX Adjustments
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-25 16:20:28 -07:00
Dave Richer
7f61f652f7 Merged in feature/IO-3377-Add-Notification-Tone-For-Messaging (pull request #2586)
Feature/IO-3377 Add Notification Tone For Messaging
2025-09-24 18:39:52 +00:00
Dave
c1e1d5e82c feature/IO-3377-Add-Notification-Tone-For-Messaging - Finalize 2025-09-24 14:38:58 -04:00
Dave
5ae2e33596 feature/IO-3377-Add-Notification-Tone-For-Messaging - Finalize 2025-09-24 14:32:45 -04:00
Dave
e11260e8fc feautre/IO-3377-Add-Notification-Tone-For-Messaging - remove wav, adjust 2025-09-24 13:52:55 -04:00
Dave Richer
282a22ff51 Merged in feature/IO-3377-Add-Notification-Tone-For-Messaging (pull request #2584)
feautre/IO-3377-Add-Notification-Tone-For-Messaging - Complete
2025-09-24 16:03:52 +00:00
Dave
dfd88308e0 feautre/IO-3377-Add-Notification-Tone-For-Messaging - Complete 2025-09-24 12:02:20 -04:00
Dave
33579c3e6a Merge remote-tracking branch 'origin/release/2025-09-26' into feature/IO-3377-Add-Notification-Tone-For-Messaging 2025-09-24 11:51:27 -04:00
Allan Carr
0b9b3c027f Merged in hotfix/2025-09-23 (pull request #2583)
Hotfix/2025 09 23

Approved-by: Dave Richer
2025-09-24 01:05:35 +00:00
Allan Carr
154f9cdfe6 Merged in feature/IO-3330-CARFAX-Datapump-Adjustments (pull request #2582)
Feature/IO-3330 CARFAX Datapump Adjustments
2025-09-23 20:38:23 +00:00
Allan Carr
5b8c7d922c Merged in feature/IO-3330-CARFAX-Datapump-Adjustments (pull request #2580)
IO-3330 CARFAX Datapump Adjustments

Approved-by: Dave Richer
2025-09-23 19:50:50 +00:00
Allan Carr
d20347d5dc IO-3330 CARFAX Datapump Adjustments
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-23 12:42:09 -07:00
Allan Carr
68c4a1efd7 Merged in feature/IO-3373-Dashboard-Component-Errors (pull request #2567)
IO-3373 Dashboard Errors on Large Datasets

Approved-by: Dave Richer
2025-09-23 15:02:42 +00:00
Allan Carr
3e6d6fdbd1 Merged in feature/IO-3330-CARFAX-Datapump-Adjustments (pull request #2572)
Feature/IO-3330 CARFAX Datapump Adjustments

Approved-by: Dave Richer
2025-09-23 15:01:58 +00:00
Allan Carr
302fd58a56 Merge branch 'feature/IO-3373-Dashboard-Component-Infinite-Recursion' into feature/IO-3373-Dashboard-Component-Errors
Signed-off-by: Allan Carr <allan@imexsystems.ca>

# Conflicts:
#	client/src/components/dashboard-grid/dashboard-grid.component.jsx
2025-09-22 09:54:48 -07:00
Patrick Fic
31cfdf9ea3 Merge branch 'feature/IO-3325-additional-log-events' into release/2025-09-26 2025-09-19 09:56:04 -07:00
Patrick Fic
f93800ded4 IO-3325 Additional ImEX log Events. 2025-09-19 09:51:38 -07:00
Patrick Fic
05385fca6d Reoslve DMS allocation scroll. 2025-09-18 14:26:23 -07:00
Patrick Fic
1355d79fa4 Comments and cleanup. 2025-09-18 14:23:51 -07:00
Patrick Fic
8805538706 Merge branch 'master-AIO' into feature/IO-3356-pbs-ro-posting 2025-09-18 14:06:24 -07:00
Patrick Fic
9e35b0f123 Add and remove logging. 2025-09-18 14:05:14 -07:00
Allan Carr
252758747b Merged in hotfix/2025-09-17 (pull request #2576)
IO-3376 Scrollbar Theming
2025-09-17 23:17:00 +00:00
Allan Carr
8b39b7c7be Merged in hotfix/2025-09-17 (pull request #2574)
IO-3376 Scrollbar Theming
2025-09-17 23:07:29 +00:00
Allan Carr
ada07bad62 Merged in feature/IO-3376-Scrollbar-Theming (pull request #2573)
IO-3376 Scrollbar Theming
2025-09-17 23:06:30 +00:00
Allan Carr
166a33af4e IO-3376 Scrollbar Theming
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-17 16:07:15 -07:00
Allan Carr
038aa82087 IO-3330 CARFAX Datapump Adjustment
Cron trigger and billing email

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-17 14:43:07 -07:00
Allan Carr
99f425eac4 Merged in hotfix/2025-09-17 (pull request #2571)
IO-3373 Dashboard Component Infinite Recursion
2025-09-17 21:09:03 +00:00
Allan Carr
b2c504c69d Merged in feature/IO-3373-Dashboard-Component-Infinite-Recursion (pull request #2570)
IO-3373 Dashboard Component Infinite Recursion
2025-09-17 21:01:11 +00:00
Allan Carr
ac6856b136 IO-3373 Dashboard Component Infinite Recursion
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-17 13:59:20 -07:00
Allan Carr
521955089f Merged in feature/IO-3373-Dashboard-Component-Infinite-Recursion (pull request #2568)
IO-3373 Dashboard Component Infinite Recursion
2025-09-17 20:58:29 +00:00
Allan Carr
4afff893c0 IO-3330 localEmailViewer Update
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-17 09:29:35 -07:00
Allan Carr
cc934fe333 IO-3373 Dashboard Errors on Large Datasets
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-16 17:20:23 -07:00
Patrick Fic
ddd3b3d056 Merged in feature/IO-3325-amplitude-reverse-proxy (pull request #2566)
IO-3325 Add reverse proxy URL for amplitude.
2025-09-15 21:37:33 +00:00
Patrick Fic
8ded028197 Merged in feature/IO-3325-amplitude-reverse-proxy (pull request #2565)
IO-3325 Add reverse proxy URL for amplitude.
2025-09-15 21:16:58 +00:00
Patrick Fic
2660466db1 IO-3325 Add reverse proxy URL for amplitude. 2025-09-15 14:13:48 -07:00
Patrick Fic
fd229d5d09 Debug level changes, vehicle upsert correction, and logic updates. 2025-09-12 13:14:38 -07:00
Allan Carr
c42a0139fc Merged in feature/IO-3365-Bills-Filters-and-Sorters (pull request #2563)
IO-3365 Push Filters to Query

Approved-by: Dave Richer
2025-09-12 15:33:12 +00:00
Allan Carr
02974e6e4b IO-3365 Push Filters to Query
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-11 21:32:18 -07:00
Patrick Fic
1bb66a5378 Merge branch 'master-AIO' into feature/IO-3356-pbs-ro-posting 2025-09-11 14:40:08 -07:00
Dave Richer
fe67efe47c Merged in release/2025-09-26 (pull request #2562)
Release/2025 09 26  into master-AIO IO-3365, IO-3366, IO-3369
2025-09-11 20:43:43 +00:00
Dave
69a35772e5 release/2025-09-26 - Remove sockets from Parts Management 2025-09-11 16:41:21 -04:00
Dave Richer
38932f4bf9 Merged in feature/IO-3369-Fix-Parts-Status-List-Component (pull request #2559)
feature/IO-3369-Fix-Parts-Status-List-Component - Add Fixes
2025-09-11 19:58:28 +00:00
Dave
3fcb36a28e feature/IO-3369-Fix-Parts-Status-List-Component - Add Fixes 2025-09-11 15:56:47 -04:00
Allan Carr
fe78f5c7ff Merged in feature/IO-3365-Bills-Filters-and-Sorters (pull request #2556)
IO-3365 Bills Filters and Sorters

Approved-by: Dave Richer
2025-09-11 18:49:55 +00:00
Allan Carr
683846c3b0 Merged in feature/IO-3366-Shop-General-Field-Validators (pull request #2557)
IO-3366 Shop General Field Validators

Approved-by: Dave Richer
2025-09-11 18:49:20 +00:00
Dave Richer
2cc0b247b6 Merged in master-AIO (pull request #2555)
Master AIO
2025-09-11 18:18:26 +00:00
Dave Richer
31579354d4 Merged in release/2025-09-12 (pull request #2554)
DO NOT MERGE - Release/2025-09-12 into master-AIO -IO-3255, IO-3310, IO-3352, IO-3355
2025-09-11 15:31:22 +00:00
Dave Richer
0e7531dc54 Merged in feature/IO-3255-simplified-part-management (pull request #2552)
feature/IO-3255-simplified-parts-management -Extra checks
2025-09-11 15:24:38 +00:00
Dave
268b57c38a feature/IO-3255-simplified-parts-management -Extra checks 2025-09-11 11:22:59 -04:00
Allan Carr
2b8b8b8073 IO-3366 Shop General Field Validators
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-10 22:12:15 -07:00
Allan Carr
808eeb91e9 IO-3365 Bills Filters and Sorters
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-10 21:33:10 -07:00
Dave Richer
23dd8fc9de Merged in feature/IO-3255-simplified-part-management (pull request #2548)
feature/IO-3255-simplified-parts-management - Remove some stuff in vendors
2025-09-10 19:59:10 +00:00
Dave
f499859078 Merge remote-tracking branch 'origin/release/2025-09-12' into feature/IO-3255-simplified-part-management 2025-09-10 15:58:06 -04:00
Dave
84d9e3251a feature/IO-3255-simplified-parts-management - Remove some stuff in vendors 2025-09-10 15:57:54 -04:00
Dave Richer
bd7db4dd02 Merged in feature/IO-3255-simplified-part-management (pull request #2546)
feature/IO-3255-simplified-parts-management - Fix Manual line in Change Request
2025-09-10 16:44:59 +00:00
Dave
e9804b736b feature/IO-3255-simplified-parts-management - Fix Manual line in Change Request 2025-09-10 12:44:16 -04:00
Dave Richer
a14874f116 Merged in feature/Reynolds-and-Reynolds-DMS-API-Integration (pull request #2543)
Feature/Reynolds and Reynolds DMS API Integration
2025-09-09 16:00:01 +00:00
Dave
ac9fac458c feature/Reynolds-and-Reynolds-DMS-API-Integration - DB Modifications 2025-09-09 11:59:18 -04:00
Dave
8f9db15852 feature/Reynolds-and-Reynolds-DMS-API-Integration - DB Modifications 2025-09-09 11:59:08 -04:00
Patrick Fic
01fc0cbc08 IO-3356 PBS posting initial working commit. 2025-09-05 10:45:59 -07:00
Dave Richer
61b3d3c18c Merged in feature/IO-3255-simplified-part-management (pull request #2540)
feature/IO-3255-simplified-parts-management - missing translations
2025-09-05 16:52:39 +00:00
Dave
e6f08d3b1c feature/IO-3255-simplified-parts-management - missing translations 2025-09-05 12:51:12 -04:00
Dave Richer
d5cf0f8371 Merged in feature/IO-3255-simplified-part-management (pull request #2538)
Feature/IO-3255 simplified part management
2025-09-05 16:46:39 +00:00
Dave
52e230fc54 feature/IO-3255-simplified-parts-management - bump deps 2025-09-05 12:45:46 -04:00
Dave
4011237c22 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 12:36:45 -04:00
Dave
c24bfbf655 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 12:31:45 -04:00
Allan Carr
08fe8c3c70 Merged in feature/IO-3355-Job-Cash-Discounting (pull request #2537)
Feature/IO-3355 Job Cash Discounting

Approved-by: Dave Richer
2025-09-05 16:30:47 +00:00
Dave
771a239773 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 12:16:25 -04:00
Dave
82195a0584 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 12:02:17 -04:00
Dave
838c24b3f1 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 11:03:20 -04:00
Dave
13cb68b0af feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 10:18:03 -04:00
Dave
7c84b08707 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 10:12:37 -04:00
Dave
3165957e95 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-05 10:05:23 -04:00
Allan Carr
d9f59fcad4 IO-3355 Correct translation
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-04 15:27:06 -07:00
Allan Carr
edaeb5d77a IO-3355 Job Cash Discounting
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-04 15:16:48 -07:00
Allan Carr
5365d95d6f Merged in feature/IO-3310-Shop-Info-Data-Preservation (pull request #2533)
IO-3310 Shop Info Data Preservation

Approved-by: Dave Richer
2025-09-03 14:48:39 +00:00
Allan Carr
5cfefd5afd Merged in feature/IO-3352-Arrived-Color-on-Schedule (pull request #2532)
IO-3352 Arrived Color on Schedule

Approved-by: Dave Richer
2025-09-03 14:48:10 +00:00
Dave Richer
bbccdb0650 Merged in feature/IO-3255-simplified-part-management (pull request #2534)
feature/IO-3255-simplified-parts-management - Fix deprovision route
2025-09-03 14:47:51 +00:00
Dave
f3535c01af feature/IO-3255-simplified-parts-management - Fix deprovision route 2025-09-03 10:47:04 -04:00
Dave Richer
7f8c82b300 Merged in release/2025-08-29 (pull request #2528)
Release/2025 08 29 into master-AIO - IO-3182, IO-3255, IO-3325, IO-3330, IO-3340
2025-09-03 00:56:28 +00:00
Patrick Fic
9ab3d8c81a Merge branch 'master-AIO' into feature/IO-XXXX-pbs-ro-posting 2025-09-02 15:24:15 -07:00
Patrick Fic
83c0696a3e Reformat translations. 2025-09-02 15:24:01 -07:00
Dave Richer
609ac2bd33 Merged in feature/IO-3255-simplified-part-management (pull request #2530)
Feature/IO-3255 simplified part management
2025-09-02 19:24:59 +00:00
Dave
0883274320 feature/IO-3255-simplified-parts-management - Expand deprovision route 2025-09-02 15:23:44 -04:00
Dave
fa33b88632 feature/IO-3255-simplified-parts-management - Expand deprovision route 2025-09-02 15:23:36 -04:00
Dave
bec32c1d70 feature/IO-3255-simplified-parts-management - Checkpoint 2025-09-02 15:01:56 -04:00
Allan Carr
eb18130e51 IO-3310 Shop Info Data Preservation
Correction to preserve hidden data

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-09-01 20:29:39 -07:00
Allan Carr
0fbf63dec8 IO-3352 Arrived Color on Schedule
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-31 22:42:24 -07:00
Patrick Fic
f817902d5c Merged in feature/IO-3340-imgproxy-pdf-contenttype (pull request #2527)
IO-3340 Resolve unenforced content-type
2025-08-28 22:08:09 +00:00
Patrick Fic
7a383aaec9 IO-3340 Resolve unenforced content-type 2025-08-28 15:07:18 -07:00
Dave Richer
e5c0ace6cb Merged in feature/IO-3255-simplified-part-management (pull request #2525)
feature/IO-3255-simplified-parts-management - Checkpoint
2025-08-28 20:01:35 +00:00
Dave
814447373a feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-28 16:00:39 -04:00
Dave Richer
d766a468c3 Merged in release/2025-08-29 (pull request #2523)
Release/2025 08 29
2025-08-28 19:36:10 +00:00
Dave
0ede2d0649 Merge remote-tracking branch 'origin/master-AIO' into release/2025-08-29 2025-08-28 15:33:26 -04:00
Dave Richer
54089c2ab3 Merged in feature/IO-3255-simplified-part-management (pull request #2521)
Feature/IO-3255 simplified part management
2025-08-28 19:26:25 +00:00
Dave
73e3d71cf1 feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-28 14:50:26 -04:00
Dave
f071a5cc9e feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-28 14:24:35 -04:00
Dave
67002b8443 Merge remote-tracking branch 'origin/feature/IO-3255-parts-management-review1' into feature/IO-3255-simplified-part-management 2025-08-28 14:06:07 -04:00
Dave
3f83c6afa7 Merge remote-tracking branch 'origin/release/2025-08-29' into feature/IO-3255-simplified-part-management 2025-08-28 13:27:16 -04:00
Dave
b7f57e91aa feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-28 13:26:47 -04:00
Allan Carr
b8465c0cc7 Merged in feature/IO-3330-CARFAX-Query (pull request #2520)
IO-3330 CARFAX Bodyshop Query Adjustment

Approved-by: Dave Richer
2025-08-28 17:25:18 +00:00
Allan Carr
6bae0b8406 IO-3330 CARFAX Bodyshop Query Adjustment
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-27 23:41:16 -07:00
Patrick Fic
553c154e46 IO-3255 Review comments. 2025-08-27 11:50:00 -07:00
Dave Richer
35cbb921d2 Merged in feature/IO-3255-simplified-part-management (pull request #2516)
feature/IO-3255-simplified-parts-management - Change Parts Status logic for Parts Management
2025-08-27 18:19:40 +00:00
Dave
6b926401d0 feature/IO-3255-simplified-parts-management - Change Parts Status logic for Parts Management 2025-08-27 14:17:23 -04:00
Patrick Fic
5b400dce4f Merge branch 'feature/IO-3255-simplified-part-management' into feature/IO-3255-parts-management-review1 2025-08-27 09:39:55 -07:00
Dave Richer
914946c264 Merged in feature/IO-3255-simplified-part-management (pull request #2514)
feature/IO-3255-simplified-parts-management - Change Parts Status logic for Parts Management
2025-08-27 16:37:27 +00:00
Dave
2939b5795b feature/IO-3255-simplified-parts-management - Change Parts Status logic for Parts Management 2025-08-27 12:35:53 -04:00
Dave Richer
531f968ce6 Merged in feature/IO-3255-simplified-part-management (pull request #2512)
feature/IO-3255-simplified-parts-management - Change From Claim to JobID (chgrq)
2025-08-27 15:37:47 +00:00
Dave
24cc3fa6a4 feature/IO-3255-simplified-parts-management - Change From Claim to JobID (chgrq) 2025-08-27 11:36:39 -04:00
Dave Richer
a6f17e7db5 Merged in feature/IO-3255-simplified-part-management (pull request #2510)
feature/IO-3255-simplified-parts-management - Typo in chg request route
2025-08-26 14:51:18 +00:00
Dave
29c904feea feature/IO-3255-simplified-parts-management - Typo in chg request route 2025-08-26 10:50:44 -04:00
Dave Richer
0743048e75 Merged in feature/IO-3255-simplified-part-management (pull request #2509)
Feature/IO-3255 simplified part management
2025-08-25 16:58:43 +00:00
Dave
e2f1758378 feature/IO-3255-simplified-parts-management - Simplified Parts print center stacks on top of each other, not beside each other 2025-08-25 11:33:12 -04:00
Dave
b6dc7a4d92 feature/IO-3255-simplified-parts-management - Notes are appended if they are not duplicates 2025-08-25 11:30:18 -04:00
Dave Richer
130745d7e7 Merged in hotfix/2025-08-22 (pull request #2508)
DO NOT MERGE Hotfix/2025 08 22
2025-08-22 19:36:51 +00:00
Patrick Fic
a722ab9758 hotfix/2025-08-22 - Datahog 2025-08-22 15:35:55 -04:00
Patrick Fic
41c9c0be49 Add in amplititude 2025-08-22 15:22:45 -04:00
Patrick Fic
52f62126e1 Merged in feature/IO-3340-imgproxy-pdf-contenttype (pull request #2507)
IO-3340 Change application type on PDFs uploaded via imgproxy to allow inline display.
2025-08-22 19:02:44 +00:00
Patrick Fic
cfdfb8110b IO-3340 Change application type on PDFs uploaded via imgproxy to allow inline display. 2025-08-22 12:01:16 -07:00
Allan Carr
eb04a8b6c5 Merged in feature/IO-3349-Chart-Enqueue-and-Label-Required (pull request #2505)
IO-3349 Chart Enqueue and Label Required

Approved-by: Dave Richer
2025-08-22 18:55:58 +00:00
Allan Carr
141b05f558 IO-3349 Chart Enqueue and Label Required
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-22 11:29:33 -07:00
Dave Richer
e9ea36fdad Merged in feature/IO-3255-simplified-part-management (pull request #2504)
feature/IO-3255-simplified-parts-management - Bug changes / Socket close cleanup
2025-08-22 18:26:38 +00:00
Dave
e990781ff7 feature/IO-3255-simplified-parts-management - Bug changes / Socket close cleanup 2025-08-22 14:24:03 -04:00
Patrick Fic
e24237e010 Merged in feature/IO-3325-amplitude (pull request #2502)
Feature/IO-3325 amplitude
2025-08-22 18:00:37 +00:00
Patrick Fic
f00f5f2e4a IO-3325 resolve partial commit error. 2025-08-22 10:57:38 -07:00
Patrick Fic
f38b550e75 IO-3325 Added posthog analytics for comparison. 2025-08-22 10:57:25 -07:00
Patrick Fic
fabe1508ac Merged in feature/IO-3325-amplitude (pull request #2500)
IO-3325 Add amplitude tracking.
2025-08-22 17:40:33 +00:00
Patrick Fic
5f3c880d0b IO-3325 Add amplitude tracking. 2025-08-22 10:40:11 -07:00
Patrick Fic
86f0c02c82 Merged in feature/IO-3348-es-vehicletype (pull request #2498)
ES-3348 Ass ES Vehicle type Route.
2025-08-22 15:53:22 +00:00
Patrick Fic
2004eb840f ES-3348 Ass ES Vehicle type Route. 2025-08-22 08:52:08 -07:00
Dave Richer
2f0190e190 Merged in feature/IO-3255-simplified-part-management (pull request #2496)
feature/IO-3255-simplified-parts-management - Missing breadcrumb / title translations
2025-08-22 15:31:40 +00:00
Dave
e5a48531a0 feature/IO-3255-simplified-parts-management - Missing breadcrumb / title translations 2025-08-22 11:31:01 -04:00
Patrick Fic
5552c73721 WIP PBS RO 2025-08-22 08:21:29 -07:00
Dave
9860447e42 feature/IO-3255-simplified-parts-management - Fix Breadcrumbs 2025-08-22 11:03:34 -04:00
Dave
364813193f feature/IO-3255-simplified-parts-management - DO NOT SPLIT LABOR / PARTS 2025-08-21 18:35:10 -04:00
Dave
2c8f3a173e feature/IO-3255-simplified-parts-management - Soft Deletes 2025-08-21 18:20:53 -04:00
Dave
1577921334 feature/IO-3255-simplified-parts-management -Bug fixes 2025-08-21 18:12:56 -04:00
Dave
a934249c02 feature/IO-3255-simplified-parts-management - Revert dashboard 2025-08-21 16:40:07 -04:00
Dave
6486de3c61 feature/IO-3255-simplified-parts-management - Update and fix dashboard-grid.component.jsx 2025-08-21 16:09:36 -04:00
Dave
ed6a8b210d feature/IO-3255-simplified-parts-management - Update and fix dashboard-grid.component.jsx 2025-08-21 15:51:10 -04:00
Dave
96e38d509f feature/IO-3255-simplified-parts-management - cleanup 2025-08-21 15:42:06 -04:00
Dave
42e072e8ff feature/IO-3255-simplified-parts-management - Package Updates 2025-08-21 15:11:41 -04:00
Dave
6942d6e4f9 feature/IO-3255-simplified-parts-management - Cleanup / Bug fixes 2025-08-21 14:57:55 -04:00
Dave
43d5a77d60 feature/IO-3255-simplified-parts-management - Checkoint 2025-08-21 12:51:14 -04:00
Dave
a74ce063ec feature/IO-3255-simplified-parts-management - Checkoint 2025-08-21 12:39:15 -04:00
Dave
9c45b49ab9 feature/IO-3255-simplified-parts-management - Remove unnecessary dotenv calls / fix import bug 2025-08-21 12:00:26 -04:00
Dave
d690790dfe feature/IO-3255-simplified-parts-management - Remove unnecessary dotenv calls / fix import bug 2025-08-21 11:57:12 -04:00
Dave
70fa638c37 feature/IO-3255-simplified-parts-management - Remove unnecessary dotenv calls 2025-08-21 11:45:38 -04:00
Dave Richer
77cacdec91 Merged in feature/IO-3343-The-Great-Lint-Cleanup (pull request #2488)
Lint all the things
2025-08-21 14:53:59 +00:00
Dave
33fb60ca1a Lint all the things 2025-08-19 16:23:29 -04:00
Dave
f6d6b548be feature/IO-3255-simplified-parts-management Deprovision route can now remove jobs, joblines, and audit trail 2025-08-19 11:17:29 -04:00
Dave
c0a215d20d feature/IO-3255-simplified-parts-management -Add Timezone default to provisioning 2025-08-19 10:32:02 -04:00
Dave Richer
8c9ef375be Merged in hotfix/background-colors-dark-mode (pull request #2485)
Hotfix/background colors dark mode
2025-08-18 18:39:42 +00:00
Dave Richer
5342377ca9 Merged in hotfix/background-colors-dark-mode (pull request #2483)
Fix Dark Mode Schedule
2025-08-18 18:38:32 +00:00
Dave
8295cb111a Fix Dark Mode Schedule 2025-08-18 14:37:00 -04:00
Dave Richer
cd4754069b Merged in hotfix/background-colors-dark-mode (pull request #2481)
feature/IO-3255-simplified-parts-management  - Beef Up Change Request Parser, add Change Request documentation data
2025-08-18 18:13:48 +00:00
Dave
951d214d49 feature/IO-3255-simplified-parts-management - Beef Up Change Request Parser, add Change Request documentation data 2025-08-18 14:13:16 -04:00
Dave
c586d0283b feature/IO-3255-simplified-parts-management - Beef Up Change Request Parser, add Change Request documentation data 2025-08-18 12:42:06 -04:00
Dave
203cc1ebdf feature/IO-3255-simplified-parts-management - Modify Parser 2025-08-18 12:03:52 -04:00
Dave
db09f33e5c feature/IO-3255-simplified-parts-management - Add PAO in for isPartsEntry 2025-08-18 11:47:34 -04:00
Dave
1941034dcb feature/IO-3255-simplified-parts-management - Record Labor Again 2025-08-18 11:02:03 -04:00
Dave
6bd2828176 Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management 2025-08-18 10:41:05 -04:00
Dave Richer
6f19c1dd3f Merged in release/2025-08-15 (pull request #2478)
Release/2025-08-15 into master-AIO - IO-1113, IO-3285, IO-3307, IO-3330, IO-3332, IO-3335
2025-08-16 01:13:04 +00:00
Dave
b428a1078c feature/IO-3255-simplified-parts-management - Bug Fixes 2025-08-15 13:38:05 -04:00
Dave
cc232eac93 feature/IO-3255-simplified-parts-management - Bug Fixes 2025-08-15 13:20:17 -04:00
Dave
5e90504e56 feature/IO-3255-simplified-parts-management - Bug Fixes 2025-08-15 13:07:32 -04:00
Dave
73e103f2df feature/IO-3255-simplified-parts-management - Bug Fixes 2025-08-15 12:25:06 -04:00
Dave
b09b8b4f34 feature/IO-3255-simplified-parts-management - Cleanup 2025-08-14 22:50:59 -04:00
Dave
9dd34c9f6c feature/IO-3255-simplified-parts-management - Bump Packages 2025-08-14 22:44:52 -04:00
Dave
cf60f7cd03 Merge remote-tracking branch 'origin/release/2025-08-15' into feature/IO-3255-simplified-part-management 2025-08-14 15:01:38 -04:00
Allan Carr
637e95c351 Merged in feature/IO-3330-CARFAX-Datapump (pull request #2474)
Feature/IO-3330 CARFAX Datapump

Approved-by: Dave Richer
2025-08-14 18:59:34 +00:00
Dave
7af7f3c4e7 feature/IO-3255-simplified-parts-management - vehicleDamageEstimateAddRq.js enhancements 2025-08-14 14:56:02 -04:00
Allan Carr
0cadf007b5 IO-3330 CARFAX Datapump
Prep for express upgrade with return

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-14 11:30:04 -07:00
Dave
1394176218 Merge branch 'feature/IO-3255-simplified-part-management' of bitbucket.org:snaptsoft/bodyshop into feature/IO-3255-simplified-part-management 2025-08-14 13:30:27 -04:00
Dave
284d25eeb9 feature/IO-3255-simplified-parts-management Bug Fixes 2025-08-14 13:29:49 -04:00
Allan Carr
60258a0f5d IO-3330 CARFAX Datapump
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-14 09:17:02 -07:00
Allan Carr
7873405a30 IO-3330 CARFAX Datapump
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-14 08:44:10 -07:00
Dave Richer
c38d7d9aea Merged in release/2025-08-15 (pull request #2472)
Release/2025 08 15
2025-08-14 14:26:36 +00:00
Allan Carr
7639655911 Merged in feature/IO-3330-CARFAX-Datapump (pull request #2471)
IO-3330 CARFAX Datapump

Approved-by: Dave Richer
2025-08-14 14:26:03 +00:00
Allan Carr
4fb1871044 Merged in feature/IO-3335-QBO-Payment-Logging (pull request #2470)
IO-3335 QBO Payment Logging

Approved-by: Dave Richer
2025-08-14 14:25:15 +00:00
Allan Carr
e5dd1edf13 IO-3330 CARFAX Datapump
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-13 21:49:55 -07:00
Allan Carr
542c95c395 IO-3335 QBO Payment Logging
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-13 16:35:52 -07:00
Dave
a3122a59b1 feature/IO-3255-simplified-parts-management - centralize alerts 2025-08-13 18:22:12 -04:00
Dave
81d642fcd3 feature/IO-3255-simplified-parts-management - dumb down print center 2025-08-13 18:10:57 -04:00
Dave
960a2ccd30 feature/IO-3255-simplified-parts-management - Add Dark mode 2025-08-13 17:39:05 -04:00
Dave
2ab6093bd8 feature/IO-3255-simplified-parts-management - Add Dark mode 2025-08-13 17:31:33 -04:00
Dave
7ed7b6117f feature/IO-3255-simplified-parts-management - Cleanup 2025-08-13 17:24:46 -04:00
Dave
7158676562 feature/IO-3255-simplified-parts-management - Cleanup 2025-08-13 17:04:48 -04:00
Dave
67a8c13bad feature/IO-3255-simplified-parts-management - Favor isPartsEntry over isPartsManagementOnly 2025-08-13 16:52:08 -04:00
Dave
766b4b950a feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-13 16:31:04 -04:00
Dave
88ba8ab929 feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-13 16:20:10 -04:00
Dave
0d570d0323 feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-13 15:48:09 -04:00
Dave
898b97151f feature/IO-3255-simplified-parts-management - Checkpoint 2025-08-13 15:05:57 -04:00
Dave Richer
de6bb3d634 Merge remote-tracking branch 'origin/release/2025-08-15' into feature/IO-3255-simplified-part-management 2025-08-12 13:51:59 -04:00
Patrick Fic
2b40793c77 Merged in feature/IO-3322-intellipay-refund (pull request #2465)
IO-3332 Add error message to intellipay refund error.
2025-08-11 19:29:59 +00:00
Dave Richer
c4649b2fc6 Merge remote-tracking branch 'origin/release/2025-08-15' into feature/IO-3255-simplified-part-management 2025-08-11 14:28:19 -04:00
Dave Richer
4d475e25fa release/2025-08-15 - Add LogImexEvent for Theme toggling / remove Tooltip with translations (no longer necessary) 2025-08-11 14:27:48 -04:00
Dave Richer
00d2d3012d Merge remote-tracking branch 'origin/release/2025-08-15' into feature/IO-3255-simplified-part-management 2025-08-11 13:50:41 -04:00
Allan Carr
4e5aba59d7 Merged in feature/IO-3285-Shop-Config-Lite-Basic (pull request #2456)
IO-3285 Shop Config Lite-Basic

Approved-by: Dave Richer
2025-08-11 17:50:02 +00:00
Patrick Fic
09f96f0b68 Merged in feature/IO-3307-imgproxy-bill-route (pull request #2455)
IO-3307 Resolve bill document for imgproxy.

Approved-by: Dave Richer
2025-08-11 17:46:03 +00:00
Dave Richer
866f5f72eb Merged in release/2025-08-15 (pull request #2461)
feature/IO-1113-Online-Dark-Mode - Adjust Car SVG Background color in Dark mode
2025-08-08 16:51:48 +00:00
Dave Richer
f0c166907b Merged in feature/IO-1113-Online-Dark-Mode (pull request #2460)
feature/IO-1113-Online-Dark-Mode - Adjust Car SVG Background color in Dark mode
2025-08-08 16:51:26 +00:00
Dave Richer
c06b4e8af5 feature/IO-1113-Online-Dark-Mode - Adjust Car SVG Background color in Dark mode 2025-08-08 12:51:00 -04:00
Dave Richer
bebe3ef633 Merge branch 'feature/IO-3255-simplified-part-management' of bitbucket.org:snaptsoft/bodyshop into feature/IO-3255-simplified-part-management 2025-08-08 12:24:30 -04:00
Dave Richer
50d2e912ed Merge remote-tracking branch 'origin/release/2025-08-15' into feature/IO-3255-simplified-part-management, resolve conflicts 2025-08-08 12:24:13 -04:00
Dave Richer
a7e21b0505 Merged in feature/IO-1113-Online-Dark-Mode (pull request #2458)
Feature/IO-1113 Online Dark Mode
2025-08-08 16:20:48 +00:00
Dave Richer
3b481afa9e feature/IO-1113-Online-Dark-Mode - Finish 2025-08-08 12:14:11 -04:00
Patrick Fic
75de177b7b IO-3332 Add error message to intellipay refund error. 2025-08-08 09:11:48 -07:00
Dave Richer
ec6c0279de feature/IO-1113-Online-Dark-Mode - Toggle / Local storage solution 2025-08-08 11:53:51 -04:00
Dave Richer
c9572d2db5 feature/IO-1113-Online-Dark-Mode - Remove unnecessary forward ref 2025-08-08 10:38:47 -04:00
Dave Richer
93e9e20f6f feature/IO-1113-Online-Dark-Mode - Initial Commit 2025-08-08 10:23:09 -04:00
Allan Carr
4e8ea736c5 IO-3285 Shop Config Lite-Basic
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-07 20:45:04 -07:00
Patrick Fic
8f00dbfc17 IO-3307 Resolve bill document for imgproxy. 2025-08-07 13:54:39 -07:00
Dave Richer
55d242d40d Merged in hotfix/2025-08-07 (pull request #2454)
IO-3322 IntelliPay Refund
2025-08-07 17:58:49 +00:00
Allan Carr
4f99ae40d3 Merged in feature/IO-3322-IntelliPay-Refund (pull request #2453)
IO-3322 IntelliPay Refund

Approved-by: Dave Richer
2025-08-07 17:28:21 +00:00
Dave Richer
05c049c9af Merged in release/2025-08-15 (pull request #2451)
IO-3322 IntelliPay Refund
2025-08-07 15:54:23 +00:00
Allan Carr
d94b573ae6 Merged in feature/IO-3322-IntelliPay-Refund (pull request #2450)
IO-3322 IntelliPay Refund

Approved-by: Dave Richer
2025-08-07 15:54:03 +00:00
Allan Carr
790ab0447f IO-3322 IntelliPay Refund
Add logging to capture Response from IntelliPay API on success

Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-08-06 16:30:56 -07:00
Dave Richer
84795b2048 feature/IO-3255-simplified-parts-management - Fix top level breadcrumbs, up deps 2025-08-06 13:34:00 -04:00
Dave Richer
567002236d feature/IO-3255-simplified-parts-management - Packages / Housekeeping 2025-08-06 11:58:50 -04:00
Dave Richer
0ed41de956 feature/IO-3255-simplified-parts-management - Add Shop / Vendor Configuration 2025-08-05 16:04:07 -04:00
Dave Richer
8a2dfae487 Merge branch 'master-AIO' into feature/IO-3255-simplified-part-management 2025-08-05 09:23:30 -04:00
Dave Richer
3737fe457f Merged in release/2025-08-01 (pull request #2448)
Release/2025 08 01 into master-AIO - IO-2604, IO-3292, IO-3310, IO-3315, IO-3316, IO-3318, IO-3319, IO-3320
2025-08-02 01:21:40 +00:00
Dave Richer
c5978d4c21 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-31 15:51:49 -04:00
Allan Carr
bb4e8eb5bd Merged in feature/IO-3320-Responsibility-Center-RBAC (pull request #2446)
IO-3320 Responsibility Center RBAC

Approved-by: Dave Richer
2025-07-31 19:51:38 +00:00
Allan Carr
27a07e8d5d IO-3320 Responsibility Center RBAC
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-31 12:49:56 -07:00
Dave Richer
e52fe93e14 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-31 15:48:06 -04:00
Allan Carr
e2618eee83 Merged in feature/IO-2604-vendor-tagging (pull request #2444)
IO-2604 Vendor Tagging English Translation

Approved-by: Dave Richer
2025-07-31 19:47:53 +00:00
Dave Richer
dd20871707 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-31 15:47:07 -04:00
Dave Richer
66c51a4be5 release/2025-08-01 - Fix Notes via refetch queries, cache manipulation too complex 2025-07-31 15:46:34 -04:00
Allan Carr
d5afcaeaab IO-2604 Vendor Tagging English Translation
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-31 11:35:02 -07:00
Dave Richer
d0370d3e60 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-30 14:14:11 -04:00
Allan Carr
c332ec11b7 Merged in feature/IO-3319-Job-Status-Change (pull request #2442)
IO-3319 Job Status Change

Approved-by: Dave Richer
2025-07-30 18:13:59 +00:00
Allan Carr
cf31290f05 IO-3319 Job Status Change
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-30 11:12:15 -07:00
Dave Richer
203dc28720 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-30 13:10:59 -04:00
Allan Carr
dbbab910b6 Merged in feature/IO-3318-Close-Job-Page-UI-Bugs (pull request #2440)
IO-3318 Close Job Page UI Bugs

Approved-by: Dave Richer
2025-07-30 17:10:40 +00:00
Allan Carr
abf01b4966 IO-3318 Close Job Page UI Bugs
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-29 19:19:38 -07:00
Dave Richer
952feeb685 feature/IO-3255-simplified-parts-management - Adjust routes / Deps 2025-07-29 14:37:51 -04:00
Dave Richer
bb2b67cece Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-29 13:25:03 -04:00
Allan Carr
a965f9edf5 Merged in feature/IO-3315-CDK-InService-Date (pull request #2437)
IO-3315 CDK InService Date

Approved-by: Dave Richer
2025-07-29 17:24:51 +00:00
Allan Carr
f02ca05eba Merged in feature/IO-3316-Local-Storage-Sort (pull request #2436)
IO-3316 Local Storage Sort

Approved-by: Dave Richer
2025-07-29 17:23:26 +00:00
Allan Carr
a182ea0869 IO-3315 CDK InService Date
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-29 10:04:13 -07:00
Dave Richer
e4fb8b61b0 feature/IO-3255-simplified-parts-management - Deprovisoning route 2025-07-25 12:26:36 -04:00
Dave Richer
1cc33a67e6 Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-25 11:25:35 -04:00
Allan Carr
5277e90946 Merged in feature/IO-3310-Shop-Data-Preservation (pull request #2433)
IO-3310 Shop Data Preservation on Hidden Fields

Approved-by: Dave Richer
2025-07-24 17:50:14 +00:00
Dave Richer
0a16a0fcbc Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-23 10:18:36 -04:00
Allan Carr
15ea4e6afa IO-3310 Shop Data Preservation on Hidden Fields
Signed-off-by: Allan Carr <allan@imexsystems.ca>
2025-07-22 17:40:51 -07:00
Dave Richer
295f1d1cb3 Merge branch 'hotfix/2025-07-22-SocketProvider' into feature/IO-3255-simplified-part-management 2025-07-22 20:17:13 -04:00
Dave Richer
5b3b6a409c Merged in hotfix/2025-07-22-SocketProvider (pull request #2431)
Hotfix/2025 07 22 SocketProvider
2025-07-23 00:16:13 +00:00
Dave Richer
37ceddf54d Merge remote-tracking branch 'origin/release/2025-08-01' into feature/IO-3255-simplified-part-management 2025-07-22 14:56:22 -04:00
Patrick Fic
736e9cedfa Merged in feature/IO-2604-vendor-tagging (pull request #2428)
IO-2604 Add vendor tags to search select & vendor edit screen.

Approved-by: Dave Richer
2025-07-22 18:09:57 +00:00
Patrick Fic
c433103e1b IO-2604 Merge in conflicted translations file. 2025-07-22 10:25:04 -07:00
Patrick Fic
2892fdbb58 IO-2604 reformat translations to resolve conflict. 2025-07-22 10:17:10 -07:00
Patrick Fic
c45f38e47b IO-2604 Add vendor tags to search select & vendor edit screen. 2025-07-22 10:03:07 -07:00
Dave Richer
6f4d21cac9 feature/IO-3255-simplified-parts-management - Merge release 2025-07-22 12:22:52 -04:00
Dave Richer
6b5ad3dafa feature/IO-3255-simplified-parts-management -Checkpoint 2025-07-22 12:20:59 -04:00
Patrick Fic
54a58c9fbc Merged in feature/IO-3292-pinned-notes (pull request #2427)
IO-3292 Add note pinning functionality.

Approved-by: Dave Richer
2025-07-22 16:13:51 +00:00
Patrick Fic
1934ae0758 IO-3292 Add note pinning functionality. 2025-07-22 09:03:41 -07:00
Dave Richer
0ac9bbd97c feature/IO-3255-simplified-parts-management -Checkpoint 2025-07-21 15:28:03 -04:00
Dave Richer
dfa457e3c6 feature/IO-3255-simplified-parts-management -Checkpoint 2025-07-21 15:05:15 -04:00
Dave Richer
e8dec042bd feature/IO-3255-simplified-parts-management - Merge Master / Bump Packages 2025-07-21 13:04:30 -04:00
Dave Richer
d65e7deacc Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management 2025-07-21 12:59:13 -04:00
Dave Richer
139ee46dc0 feature/IO-3255-simplified-parts-management - Merge release / resolve conflicts 2025-07-18 11:26:51 -04:00
Dave Richer
7a1e7bd4f9 feature/IO-3255-simplified-parts-management - Expand Joblines Parser, remove body-parser reference in favor of express.raw 2025-07-17 13:31:02 -04:00
Dave Richer
254fb4f77f Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2025-07-17 12:49:03 -04:00
Dave Richer
53a559b126 feature/IO-3255-simplified-parts-management - Checkpoint 2025-07-17 12:48:30 -04:00
Dave Richer
931fa4b82b Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management 2025-07-09 11:15:00 -04:00
Dave Richer
91cc12873e feature/IO-3255-simplified-parts-management -Cleanup / Docblocks 2025-07-07 13:19:19 -04:00
Dave Richer
c71026f22a feature/IO-3255-simplified-parts-management - Sample data modified / added 2025-07-07 13:12:47 -04:00
Dave Richer
bd2720f534 feature/IO-3255-simplified-parts-management - Refactor / Working Change Request 2025-07-07 13:09:55 -04:00
Dave Richer
8bc6bea4b2 feature/IO-3255-simplified-parts-management - Bump deps, add Change Request 2025-07-07 12:14:23 -04:00
Dave Richer
0891c7d4b3 Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management
# Conflicts:
#	client/src/components/jobs-detail-header/jobs-detail-header.component.jsx
2025-07-07 11:18:16 -04:00
Dave Richer
94b154a4ac feature/IO-3255-simplified-parts-management - Checkpoint / Meeting changes 2025-07-03 14:05:35 -04:00
Dave Richer
5fa58e5013 feature/IO-3255-simplified-parts-management - Front End Cleanup 2025-07-03 12:00:21 -04:00
Dave Richer
6496f6c414 feature/IO-3255-simplified-parts-management - Bump deps 2025-07-03 11:52:01 -04:00
Patrick Fic
3a0a3c9fb9 Merge branch 'feature/IO-3255-simplified-part-management' of bitbucket.org:snaptsoft/bodyshop into feature/IO-3255-simplified-part-management 2025-07-02 11:06:29 -07:00
Dave Richer
646c42b8c7 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-27 13:48:49 -04:00
Dave Richer
8de92403ee feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-27 13:01:18 -04:00
Dave Richer
f5ea8719ef Merge branch 'master-AIO' into feature/IO-3255-simplified-part-management 2025-06-26 14:17:04 -04:00
Patrick Fic
0e1ec83fcd IO-3255 Fix breadcrumb path. 2025-06-25 08:15:37 -07:00
Dave Richer
01185b3073 feature/IO-3182-Phone-Number-Consent - Checkpoint 2025-06-24 16:22:07 -04:00
Dave Richer
49b0990c7b Merge remote-tracking branch 'origin/master-AIO' into feature/IO-3255-simplified-part-management 2025-06-24 14:45:04 -04:00
Dave Richer
7d930045ef feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-24 14:38:13 -04:00
Patrick Fic
cbb6c43ec3 IO-3255 Clean up front end components for pm. 2025-06-23 14:00:25 -07:00
Dave Richer
09e1887609 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-23 14:24:15 -04:00
Dave Richer
4b83330db9 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-23 14:00:58 -04:00
Dave Richer
b0283f827e feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-20 14:51:30 -04:00
Dave Richer
3c71902047 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-20 13:48:58 -04:00
Dave Richer
79bf30b299 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-20 13:30:49 -04:00
Dave Richer
dc3e9b7226 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-20 13:25:48 -04:00
Dave Richer
2505edede7 feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-19 13:08:48 -04:00
Dave Richer
4b75504d9e feature/IO-3255-simplified-parts-management - Checkpoint 2025-06-18 11:22:23 -04:00
Dave Richer
8af6c8dd24 feature/IO-3255-simplified-parts-management 2025-06-12 12:05:40 -04:00
Dave Richer
4c6344a8d7 Merge remote-tracking branch 'origin/release/2025-06-13' into feature/IO-3255-simplified-part-management 2025-06-12 11:49:28 -04:00
Patrick Fic
92369fceba IO-3255 Initial parts management changes. 2025-06-11 10:29:58 -07:00
913 changed files with 26181 additions and 16721 deletions

View File

@@ -11,7 +11,6 @@ node_modules
# Files to exclude
.ebignore
.editorconfig
.eslintrc.json
.gitignore
.prettierrc.js
Dockerfile
@@ -19,6 +18,6 @@ README.MD
bodyshop_translations.babel
docker-compose.yml
ecosystem.config.js
eslint.config.mjs
# Optional: Exclude logs and temporary files
*.log

View File

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

View File

@@ -1,116 +1,96 @@
// index.js
import express from 'express';
import fetch from 'node-fetch';
import {simpleParser} from 'mailparser';
import express from "express";
import fetch from "node-fetch";
import { simpleParser } from "mailparser";
const app = express();
const PORT = 3334;
app.get('/', async (req, res) => {
try {
const response = await fetch('http://localhost:4566/_aws/ses');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
const messagesHtml = await parseMessages(data.messages);
res.send(renderHtml(messagesHtml));
} catch (error) {
console.error('Error fetching messages:', error);
res.status(500).send('Error fetching messages');
app.get("/", async (req, res) => {
try {
const response = await fetch("http://localhost:4566/_aws/ses");
if (!response.ok) {
throw new Error("Network response was not ok");
}
const data = await response.json();
const messagesHtml = await parseMessages(data.messages);
res.send(renderHtml(messagesHtml));
} catch (error) {
console.error("Error fetching messages:", error);
res.status(500).send("Error fetching messages");
}
});
async function parseMessages(messages) {
const parsedMessages = await Promise.all(
messages.map(async (message, index) => {
try {
const parsed = await simpleParser(message.RawData);
return `
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: lightgray">
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: white">
<div class="mb-2">
<span class="font-bold text-lg">Message ${index + 1}</span>
</div>
<div class="mb-2">
<span class="font-semibold">From:</span> ${message.Source}
</div>
<div class="mb-2">
<span class="font-semibold">Region:</span> ${message.Region}
</div>
<div class="mb-2">
<span class="font-semibold">Timestamp:</span> ${message.Timestamp}
</div>
</div>
<div class="prose">
${parsed.html || parsed.textAsHtml || 'No HTML content available'}
</div>
</div>
`;
} catch (error) {
console.error('Error parsing email:', error);
return `
<div class="bg-white shadow-md rounded-lg p-4 mb-6">
<div class="mb-2">
<span class="font-bold text-lg">Message ${index + 1}</span>
</div>
<div class="mb-2">
<span class="font-semibold">From:</span> ${message.Source}
</div>
<div class="mb-2">
<span class="font-semibold">Region:</span> ${message.Region}
</div>
<div class="mb-2">
<span class="font-semibold">Timestamp:</span> ${message.Timestamp}
</div>
<div class="text-red-500">
Error parsing email content
</div>
</div>
`;
}
})
);
return parsedMessages.join('');
const parsedMessages = await Promise.all(
messages.map(async (message, index) => {
try {
const parsed = await simpleParser(message.RawData);
return `
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: lightgray">
<div class="shadow-md rounded-lg p-4 mb-6" style="background-color: white">
<div class="mb-2"><span class="font-bold text-lg">Message ${index + 1}</span></div>
<div class="mb-2"><span class="font-semibold">From:</span> ${message.Source}</div>
<div class="mb-2"><span class="font-semibold">To:</span> ${parsed.to.text || "No To Address"}</div>
<div class="mb-2"><span class="font-semibold">Subject:</span> ${parsed.subject || "No Subject"}</div>
<div class="mb-2"><span class="font-semibold">Region:</span> ${message.Region}</div>
<div class="mb-2"><span class="font-semibold">Timestamp:</span> ${message.Timestamp}</div>
</div>
<div class="prose">${parsed.html || parsed.textAsHtml || "No HTML content available"}</div>
</div>
`;
} catch (error) {
console.error("Error parsing email:", error);
return `
<div class="bg-white shadow-md rounded-lg p-4 mb-6">
<div class="mb-2"><span class="font-bold text-lg">Message ${index + 1}</span></div>
<div class="mb-2"><span class="font-semibold">From:</span> ${message.Source}</div>
<div class="mb-2"><span class="font-semibold">Region:</span> ${message.Region}</div>
<div class="mb-2"><span class="font-semibold">Timestamp:</span> ${message.Timestamp}</div>
<div class="text-red-500">Error parsing email content</div>
</div>
`;
}
})
);
return parsedMessages.join("");
}
function renderHtml(messagesHtml) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Messages Viewer</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background-color: #f3f4f6;
font-family: Arial, sans-serif;
}
.container {
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.prose {
line-height: 1.6;
}
</style>
</head>
<body>
<div class="container bg-white shadow-lg rounded-lg p-6">
<h1 class="text-2xl font-bold text-center mb-6">Email Messages Viewer</h1>
<div id="messages-container">
${messagesHtml}
</div>
</div>
</body>
</html>
`;
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Messages Viewer</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background-color: #f3f4f6;
font-family: Arial, sans-serif;
}
.container {
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.prose {
line-height: 1.6;
}
</style>
</head>
<body>
<div class="container bg-white shadow-lg rounded-lg p-6">
<h1 class="text-2xl font-bold text-center mb-6">Email Messages Viewer</h1>
<div id="messages-container">${messagesHtml}</div>
</div>
</body>
</html>
`;
}
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
console.log(`Server is running on http://localhost:${PORT}`);
});

View File

@@ -10,7 +10,7 @@
"license": "ISC",
"dependencies": {
"express": "^5.1.0",
"mailparser": "^3.7.2",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"
}
},
@@ -634,9 +634,9 @@
"license": "MIT"
},
"node_modules/libmime": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.6.tgz",
"integrity": "sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA==",
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz",
"integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==",
"license": "MIT",
"dependencies": {
"encoding-japanese": "2.2.0",
@@ -661,31 +661,31 @@
}
},
"node_modules/mailparser": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.2.tgz",
"integrity": "sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q==",
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.4.tgz",
"integrity": "sha512-Beh4yyR4jLq3CZZ32asajByrXnW8dLyKCAQD3WvtTiBnMtFWhxO+wa93F6sJNjDmfjxXs4NRNjw3XAGLqZR3Vg==",
"license": "MIT",
"dependencies": {
"encoding-japanese": "2.2.0",
"he": "1.2.0",
"html-to-text": "9.0.5",
"iconv-lite": "0.6.3",
"libmime": "5.3.6",
"libmime": "5.3.7",
"linkify-it": "5.0.0",
"mailsplit": "5.4.2",
"nodemailer": "6.9.16",
"mailsplit": "5.4.5",
"nodemailer": "7.0.4",
"punycode.js": "2.3.1",
"tlds": "1.255.0"
"tlds": "1.259.0"
}
},
"node_modules/mailsplit": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.2.tgz",
"integrity": "sha512-4cczG/3Iu3pyl8JgQ76dKkisurZTmxMrA4dj/e8d2jKYcFTZ7MxOzg1gTioTDMPuFXwTrVuN/gxhkrO7wLg7qA==",
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.5.tgz",
"integrity": "sha512-oMfhmvclR689IIaQmIcR5nODnZRRVwAKtqFT407TIvmhX2OLUBnshUTcxzQBt3+96sZVDud9NfSe1NxAkUNXEQ==",
"license": "(MIT OR EUPL-1.1+)",
"dependencies": {
"libbase64": "1.3.0",
"libmime": "5.3.6",
"libmime": "5.3.7",
"libqp": "2.1.1"
}
},
@@ -793,9 +793,9 @@
}
},
"node_modules/nodemailer": {
"version": "6.9.16",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.16.tgz",
"integrity": "sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==",
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.4.tgz",
"integrity": "sha512-9O00Vh89/Ld2EcVCqJ/etd7u20UhME0f/NToPfArwPEe1Don1zy4mAIz6ariRr7mJ2RDxtaDzN0WJVdVXPtZaw==",
"license": "MIT-0",
"engines": {
"node": ">=6.0.0"
@@ -1114,9 +1114,9 @@
}
},
"node_modules/tlds": {
"version": "1.255.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz",
"integrity": "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==",
"version": "1.259.0",
"resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz",
"integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==",
"license": "MIT",
"bin": {
"tlds": "bin.js"

View File

@@ -12,7 +12,7 @@
"description": "",
"dependencies": {
"express": "^5.1.0",
"mailparser": "^3.7.2",
"mailparser": "^3.7.4",
"node-fetch": "^3.3.2"
}
}

View File

@@ -0,0 +1,61 @@
# PATCH /integrations/parts-management/job/:id/status
Update (patch) the status of a job created under parts management. This endpoint is only available
for jobs whose parent bodyshop has an `external_shop_id` (i.e., is provisioned for parts
management).
## Endpoint
```
PATCH /integrations/parts-management/job/:id/status
```
- `:id` is the UUID of the job to update.
## Request Headers
- `Authorization`: (if required by your integration middleware)
- `Content-Type: application/json`
## Request Body
Send a JSON object with the following field:
- `status` (string, required): The new status for the job.
Example:
```
PATCH /integrations/parts-management/job/123e4567-e89b-12d3-a456-426614174000/status
Content-Type: application/json
{
"status": "IN_PROGRESS"
}
```
## Success Response
- **200 OK**
- Returns the updated job object with the new status.
```
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"status": "IN_PROGRESS",
...
}
```
## Error Responses
- **400 Bad Request**: Missing status field, or parent bodyshop does not have an `external_shop_id`.
- **404 Not Found**: No job found with the given ID.
- **500 Internal Server Error**: Unexpected error.
## Notes
- Only jobs whose parent bodyshop has an `external_shop_id` can be patched via this route.
- Fields other than `status` will be ignored if included in the request body.
- The route is protected by the same middleware as other parts management endpoints.

View File

@@ -0,0 +1,86 @@
# PATCH /integrations/parts-management/provision/:id
Update (patch) select fields for a parts management bodyshop. Only available for shops that have an
`external_shop_id` (i.e., are provisioned for parts management).
## Endpoint
```
PATCH /integrations/parts-management/provision/:id
```
- `:id` is the UUID of the bodyshop to update.
## Request Headers
- `Authorization`: (if required by your integration middleware)
- `Content-Type: application/json`
## Request Body
Send a JSON object with one or more of the following fields to update:
- `shopname` (string)
- `address1` (string)
- `address2` (string, optional)
- `city` (string)
- `state` (string)
- `zip_post` (string)
- `country` (string)
- `email` (string, shop's email, not user email)
- `timezone` (string)
- `phone` (string)
- `logo_img_path` (object, e.g. `{ src, width, height, headerMargin }`)
Any fields not included in the request body will remain unchanged.
## Example Request
```
PATCH /integrations/parts-management/provision/123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json
{
"shopname": "New Shop Name",
"address1": "123 Main St",
"city": "Springfield",
"state": "IL",
"zip_post": "62704",
"country": "USA",
"email": "shop@example.com",
"timezone": "America/Chicago",
"phone": "555-123-4567",
"logo_img_path": {
"src": "https://example.com/logo.png",
"width": "200",
"height": "100",
"headerMargin": 10
}
}
```
## Success Response
- **200 OK**
- Returns the updated shop object with the patched fields.
```
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"shopname": "New Shop Name",
...
}
```
## Error Responses
- **400 Bad Request**: No valid fields provided, or shop does not have an `external_shop_id`.
- **404 Not Found**: No shop found with the given ID.
- **500 Internal Server Error**: Unexpected error.
## Notes
- Only shops with an `external_shop_id` can be patched via this route.
- Fields not listed above will be ignored if included in the request body.
- The route is protected by the same middleware as other parts management endpoints.

View File

@@ -54,6 +54,10 @@ paths:
userEmail:
type: string
format: email
userPassword:
type: string
description: Optional password for the new user. If provided, the password is set directly, and no password reset link is sent. Must be at least 6 characters.
nullable: true
logoUrl:
type: string
format: uri
@@ -140,6 +144,8 @@ paths:
resetLink:
type: string
format: uri
nullable: true
description: Password reset link for the user. Only included if userPassword is not provided in the request.
'400':
description: Bad request (missing or invalid fields)
content:

View File

@@ -0,0 +1,10 @@
services:
ragmate:
image: ghcr.io/ragmate/ragmate:latest
ports:
- "11434:11434"
env_file:
- .ragmate.env
volumes:
- .:/project
- ./docker_data/ragmate:/apps/cache

File diff suppressed because it is too large Load Diff

View File

@@ -14,3 +14,7 @@ VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=IMEX
TEST_USERNAME="test@imex.dev"
TEST_PASSWORD="test123"
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=6228a598e57cd66875cfd41604f1f891

View File

@@ -16,3 +16,7 @@ VITE_APP_COUNTRY=USA
VITE_APP_INSTANCE=ROME
TEST_USERNAME="test@imex.dev"
TEST_PASSWORD="test123"
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=46b1193a867d4e3131ae4c3a64a3fc78

View File

@@ -13,3 +13,7 @@ VITE_APP_AXIOS_BASE_API_URL=https://api.imex.online/
VITE_APP_REPORTS_SERVER_URL=https://reports.imex.online
VITE_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
VITE_APP_INSTANCE=IMEX
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=6228a598e57cd66875cfd41604f1f891

View File

@@ -13,3 +13,7 @@ VITE_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/
VITE_APP_REPORTS_SERVER_URL=https://reports.romeonline.io
VITE_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk
VITE_APP_INSTANCE=ROME
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=46b1193a867d4e3131ae4c3a64a3fc78

View File

@@ -13,3 +13,7 @@ VITE_APP_REPORTS_SERVER_URL=https://reports.test.imex.online
VITE_APP_IS_TEST=true
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=IMEX
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=6228a598e57cd66875cfd41604f1f891

View File

@@ -13,3 +13,7 @@ VITE_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
VITE_APP_IS_TEST=true
VITE_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
VITE_APP_INSTANCE=ROME
VITE_PUBLIC_POSTHOG_KEY=phc_xtLmBIu0rjWwExY73Oj5DTH1bGbwq1G1Y8jnlTceien
VITE_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
VITE_APP_AMP_URL=https://vp8k908qy2.execute-api.ca-central-1.amazonaws.com
VITE_APP_AMP_KEY=46b1193a867d4e3131ae4c3a64a3fc78

View File

@@ -1,8 +0,0 @@
{
"extends": [
"react-app"
],
"rules": {
"no-useless-rename": "off"
}
}

View File

@@ -2,9 +2,9 @@ import globals from "globals";
import pluginJs from "@eslint/js";
import pluginReact from "eslint-plugin-react";
/** @type {import('eslint').Linter.Config[]} */
/** @type {import("eslint").Linter.Config[]} */
export default [
{ ignores: ["node_modules/**", "dist/**", "build/**", "dev-dist/**"] },
{
files: ["**/*.{js,mjs,cjs,jsx}"]
},
@@ -12,9 +12,13 @@ export default [
pluginJs.configs.recommended,
{
...pluginReact.configs.flat.recommended,
settings: {
react: { version: "detect" }
},
rules: {
...pluginReact.configs.flat.recommended.rules,
"react/prop-types": 0
"react/prop-types": 0,
"react/no-children-prop": 0 // Disable react/no-children-prop rule
}
},
pluginReact.configs.flat["jsx-runtime"]

6059
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,58 +8,61 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@ant-design/pro-layout": "^7.22.4",
"@apollo/client": "^3.13.6",
"@emotion/is-prop-valid": "^1.3.1",
"@amplitude/analytics-browser": "^2.25.2",
"@ant-design/pro-layout": "^7.22.6",
"@apollo/client": "^3.13.9",
"@emotion/is-prop-valid": "^1.4.0",
"@fingerprintjs/fingerprintjs": "^4.6.1",
"@firebase/analytics": "^0.10.16",
"@firebase/app": "^0.13.1",
"@firebase/auth": "^1.10.6",
"@firebase/firestore": "^4.7.17",
"@firebase/messaging": "^0.12.21",
"@firebase/analytics": "^0.10.17",
"@firebase/app": "^0.14.3",
"@firebase/auth": "^1.10.8",
"@firebase/firestore": "^4.9.2",
"@firebase/messaging": "^0.12.22",
"@jsreport/browser-client": "^3.1.0",
"@reduxjs/toolkit": "^2.8.2",
"@sentry/cli": "^2.47.1",
"@sentry/react": "^9.38.0",
"@sentry/vite-plugin": "^3.5.0",
"@splitsoftware/splitio-react": "^2.3.1",
"@reduxjs/toolkit": "^2.9.0",
"@sentry/cli": "^2.56.0",
"@sentry/react": "^9.43.0",
"@sentry/vite-plugin": "^4.3.0",
"@splitsoftware/splitio-react": "^2.5.0",
"@tanem/react-nprogress": "^5.0.53",
"antd": "^5.25.4",
"antd": "^5.27.4",
"apollo-link-logger": "^2.0.1",
"apollo-link-sentry": "^4.3.0",
"apollo-link-sentry": "^4.4.0",
"autosize": "^6.0.1",
"axios": "^1.8.4",
"axios": "^1.12.2",
"classnames": "^2.5.1",
"css-box-model": "^1.2.1",
"dayjs": "^1.11.13",
"dayjs": "^1.11.18",
"dayjs-business-days2": "^1.3.0",
"dinero.js": "^1.9.1",
"dotenv": "^16.4.7",
"dotenv": "^17.2.3",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"graphql": "^16.11.0",
"i18next": "^24.2.3",
"i18next-browser-languagedetector": "^8.1.0",
"i18next": "^25.5.3",
"i18next-browser-languagedetector": "^8.2.0",
"immutability-helper": "^3.1.1",
"libphonenumber-js": "^1.12.10",
"libphonenumber-js": "^1.12.23",
"lightningcss": "^1.30.2",
"logrocket": "^9.0.2",
"markerjs2": "^2.32.4",
"markerjs2": "^2.32.7",
"memoize-one": "^6.0.0",
"normalize-url": "^8.0.2",
"normalize-url": "^8.1.0",
"object-hash": "^3.0.0",
"phone": "^3.1.59",
"phone": "^3.1.67",
"posthog-js": "^1.271.0",
"prop-types": "^15.8.1",
"query-string": "^9.2.0",
"query-string": "^9.3.1",
"raf-schd": "^4.0.3",
"react": "^18.3.1",
"react-big-calendar": "^1.19.2",
"react-big-calendar": "^1.19.4",
"react-color": "^2.19.3",
"react-cookie": "^8.0.1",
"react-dom": "^18.3.1",
"react-drag-listview": "^2.0.0",
"react-grid-gallery": "^1.0.1",
"react-grid-layout": "1.3.4",
"react-i18next": "^15.5.2",
"react-i18next": "^15.7.3",
"react-icons": "^5.5.0",
"react-image-lightbox": "^5.1.4",
"react-markdown": "^10.1.0",
@@ -70,7 +73,7 @@
"react-resizable": "^3.0.5",
"react-router-dom": "^6.30.0",
"react-sticky": "^6.0.3",
"react-virtuoso": "^4.12.8",
"react-virtuoso": "^4.14.1",
"recharts": "^2.15.2",
"redux": "^5.0.1",
"redux-actions": "^3.0.3",
@@ -78,9 +81,9 @@
"redux-saga": "^1.3.0",
"redux-state-sync": "^3.1.4",
"reselect": "^5.1.1",
"sass": "^1.89.1",
"sass": "^1.93.2",
"socket.io-client": "^4.8.1",
"styled-components": "^6.1.18",
"styled-components": "^6.1.19",
"subscriptions-transport-ws": "^0.11.0",
"use-memo-one": "^1.1.3",
"vite-plugin-ejs": "^1.7.0",
@@ -107,7 +110,9 @@
"test:e2e:rome": "playwright test --config playwright.rome.config.js",
"test:e2e:imex:headed": "playwright test --config playwright.config.js --headed",
"test:e2e:rome:headed": "playwright test --config playwright.rome.config.js --headed",
"test:e2e:report": "playwright show-report"
"test:e2e:report": "playwright show-report",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"browserslist": {
"production": [
@@ -128,40 +133,39 @@
"@rollup/rollup-linux-x64-gnu": "4.6.1"
},
"devDependencies": {
"@ant-design/icons": "^6.0.0",
"@ant-design/icons": "^6.1.0",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-react": "^7.27.1",
"@dotenvx/dotenvx": "^1.47.5",
"@dotenvx/dotenvx": "^1.51.0",
"@emotion/babel-plugin": "^11.13.5",
"@emotion/react": "^11.14.0",
"@eslint/js": "^9.31.0",
"@playwright/test": "^1.54.1",
"@sentry/webpack-plugin": "^3.5.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@eslint/js": "^9.37.0",
"@playwright/test": "^1.56.0",
"@sentry/webpack-plugin": "^4.3.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.0",
"@vitejs/plugin-react": "^4.5.1",
"browserslist": "^4.25.0",
"@vitejs/plugin-react": "^4.6.0",
"browserslist": "^4.26.3",
"browserslist-to-esbuild": "^2.1.1",
"chalk": "^5.4.1",
"eslint": "^8.57.1",
"eslint-config-react-app": "^7.0.1",
"chalk": "^5.6.2",
"eslint": "^9.37.0",
"eslint-plugin-react": "^7.37.5",
"globals": "^15.15.0",
"jsdom": "^26.0.0",
"memfs": "^4.17.2",
"memfs": "^4.48.1",
"os-browserify": "^0.3.0",
"playwright": "^1.54.1",
"playwright": "^1.56.0",
"react-error-overlay": "^6.1.0",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.3",
"vite": "^6.3.5",
"vite-plugin-babel": "^1.3.1",
"vite": "^7.1.9",
"vite-plugin-babel": "^1.3.2",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-node-polyfills": "^0.23.0",
"vite-plugin-pwa": "^1.0.0",
"vite-plugin-node-polyfills": "^0.24.0",
"vite-plugin-pwa": "^1.0.3",
"vite-plugin-style-import": "^2.0.0",
"vitest": "^3.2.3",
"vitest": "^3.2.4",
"workbox-window": "^7.3.0"
}
}

View File

@@ -20,6 +20,7 @@ export default defineConfig({
command: "npm run start:imex",
ignoreHTTPSErrors: true,
url: "https://localhost:3000/health", // Health check endpoint will tell us when the server is ready
// eslint-disable-next-line no-undef
reuseExistingServer: !process.env.CI // Reuse server locally, not in CI
}
});

View File

@@ -20,6 +20,7 @@ export default defineConfig({
command: "npm run start:rome",
ignoreHTTPSErrors: true,
url: "https://localhost:3000/health", // Health check endpoint will tell us when the server is ready
// eslint-disable-next-line no-undef
reuseExistingServer: !process.env.CI // Reuse server locally, not in CI
}
});

View File

@@ -1,5 +1,7 @@
// Scripts for firebase and firebase messaging
// eslint-disable-next-line no-undef
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js");
// eslint-disable-next-line no-undef
importScripts("https://www.gstatic.com/firebasejs/10.14.1/firebase-messaging-compat.js");
// Initialize the Firebase app in the service worker by passing the generated config
@@ -42,13 +44,16 @@ switch (this.location.hostname) {
};
}
// eslint-disable-next-line no-undef
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
// eslint-disable-next-line no-undef
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
// Customize notification here
console.log("[firebase-messaging-sw.js] Received background message ", payload);
// eslint-disable-next-line no-undef
self.registration.showNotification(notificationTitle, notificationOptions);
});

View File

@@ -1,42 +1,106 @@
import { ApolloProvider } from "@apollo/client";
import * as Sentry from "@sentry/react";
import { SplitFactoryProvider, useSplitClient } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import { useEffect } from "react";
import { useEffect, useMemo } from "react";
import { CookiesProvider } from "react-cookie";
import { useTranslation } from "react-i18next";
import { useSelector } from "react-redux";
import { connect, useSelector } from "react-redux";
import { createStructuredSelector } from "reselect";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import { setDarkMode } from "../redux/application/application.actions";
import { selectDarkMode } from "../redux/application/application.selectors";
import { selectCurrentUser } from "../redux/user/user.selectors.js";
import { signOutStart } from "../redux/user/user.actions";
import client from "../utils/GraphQLClient";
import App from "./App";
import * as Sentry from "@sentry/react";
import themeProvider from "./themeProvider";
import { CookiesProvider } from "react-cookie";
import getTheme from "./themeProvider";
// Base Split configuration
const config = {
core: {
authorizationKey: import.meta.env.VITE_APP_SPLIT_API,
key: "anon" // Default key, overridden dynamically by SplitClientProvider
key: "anon"
}
};
// Custom provider to manage the Split client key based on imexshopid from Redux
function SplitClientProvider({ children }) {
const imexshopid = useSelector((state) => state.user.imexshopid); // Access imexshopid from Redux store
const splitClient = useSplitClient({ key: imexshopid || "anon" }); // Use imexshopid or fallback to "anon"
const imexshopid = useSelector((state) => state.user.imexshopid);
const splitClient = useSplitClient({ key: imexshopid || "anon" });
useEffect(() => {
if (splitClient && imexshopid) {
// Log readiness for debugging; no need for ready() since isReady is available
console.log(`Split client initialized with key: ${imexshopid}, isReady: ${splitClient.isReady}`);
}
}, [splitClient, imexshopid]);
return children;
}
function AppContainer() {
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
setDarkMode: (isDarkMode) => dispatch(setDarkMode(isDarkMode)),
signOutStart: () => dispatch(signOutStart())
});
function AppContainer({ currentUser, setDarkMode, signOutStart }) {
const { t } = useTranslation();
const isDarkMode = useSelector(selectDarkMode);
const theme = useMemo(() => getTheme(isDarkMode), [isDarkMode]);
// Global seamless logout listener with redirect to /signin
useEffect(() => {
const handleSeamlessLogout = (event) => {
if (event.data?.type !== "seamlessLogoutRequest") return;
const requestOrigin = event.origin;
if (currentUser?.authorized !== true) {
window.parent.postMessage(
{ type: "seamlessLogoutResponse", status: "already_logged_out" },
requestOrigin || "*"
);
return;
}
signOutStart();
window.parent.postMessage({ type: "seamlessLogoutResponse", status: "logged_out" }, requestOrigin || "*");
};
window.addEventListener("message", handleSeamlessLogout);
return () => {
window.removeEventListener("message", handleSeamlessLogout);
};
}, [signOutStart, currentUser]);
// Update data-theme attribute
useEffect(() => {
document.documentElement.setAttribute("data-theme", isDarkMode ? "dark" : "light");
return () => document.documentElement.removeAttribute("data-theme");
}, [isDarkMode]);
// Sync darkMode with localStorage
useEffect(() => {
if (currentUser?.uid) {
const savedMode = localStorage.getItem(`dark-mode-${currentUser.uid}`);
if (savedMode !== null) {
setDarkMode(JSON.parse(savedMode));
} else {
setDarkMode(false);
}
} else {
setDarkMode(false);
}
}, [currentUser?.uid, setDarkMode]);
// Persist darkMode
useEffect(() => {
if (currentUser?.uid) {
localStorage.setItem(`dark-mode-${currentUser.uid}`, JSON.stringify(isDarkMode));
}
}, [isDarkMode, currentUser?.uid]);
return (
<CookiesProvider>
@@ -44,10 +108,9 @@ function AppContainer() {
<ConfigProvider
input={{ autoComplete: "new-password" }}
locale={enLocale}
theme={themeProvider}
theme={theme}
form={{
validateMessages: {
// eslint-disable-next-line no-template-curly-in-string
required: t("general.validation.required", { label: "${label}" })
}
}}
@@ -64,4 +127,4 @@ function AppContainer() {
);
}
export default Sentry.withProfiler(AppContainer);
export default Sentry.withProfiler(connect(mapStateToProps, mapDispatchToProps)(AppContainer));

View File

@@ -7,13 +7,14 @@ import { connect } from "react-redux";
import { Route, Routes, useNavigate } 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 ErrorBoundary from "../components/error-boundary/error-boundary.component";
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import LandingPage from "../pages/landing/landing.page";
import TechPageContainer from "../pages/tech/tech.page.container";
import { setOnline } from "../redux/application/application.actions";
import { selectOnline } from "../redux/application/application.selectors";
import SimplifiedPartsPageContainer from "../pages/simplified-parts/simplified-parts.page.container.jsx";
import { setIsPartsEntry, setOnline } from "../redux/application/application.actions";
import { selectIsPartsEntry, selectOnline } from "../redux/application/application.selectors";
import { checkUserSession } from "../redux/user/user.actions";
import { selectBodyshop, selectCurrentEula, selectCurrentUser } from "../redux/user/user.selectors";
import PrivateRoute from "../components/PrivateRoute";
@@ -23,26 +24,37 @@ import InstanceRenderMgr from "../utils/instanceRenderMgr";
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
import { NotificationProvider } from "../contexts/Notifications/notificationContext.jsx";
import SocketProvider from "../contexts/SocketIO/socketProvider.jsx";
import SoundWrapper from "./SoundWrapper.jsx";
const ResetPassword = lazy(() => import("../pages/reset-password/reset-password.component"));
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
const SignInPage = lazy(() => import("../pages/sign-in/sign-in.page"));
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() => import("../pages/mobile-payment/mobile-payment.container"));
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
bodyshop: selectBodyshop,
currentEula: selectCurrentEula
currentEula: selectCurrentEula,
isPartsEntry: selectIsPartsEntry
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline))
setOnline: (isOnline) => dispatch(setOnline(isOnline)),
setIsPartsEntry: (isParts) => dispatch(setIsPartsEntry(isParts))
});
export function App({ bodyshop, checkUserSession, currentUser, online, setOnline, currentEula }) {
export function App({
bodyshop,
checkUserSession,
currentUser,
online,
setOnline,
setIsPartsEntry,
currentEula,
isPartsEntry
}) {
const client = useSplitClient().client;
const [listenersAdded, setListenersAdded] = useState(false);
const { t } = useTranslation();
@@ -52,12 +64,14 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
if (!navigator.onLine) {
setOnline(false);
}
checkUserSession();
}, [checkUserSession, setOnline]);
//const b = Grid.useBreakpoint();
// console.log("Breakpoints:", b);
useEffect(() => {
const pathname = window.location.pathname;
const isParts = pathname === "/parts" || pathname.startsWith("/parts/");
setIsPartsEntry(isParts);
}, [setIsPartsEntry]);
// Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
@@ -124,7 +138,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
);
}
if (currentEula && !currentUser.eulaIsAccepted) {
if (!isPartsEntry && currentEula && !currentUser.eulaIsAccepted) {
return <Eula />;
}
@@ -144,86 +158,91 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
currentUser={currentUser}
bodyshop={bodyshop}
workspaceCode={bodyshop?.tours_enabled ? "9BkbEseqNqxw8jUH" : ""}
isPartsEntry={isPartsEntry}
/>
<NotificationProvider>
<Routes>
<Route
path="*"
element={
<ErrorBoundary>
<LandingPage />
</ErrorBoundary>
}
/>
<Route
path="/signin"
element={
<ErrorBoundary>
<SignInPage />
</ErrorBoundary>
}
/>
<Route
path="/resetpassword"
element={
<ErrorBoundary>
<ResetPassword />
</ErrorBoundary>
}
/>
<Route
path="/csi/:surveyId"
element={
<ErrorBoundary>
<CsiPage />
</ErrorBoundary>
}
/>
<Route
path="/disclaimer"
element={
<ErrorBoundary>
<DisclaimerPage />
</ErrorBoundary>
}
/>
<Route
path="/mp/:paymentIs"
element={
<ErrorBoundary>
<MobilePaymentContainer />
</ErrorBoundary>
}
/>
<Route
path="/manage/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop} navigate={navigate} currentUser={currentUser}>
<SoundWrapper bodyshop={bodyshop}>
<Routes>
<Route
path="*"
element={
<ErrorBoundary>
<LandingPage />
</ErrorBoundary>
}
/>
<Route
path="/signin"
element={
<ErrorBoundary>
<SignInPage />
</ErrorBoundary>
}
/>
<Route
path="/resetpassword"
element={
<ErrorBoundary>
<ResetPassword />
</ErrorBoundary>
}
/>
<Route
path="/csi/:surveyId"
element={
<ErrorBoundary>
<CsiPage />
</ErrorBoundary>
}
/>
<Route
path="/disclaimer"
element={
<ErrorBoundary>
<DisclaimerPage />
</ErrorBoundary>
}
/>
<Route
path="/manage/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop} navigate={navigate} currentUser={currentUser}>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
</ErrorBoundary>
}
>
<Route path="*" element={<ManagePage />} />
</Route>
<Route
path="/tech/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop} navigate={navigate} currentUser={currentUser}>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
</ErrorBoundary>
}
>
<Route path="*" element={<TechPageContainer />} />
</Route>
<Route
path="/parts/*"
element={
<ErrorBoundary>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
</ErrorBoundary>
}
>
<Route path="*" element={<ManagePage />} />
</Route>
<Route
path="/tech/*"
element={
<ErrorBoundary>
<SocketProvider bodyshop={bodyshop} navigate={navigate} currentUser={currentUser}>
<PrivateRoute isAuthorized={currentUser.authorized} />
</SocketProvider>
</ErrorBoundary>
}
>
<Route path="*" element={<TechPageContainer />} />
</Route>
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized} />}>
<Route path="*" element={<DocumentEditorContainer />} />
</Route>
</Routes>
</ErrorBoundary>
}
>
<Route path="*" element={<SimplifiedPartsPageContainer />} />
</Route>
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized} />}>
<Route path="*" element={<DocumentEditorContainer />} />
</Route>
</Routes>
</SoundWrapper>
</NotificationProvider>
</Suspense>
);

View File

@@ -1,15 +1,226 @@
//Global Styles.
:root {
--table-stripe-bg: #f4f4f4; /* Light mode table stripe */
--menu-divider-color: #74695c; /* Light mode menu divider */
--menu-submenu-text: rgba(255, 255, 255, 0.65); /* Light mode submenu text */
--kanban-column-bg: #ddd; /* Light mode kanban column */
--alert-color: blue; /* Light mode alert */
--completion-soon-color: rgba(255, 140, 0, 0.8); /* Light mode completion soon */
--completion-past-color: rgba(255, 0, 0, 0.8); /* Light mode completion past */
--job-line-manual-color: tomato; /* Light mode job line manual */
--muted-button-color: lightgray; /* Light mode muted button */
--muted-button-hover-color: darkgrey; /* Light mode muted button hover */
--table-border-color: #ddd; /* Light mode table border */
--table-hover-bg: #f5f5f5; /* Light mode table hover */
--popover-bg: #fff; /* Light mode popover background */
--error-text: red; /* Light mode error message */
--no-jobs-text: #888; /* Light mode no jobs message */
--message-yours-bg: #eee; /* Light mode yours message background */
--message-mine-bg-start: #00d0ea; /* Light mode mine message gradient start */
--message-mine-bg-end: #0085d1; /* Light mode mine message gradient end */
--message-mine-text: white; /* Light mode mine message text */
--message-mine-tail-bg: white; /* Light mode mine/yours message tail */
--system-message-bg: #f5f5f5; /* Light mode system message background */
--system-message-text: #555; /* Light mode system message text */
--system-label-text: #888; /* Light mode system label/date text */
--message-icon-color: whitesmoke; /* Light mode message icon */
--eula-card-bg: lightgray; /* Light mode eula card background */
--notification-bg: #fff; /* Light mode notification background */
--notification-text: rgba(0, 0, 0, 0.85); /* Light mode notification text */
--notification-border: #d9d9d9; /* Light mode notification border */
--notification-header-bg: #fafafa; /* Light mode notification header background */
--notification-header-border: #f0f0f0; /* Light mode notification header border */
--notification-header-text: rgba(0, 0, 0, 0.85); /* Light mode notification header text */
--notification-toggle-icon: #1677ff; /* Light mode notification toggle icon */
--notification-switch-bg: #1677ff; /* Light mode notification switch background */
--notification-btn-link: #1677ff; /* Light mode notification link button */
--notification-btn-link-hover: #69b1ff; /* Light mode notification link button hover */
--notification-btn-link-disabled: rgba(0, 0, 0, 0.25); /* Light mode notification link button disabled */
--notification-btn-link-active: #0958d9; /* Light mode notification link button active */
--notification-read-bg: #fff; /* Light mode notification read background */
--notification-read-text: rgba(0, 0, 0, 0.65); /* Light mode notification read text */
--notification-unread-bg: #f5f5f5; /* Light mode notification unread background */
--notification-unread-text: rgba(0, 0, 0, 0.85); /* Light mode notification unread text */
--notification-item-hover-bg: #fafafa; /* Light mode notification item hover background */
--notification-ro-number: #1677ff; /* Light mode notification RO number */
--notification-relative-time: rgba(0, 0, 0, 0.45); /* Light mode notification relative time */
--alert-bg: #fff1f0; /* Light mode alert background */
--alert-text: rgba(0, 0, 0, 0.85); /* Light mode alert text */
--alert-border: #ffa39e; /* Light mode alert border */
--alert-message: #ff4d4f; /* Light mode alert message */
--share-badge-bg: #cccccc; /* Light mode share badge background */
--column-header-bg: #d0d0d0; /* Light mode column header background */
--footer-bg: #d0d0d0; /* Light mode footer background */
--tech-icon-color: orangered; /* Light mode tech icon color */
--clone-border-color: #1890ff; /* Light mode clone border color */
--event-arrived-bg: rgba(4, 141, 4, 0.4); /* Light mode arrived event background */
--event-block-bg: tomato; /* Light mode block event background */
--event-selected-bg: slategrey; /* Light mode selected event background */
--task-bg: #fff; /* Light mode task center background */
--task-text: rgba(0, 0, 0, 0.85); /* Light mode task text */
--task-border: #d9d9d9; /* Light mode task border */
--task-header-bg: #fafafa; /* Light mode task header background */
--task-header-border: #f0f0f0; /* Light mode task header border */
--task-section-bg: #f5f5f5; /* Light mode task section background */
--task-section-border: #e8e8e8; /* Light mode task section border */
--task-row-hover-bg: #f5f5f5; /* Light mode task row hover background */
--task-row-border: #f0f0f0; /* Light mode task row border */
--task-ro-number: #1677ff; /* Light mode task RO number */
--task-due-text: rgba(0, 0, 0, 0.45); /* Light mode task due text */
--task-button-bg: #1677ff; /* Light mode task button background */
--task-button-hover-bg: #4096ff; /* Light mode task button hover background */
--task-button-disabled-bg: #d9d9d9; /* Light mode task button disabled background */
--task-button-text: white; /* Light mode task button text */
--task-message-text: rgba(0, 0, 0, 0.45); /* Light mode task message text */
--mask-bg: rgba(0, 0, 0, 0.05); /* Light mode mask background */
--board-text-color: #393939; /* Light mode board text color */
--section-bg: #e3e3e3; /* Light mode section background */
--detail-text-color: #4d4d4d; /* Light mode detail text color */
--card-selected-bg: rgba(128, 128, 128, 0.2); /* Light mode selected card background */
--card-stripe-even-bg: #f0f2f5; /* Light mode even card background */
--card-stripe-odd-bg: #ffffff; /* Light mode odd card background */
--bar-border-color: #f0f2f5; /* Light mode bar border and background */
--tag-wrapper-bg: #f0f2f5; /* Light mode tag wrapper background */
--tag-wrapper-text: #000; /* Light mode tag wrapper text */
--preview-bg: lightgray; /* Light mode preview background */
--preview-border-color: #2196F3; /* Light mode preview border color */
--event-bg-fallback: #c4c4c4; /* Light mode event background fallback */
--card-bg-fallback: #ffffff; /* Light mode card background fallback */
--card-text-fallback: black; /* Light mode card text fallback */
--table-row-even-bg: rgb(236, 236, 236); /* Light mode table row even background */
--status-row-bg-fallback: #ffffff; /* Light mode status row fallback background */
--reset-link-color: #0000ff; /* Light mode reset link color */
--error-header-text: tomato; /* Light mode error header text */
--tooltip-bg: white; /* Light mode tooltip background */
--tooltip-border: gray; /* Light mode tooltip border */
--tooltip-text-fallback: black; /* Light mode tooltip text fallback */
--teams-button-bg: #6264A7; /* Light mode Teams button background */
--teams-button-border: #6264A7; /* Light mode Teams button border */
--teams-button-text: #FFFFFF; /* Light mode Teams button text and icon */
--content-bg: #fff; /* Light mode content background */
--legend-bg-fallback: #ffffff; /* Light mode legend background fallback */
--tech-content-bg: #fff; /* Light mode tech content background */
--today-bg: #ffffff; /* Light mode today background */
--today-text: #000000; /* Light mode today text */
--off-range-bg: #f8f8f8; /* Light mode off-range background */
}
[data-theme="dark"] {
--table-stripe-bg: #2a2a2a; /* Dark mode table stripe */
--menu-divider-color: #5c5c5c; /* Dark mode menu divider */
--menu-submenu-text: rgba(255, 255, 255, 0.85); /* Dark mode submenu text */
--kanban-column-bg: #333333; /* Dark mode kanban column */
--alert-color: #4da8ff; /* Dark mode alert */
--completion-soon-color: #ff8c1a; /* Dark mode completion soon */
--completion-past-color: #ff4d4f; /* Dark mode completion past */
--job-line-manual-color: #ff6347; /* Dark mode job line manual */
--muted-button-color: #666666; /* Dark mode muted button */
--muted-button-hover-color: #999999; /* Dark mode muted button hover */
--table-border-color: #5c5c5c; /* Dark mode table border */
--table-hover-bg: #2a2a2a; /* Dark mode table hover */
--popover-bg: #2a2a2a; /* Dark mode popover background */
--error-text: #ff4d4f; /* Dark mode error message */
--no-jobs-text: #999999; /* Dark mode no jobs message */
--message-yours-bg: #2a2a2a; /* Dark mode yours message background */
--message-mine-bg-start: #4da8ff; /* Dark mode mine message gradient start */
--message-mine-bg-end: #326ade; /* Dark mode mine message gradient end */
--message-mine-text: #ffffff; /* Dark mode mine message text */
--message-mine-tail-bg: #1f1f1f; /* Dark mode mine/yours message tail */
--system-message-bg: #333333; /* Dark mode system message background */
--system-message-text: #cccccc; /* Dark mode system message text */
--system-label-text: #999999; /* Dark mode system label/date text */
--message-icon-color: #cccccc; /* Dark mode message icon */
--eula-card-bg: #2a2a2a; /* Dark mode eula card background */
--notification-bg: #2a2a2a; /* Dark mode notification background */
--notification-text: rgba(255, 255, 255, 0.85); /* Dark mode notification text */
--notification-border: #5c5c5c; /* Dark mode notification border */
--notification-header-bg: #333333; /* Dark mode notification header background */
--notification-header-border: #444444; /* Dark mode notification header border */
--notification-header-text: rgba(255, 255, 255, 0.85); /* Dark mode notification header text */
--notification-toggle-icon: #4da8ff; /* Dark mode notification toggle icon */
--notification-switch-bg: #4da8ff; /* Dark mode notification switch background */
--notification-btn-link: #4da8ff; /* Dark mode notification link button */
--notification-btn-link-hover: #80c1ff; /* Dark mode notification link button hover */
--notification-btn-link-disabled: rgba(255, 255, 255, 0.25); /* Dark mode notification link button disabled */
--notification-btn-link-active: #2681ff; /* Dark mode notification link button active */
--notification-read-bg: #2a2a2a; /* Dark mode notification read background */
--notification-read-text: rgba(255, 255, 255, 0.65); /* Dark mode notification read text */
--notification-unread-bg: #333333; /* Dark mode notification unread background */
--notification-unread-text: rgba(255, 255, 255, 0.85); /* Dark mode notification unread text */
--notification-item-hover-bg: #3a3a3a; /* Dark mode notification item hover background */
--notification-ro-number: #4da8ff; /* Dark mode notification RO number */
--notification-relative-time: rgba(255, 255, 255, 0.45); /* Dark mode notification relative time */
--alert-bg: #3a1a1a; /* Dark mode alert background */
--alert-text: rgba(255, 255, 255, 0.85); /* Dark mode alert text */
--alert-border: #ff6666; /* Dark mode alert border */
--alert-message: #ff6666; /* Dark mode alert message */
--share-badge-bg: #666666; /* Dark mode share badge background */
--column-header-bg: #333333; /* Dark mode column header background */
--footer-bg: #333333; /* Dark mode footer background */
--tech-icon-color: #ff4500; /* Dark mode tech icon color */
--clone-border-color: #4da8ff; /* Dark mode clone border color */
--event-arrived-bg: rgba(4, 141, 4, 0.6); /* Dark mode arrived event background */
--event-block-bg: tomato; /* Dark mode block event background */
--event-selected-bg: #4a5e6e; /* Dark mode selected event background */
--task-bg: #2a2a2a; /* Dark mode task center background */
--task-text: rgba(255, 255, 255, 0.85); /* Dark mode task text */
--task-border: #5c5c5c; /* Dark mode task border */
--task-header-bg: #333333; /* Dark mode task header background */
--task-header-border: #444444; /* Dark mode task header border */
--task-section-bg: #333333; /* Dark mode task section background */
--task-section-border: #444444; /* Dark mode task section border */
--task-row-hover-bg: #3a3a3a; /* Dark mode task row hover background */
--task-row-border: #444444; /* Dark mode task row border */
--task-ro-number: #4da8ff; /* Dark mode task RO number */
--task-due-text: rgba(255, 255, 255, 0.45); /* Dark mode task due text */
--task-button-bg: #4da8ff; /* Dark mode task button background */
--task-button-hover-bg: #80c1ff; /* Dark mode task button hover background */
--task-button-disabled-bg: #666666; /* Dark mode task button disabled background */
--task-button-text: #ffffff; /* Dark mode task button text */
--task-message-text: rgba(255, 255, 255, 0.45); /* Dark mode task message text */
--mask-bg: rgba(255, 255, 255, 0.05); /* Dark mode mask background */
--board-text-color: #cccccc; /* Dark mode board text color */
--section-bg: #333333; /* Dark mode section background */
--detail-text-color: #bbbbbb; /* Dark mode detail text color */
--card-selected-bg: rgba(255, 255, 255, 0.1); /* Dark mode selected card background */
--card-stripe-even-bg: #2a2a2a; /* Dark mode even card background */
--card-stripe-odd-bg: #1f1f1f; /* Dark mode odd card background */
--bar-border-color: #2a2a2a; /* Dark mode bar border and background */
--tag-wrapper-bg: #2a2a2a; /* Dark mode tag wrapper background */
--tag-wrapper-text: #cccccc; /* Dark mode tag wrapper text */
--preview-bg: #2a2a2a; /* Dark mode preview background */
--preview-border-color: #4da8ff; /* Dark mode preview border color */
--event-bg-fallback: #262626; /* Dark mode event background fallback */
--card-bg-fallback: #2a2a2a; /* Dark mode card background fallback */
--card-text-fallback: #cccccc; /* Dark mode card text fallback */
--table-row-even-bg: #2a2a2a; /* Dark mode table row even background */
--status-row-bg-fallback: #1f1f1f; /* Dark mode status row fallback background */
--reset-link-color: #4da8ff; /* Dark mode reset link color */
--error-header-text: #ff6347; /* Dark mode error header text */
--tooltip-bg: #2a2a2a; /* Dark mode tooltip background */
--tooltip-border: #5c5c5c; /* Dark mode tooltip border */
--tooltip-text-fallback: #cccccc; /* Dark mode tooltip text fallback */
--teams-button-bg: #7b7dc4; /* Dark mode Teams button background */
--teams-button-border: #7b7dc4; /* Dark mode Teams button border */
--teams-button-text: #ffffff; /* Dark mode Teams button text and icon */
--content-bg: #2a2a2a; /* Dark mode content background */
--legend-bg-fallback: #2a2a2a; /* Dark mode legend background fallback */
--tech-content-bg: #2a2a2a; /* Dark mode tech content background */
--today-bg: #4a5e6e; /* Dark mode today background */
--today-text: #ffffff; /* Dark mode today text */
--off-range-bg: #333333; /* Dark mode off-range background */
--svg-background: #FFF; /* Dark mode SVG background */
}
// Global Styles
@import "react-big-calendar/lib/sass/styles";
.ant-menu-item-divider {
border-bottom: 1px solid #74695c !important;
border-bottom: 1px solid var(--menu-divider-color) !important;
}
// TODO: This was added because the newest release of ant was making the text color and the background color the same on a selected header
// Tried all available tokens (https://ant.design/components/menu?locale=en-US) and even reverted all our custom styles, to no avail
// This should be kept an eye on, especially if implementing DARK MODE
// Note: Monitor this in dark mode to ensure text visibility
.ant-menu-submenu-title {
color: rgba(255, 255, 255, 0.65) !important;
color: var(--menu-submenu-text) !important;
}
.imex-table-header {
@@ -46,7 +257,7 @@
}
.ellipses {
display: inline-block; /* for em, a, span, etc (inline by default) */
display: inline-block;
text-overflow: ellipsis;
width: calc(95%);
overflow: hidden;
@@ -60,22 +271,23 @@
}
}
// Scrollbar styles (uncomment if needed, updated for dark mode)
// ::-webkit-scrollbar-track {
// -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
// border-radius: 0.2rem;
// background-color: #f5f5f5;
// background-color: var(--table-stripe-bg);
// }
// ::-webkit-scrollbar {
// width: 0.25rem;
// max-height: 0.25rem;
// background-color: #f5f5f5;
// background-color: var(--table-stripe-bg);
// }
// ::-webkit-scrollbar-thumb {
// border-radius: 0.2rem;
// -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
// background-color: #188fff;
// background-color: var(--alert-color);
// }
.ant-input-number-input,
@@ -88,28 +300,27 @@
.production-alert {
animation: alertBlinker 1s linear infinite;
color: blue;
color: var(--alert-color);
}
@keyframes alertBlinker {
50% {
color: red;
color: var(--completion-past-color);
opacity: 100;
//opacity: 0;
}
}
.blue {
color: blue;
color: var(--alert-color);
}
.production-completion-soon {
color: rgba(255, 140, 0, 0.8);
color: var(--completion-soon-color);
font-weight: bold;
}
.production-completion-past {
color: rgba(255, 0, 0, 0.8);
color: var(--completion-past-color);
font-weight: bold;
}
@@ -139,7 +350,7 @@
}
.react-kanban-column {
background-color: #ddd !important;
background-color: var(--kanban-column-bg) !important;
}
.production-list-table {
@@ -151,18 +362,18 @@
.ReactGridGallery_tile-icon-bar {
div {
svg {
fill: #1890ff;
fill: var(--alert-color);
}
}
}
.job-line-manual {
color: tomato;
color: var(--job-line-manual-color);
font-style: italic;
}
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
background-color: #f4f4f4;
background-color: var(--table-stripe-bg);
}
.rowWithColor > td {
@@ -170,15 +381,15 @@
}
.muted-button {
color: lightgray;
color: var(--muted-button-color);
border: none;
background: none;
cursor: pointer;
font-size: 16px; /* Adjust as needed */
font-size: 16px;
}
.muted-button:hover {
color: darkgrey;
color: var(--muted-button-hover-color);
}
.notification-alert-unordered-list {
@@ -190,3 +401,31 @@
margin-right: 0;
}
}
.content-container {
padding: 1rem;
}
// Override react-big-calendar styles for dark mode only
[data-theme="dark"] {
.car-svg {
background-color: var(--svg-background);
}
.rbc-today {
background-color: var(--today-bg);
color: var(--today-text);
}
.rbc-off-range {
background-color: var(--off-range-bg);
}
.rbc-day-bg.rbc-today {
background-color: var(--today-bg);
}
}
//.rbc-time-header-gutter {
// padding: 0;
//}

View File

@@ -1,9 +1,9 @@
import React from "react";
import { memo } from "react";
import PropTypes from "prop-types";
import { ProductFruits } from "react-product-fruits";
import dayjs from "dayjs";
const ProductFruitsWrapper = React.memo(({ currentUser, bodyshop, workspaceCode }) => {
const ProductFruitsWrapper = memo(({ currentUser, bodyshop, workspaceCode, isPartsEntry }) => {
const featureProps = bodyshop?.features
? Object.entries(bodyshop.features).reduce((acc, [key, value]) => {
acc[key] = value === true || (typeof value === "string" && dayjs(value).isAfter(dayjs()));
@@ -12,6 +12,7 @@ const ProductFruitsWrapper = React.memo(({ currentUser, bodyshop, workspaceCode
: {};
return (
!isPartsEntry &&
workspaceCode &&
currentUser?.authorized === true &&
currentUser?.email && (
@@ -30,6 +31,8 @@ const ProductFruitsWrapper = React.memo(({ currentUser, bodyshop, workspaceCode
);
});
ProductFruitsWrapper.displayName = "ProductFruitsWrapper";
export default ProductFruitsWrapper;
ProductFruitsWrapper.propTypes = {

View File

@@ -0,0 +1,43 @@
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { useNotification } from "../contexts/Notifications/notificationContext.jsx";
import { initNewMessageSound, unlockAudio } from "./../utils/soundManager";
import { initSingleTabAudioLeader } from "../utils/singleTabAudioLeader";
export default function SoundWrapper({ children, bodyshop }) {
const { t } = useTranslation();
const notification = useNotification();
useEffect(() => {
if (!bodyshop?.id) return;
// 1) Init single-tab leader election (only one tab should play sounds), scoped by bodyshopId
const cleanupLeader = initSingleTabAudioLeader(bodyshop.id);
// 2) Initialize base audio
initNewMessageSound("https://images.imex.online/app/messageTone.wav", 0.7);
// 3) Show a one-time prompt when autoplay blocks first play
const onNeedsUnlock = () => {
notification.info({
description: t("audio.manager.description"),
duration: 3
});
};
window.addEventListener("sound-needs-unlock", onNeedsUnlock);
// 4) Proactively unlock on first gesture (once per session)
const gesture = () => unlockAudio(bodyshop.id);
window.addEventListener("click", gesture, { once: true, passive: true });
window.addEventListener("touchstart", gesture, { once: true, passive: true });
window.addEventListener("keydown", gesture, { once: true });
return () => {
cleanupLeader();
window.removeEventListener("sound-needs-unlock", onNeedsUnlock);
// gesture listeners were added with {once:true}
};
}, [notification, t, bodyshop?.id]); // include bodyshop.id so this runs when org changes
return <>{children}</>;
}

View File

@@ -4,36 +4,42 @@ import InstanceRenderMgr from "../utils/instanceRenderMgr";
const { defaultAlgorithm, darkAlgorithm } = theme;
let isDarkMode = false;
/**
* Default theme
* @type {{components: {Menu: {itemDividerBorderColor: string}}}}
*/
const defaultTheme = {
const defaultTheme = (isDarkMode) => ({
components: {
Table: {
rowHoverBg: "#e7f3ff",
rowSelectedBg: "#e6f7ff",
rowHoverBg: isDarkMode ? "#2a2a2a" : "#e7f3ff",
rowSelectedBg: isDarkMode ? "#333333" : "#e6f7ff",
headerSortHoverBg: "transparent"
},
Menu: {
darkItemHoverBg: "#1890ff",
itemHoverBg: "#1890ff",
horizontalItemHoverBg: "#1890ff"
darkItemHoverBg: isDarkMode ? "#004a77" : "#1890ff",
itemHoverBg: isDarkMode ? "#004a77" : "#1890ff",
horizontalItemHoverBg: isDarkMode ? "#004a77" : "#1890ff"
}
},
token: {
colorPrimary: InstanceRenderMgr({
imex: "#1890ff",
rome: "#326ade"
}),
colorInfo: InstanceRenderMgr({
imex: "#1890ff",
rome: "#326ade"
})
colorPrimary: InstanceRenderMgr(
{
imex: isDarkMode ? "#4da8ff" : "#1890ff",
rome: isDarkMode ? "#5b8ce6" : "#326ade"
},
isDarkMode
),
colorInfo: InstanceRenderMgr(
{
imex: isDarkMode ? "#4da8ff" : "#1890ff",
rome: isDarkMode ? "#5b8ce6" : "#326ade"
},
isDarkMode
),
colorError: isDarkMode ? "#ff4d4f" : "#f5222d",
colorBgBase: isDarkMode ? "#1f1f1f" : "#ffffff" // Align with Ant Design dark mode
}
};
});
/**
* Development theme
@@ -60,8 +66,9 @@ const prodTheme = {};
const currentTheme = import.meta.env.DEV ? devTheme : prodTheme;
const finaltheme = {
const getTheme = (isDarkMode) => ({
algorithm: isDarkMode ? darkAlgorithm : defaultAlgorithm,
...defaultsDeep(currentTheme, defaultTheme)
};
export default finaltheme;
});
export default getTheme;

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { useEffect } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
function PrivateRoute({ component: Component, isAuthorized, ...rest }) {
function PrivateRoute({ isAuthorized }) {
const location = useLocation();
const navigate = useNavigate();

View File

@@ -1,5 +1,4 @@
import { Button } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";

View File

@@ -142,7 +142,16 @@ export function AccountingReceivablesTableComponent({ bodyshop, loading, jobs, r
refetch={refetch}
/>
<Link to={`/manage/jobs/${record.id}/close`}>
<Button>{t("jobs.labels.viewallocations")}</Button>
<Button
style={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
verticalAlign: "middle"
}}
>
{t("jobs.labels.viewallocations")}
</Button>
</Link>
</Space>
)

View File

@@ -1,5 +1,4 @@
import { Alert } from "antd";
import React from "react";
export default function AlertComponent(props) {
return <Alert {...props} />;

View File

@@ -1,5 +1,4 @@
import { Button, InputNumber, Popover, Select } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import AllocationsAssignmentComponent from "./allocations-assignment.component";
import { useMutation } from "@apollo/client";
import { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
@@ -18,7 +18,7 @@ export default function AllocationsAssignmentContainer({ jobLineId, hours, refet
const handleAssignment = () => {
insertAllocation({ variables: { alloc: { ...assignment } } })
.then((r) => {
.then(() => {
notification["success"]({
message: t("allocations.successes.save")
});

View File

@@ -1,5 +1,4 @@
import { Button, Popover, Select } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import AllocationsBulkAssignment from "./allocations-bulk-assignment.component";
import { useMutation } from "@apollo/client";
import { INSERT_ALLOCATION } from "../../graphql/allocations.queries";
@@ -24,7 +24,7 @@ export default function AllocationsBulkAssignmentContainer({ jobLines, refetch }
return acc;
}, []);
insertAllocation({ variables: { alloc: allocs } }).then((r) => {
insertAllocation({ variables: { alloc: allocs } }).then(() => {
notification["success"]({
message: t("employees.successes.save")
});

View File

@@ -1,5 +1,4 @@
import Icon from "@ant-design/icons";
import React from "react";
import { MdRemoveCircleOutline } from "react-icons/md";
export default function AllocationsLabelComponent({ allocation, handleClick }) {

View File

@@ -1,4 +1,3 @@
import React from "react";
import { useMutation } from "@apollo/client";
import { DELETE_ALLOCATION } from "../../graphql/allocations.queries";
import AllocationsLabelComponent from "./allocations-employee-label.component";
@@ -13,13 +12,13 @@ export default function AllocationsLabelContainer({ allocation, refetch }) {
const handleClick = (e) => {
e.preventDefault();
deleteAllocation({ variables: { id: allocation.id } })
.then((r) => {
.then(() => {
notification["success"]({
message: t("allocations.successes.deleted")
});
if (refetch) refetch();
})
.catch((error) => {
.catch(() => {
notification["error"]({ message: t("allocations.errors.deleting") });
});
};

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import { Table } from "antd";
import { alphaSort } from "../../utils/sorters";
import { DateTimeFormatter } from "../../utils/DateFormatter";

View File

@@ -1,4 +1,3 @@
import React from "react";
import AuditTrailListComponent from "./audit-trail-list.component";
import { useQuery } from "@apollo/client";
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";

View File

@@ -1,5 +1,5 @@
import { Table } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";

View File

@@ -1,4 +1,3 @@
import React from "react";
import { List } from "antd";
import Icon from "@ant-design/icons";
import { FaArrowRight } from "react-icons/fa";

View File

@@ -1,5 +1,4 @@
import { Popover, Tag } from "antd";
import React from "react";
import Barcode from "react-barcode";
import { useTranslation } from "react-i18next";

View File

@@ -1,5 +1,5 @@
import { Checkbox, Form, Skeleton, Typography } from "antd";
import React, { useEffect } from "react";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-cm-returns-table.styles.scss";
@@ -33,7 +33,7 @@ export default function BillCmdReturnsTableComponent({ form, returnLoading, retu
return (
<Form.List name="outstanding_returns">
{(fields, { add, remove, move }) => {
{(fields) => {
return (
<>
<Typography.Title level={4}>{t("bills.labels.creditsnotreceived")}</Typography.Title>

View File

@@ -6,7 +6,7 @@
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid var(--table-border-color);
.ant-form-item {
margin-bottom: 0px !important;
@@ -14,6 +14,6 @@
}
tr:hover {
background-color: #f5f5f5;
background-color: var(--table-hover-bg);
}
}

View File

@@ -1,7 +1,7 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Popconfirm } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { DELETE_BILL } from "../../graphql/bills.queries";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
@@ -43,7 +43,7 @@ export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) {
}
});
if (!!!result.errors) {
if (!result.errors) {
notification["success"]({ message: t("bills.successes.deleted") });
insertAuditTrail({
jobid: jobid,

View File

@@ -2,7 +2,7 @@ import { PageHeader } from "@ant-design/pro-layout";
import { useMutation, useQuery } from "@apollo/client";
import { Button, Divider, Form, Popconfirm, Space } from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
@@ -10,7 +10,6 @@ import { createStructuredSelector } from "reselect";
import { DELETE_BILL_LINE, INSERT_NEW_BILL_LINES, UPDATE_BILL_LINE } from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import dayjs from "../../utils/day";
@@ -28,13 +27,12 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type }))
});
export default connect(mapStateToProps, mapDispatchToProps)(BillDetailEditcontainer);
export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail, bodyshop }) {
export function BillDetailEditcontainer({ insertAuditTrail, bodyshop }) {
const search = queryString.parse(useLocation().search);
const { t } = useTranslation();
@@ -48,7 +46,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
variables: { billid: search.billid },
skip: !!!search.billid,
skip: !search.billid,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
});
@@ -71,7 +69,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
setUpdateLoading(true);
//let adjustmentsToInsert = {};
const { billlines, upload, ...bill } = values;
const { billlines, ...bill } = values;
const updates = [];
updates.push(
update_bill({
@@ -98,6 +96,7 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
});
billlines.forEach((billline) => {
// eslint-disable-next-line no-unused-vars
const { deductedfromlbr, inventories, jobline, original_actual_price, create_ppc, ...il } = billline;
delete il.__typename;
@@ -152,8 +151,8 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
if (error) return <AlertComponent message={error.message} type="error" />;
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
const isinhouse = data && data.bills_by_pk && data.bills_by_pk.isinhouse;
const exported = data?.bills_by_pk && data.bills_by_pk.exported;
const isinhouse = data?.bills_by_pk && data.bills_by_pk.isinhouse;
return (
<>
@@ -183,8 +182,8 @@ export function BillDetailEditcontainer({ setPartsOrderContext, insertAuditTrail
{t("general.actions.save")}
</Button>
</Popconfirm>
<BillReeportButtonComponent bill={data && data.bills_by_pk} />
<BillMarkExportedButton bill={data && data.bills_by_pk} />
<BillReeportButtonComponent bill={data?.bills_by_pk} />
<BillMarkExportedButton bill={data?.bills_by_pk} />
</Space>
}
/>
@@ -220,11 +219,11 @@ const transformData = (data) => {
billlines: data.bills_by_pk.billlines.map((i) => {
return {
...i,
joblineid: !!i.joblineid ? i.joblineid : "noline",
joblineid: i.joblineid ? i.joblineid : "noline",
applicable_taxes: {
federal: (i.applicable_taxes && i.applicable_taxes.federal) || false,
state: (i.applicable_taxes && i.applicable_taxes.state) || false,
local: (i.applicable_taxes && i.applicable_taxes.local) || false
federal: i.applicable_taxes?.federal || false,
state: i.applicable_taxes?.state || false,
local: i.applicable_taxes?.local || false
}
};
}),

View File

@@ -1,18 +1,15 @@
import { Button, Checkbox, Form, Modal } from "antd";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) =>
dispatch(
@@ -20,20 +17,12 @@ const mapDispatchToProps = (dispatch) => ({
context: context,
modal: "partsOrder"
})
),
insertAuditTrail: ({ jobid, operation, type }) =>
dispatch(
insertAuditTrail({
jobid,
operation,
type
})
)
});
export default connect(mapStateToProps, mapDispatchToProps)(BillDetailEditReturn);
export function BillDetailEditReturn({ setPartsOrderContext, insertAuditTrail, bodyshop, data, disabled }) {
export function BillDetailEditReturn({ setPartsOrderContext, data, disabled }) {
const search = queryString.parse(useLocation().search);
const history = useNavigate();
const { t } = useTranslation();
@@ -86,9 +75,9 @@ export function BillDetailEditReturn({ setPartsOrderContext, insertAuditTrail, b
title={t("bills.actions.return")}
onOk={() => form.submit()}
>
<Form initialValues={data && data.bills_by_pk} onFinish={handleFinish} form={form}>
<Form initialValues={data?.bills_by_pk} onFinish={handleFinish} form={form}>
<Form.List name={["billlines"]}>
{(fields, { add, remove, move }) => {
{(fields) => {
return (
<table style={{ tableLayout: "auto", width: "100%" }}>
<thead>

View File

@@ -1,6 +1,5 @@
import { Drawer, Grid } from "antd";
import queryString from "query-string";
import React from "react";
import { useLocation, useNavigate } from "react-router-dom";
import BillDetailEditComponent from "./bill-detail-edit-component";

View File

@@ -26,6 +26,7 @@ import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { handleUpload as handleUploadToImageProxy } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -85,6 +86,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
}
setLoading(true);
// eslint-disable-next-line no-unused-vars
const { upload, location, outstanding_returns, inventory, federal_tax_exempt, ...remainingValues } = values;
let adjustmentsToInsert = {};
@@ -102,9 +105,13 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
const {
deductedfromlbr,
lbr_adjustment,
// eslint-disable-next-line no-unused-vars
location: lineLocation,
// eslint-disable-next-line no-unused-vars
part_type,
// eslint-disable-next-line no-unused-vars
create_ppc,
// eslint-disable-next-line no-unused-vars
original_actual_price,
...restI
} = i;
@@ -444,7 +451,9 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
setEnterAgain(false);
}}
>
<BillFormContainer form={form} disableInvNumber={billEnterModal.context.disableInvNumber} />
<RbacWrapper action="bills:enter">
<BillFormContainer form={form} disableInvNumber={billEnterModal.context.disableInvNumber} />
</RbacWrapper>
</Form>
</Modal>
);

View File

@@ -1,11 +1,11 @@
import { Form, Input, Table } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import BillFormItemsExtendedFormItem from "./bill-form-lines.extended.formitem.component";
export default function BillFormLinesExtended({ lineData, discount, form, responsibilityCenters, disabled }) {
export default function BillFormLinesExtended({ lineData, discount, form, responsibilityCenters }) {
const [search, setSearch] = useState("");
const { t } = useTranslation();
const columns = [

View File

@@ -1,4 +1,3 @@
import React from "react";
import { MinusCircleFilled, PlusCircleFilled, WarningOutlined } from "@ant-design/icons";
import { Button, Form, Input, InputNumber, Select, Space, Switch } from "antd";
import { useTranslation } from "react-i18next";
@@ -12,7 +11,7 @@ import CiecaSelect from "../../utils/Ciecaselect";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(BillFormItemsExtendedFormItem);
@@ -22,7 +21,6 @@ export function BillFormItemsExtendedFormItem({
bodyshop,
form,
record,
index,
disabled,
responsibilityCenters,
discount
@@ -78,7 +76,7 @@ export function BillFormItemsExtendedFormItem({
...billlineskeys,
[record.id]: {
...billlineskeys[billlineskeys],
actual_cost: !!billlineskeys[billlineskeys].actual_cost
actual_cost: billlineskeys[billlineskeys].actual_cost
? billlineskeys[billlineskeys].actual_cost
: Math.round((parseFloat(e.target.value) * (1 - discount) + Number.EPSILON) * 100) / 100
}
@@ -93,7 +91,7 @@ export function BillFormItemsExtendedFormItem({
<Form.Item shouldUpdate>
{() => {
const line = value;
if (!!!line) return null;
if (!line) return null;
const lineDiscount = (1 - Math.round((line.actual_cost / line.actual_price) * 100) / 100).toPrecision(2);
if (lineDiscount - discount === 0) return <div />;

View File

@@ -2,7 +2,7 @@ import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Alert, Divider, Form, Input, Select, Space, Statistic, Switch, Upload } from "antd";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdOpenInNew } from "react-icons/md";
import { connect } from "react-redux";
@@ -26,7 +26,7 @@ import DateTimePicker from "../form-date-time-picker/form-date-time-picker.compo
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
const mapDispatchToProps = () => ({});
export function BillFormComponent({
bodyshop,
@@ -254,7 +254,7 @@ export function BillFormComponent({
required: true
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
() => ({
validator(rule, value) {
if (ClosingPeriod.treatment === "on" && bodyshop.accountingconfig.ClosingPeriod) {
if (
@@ -374,8 +374,10 @@ export function BillFormComponent({
let totals;
if (!!values.total && !!values.billlines && values.billlines.length > 0)
totals = CalculateBillTotal(values);
if (!!totals)
if (totals)
return (
// TODO: Align is not correct
// eslint-disable-next-line react/no-unknown-property
<div align="right">
<Space size="large" wrap>
<Statistic title={t("bills.labels.subtotal")} value={totals.subtotal.toFormat()} precision={2} />
@@ -458,7 +460,7 @@ export function BillFormComponent({
if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
return e?.fileList;
}}
>
<Upload.Dragger multiple={true} name="logo" beforeUpload={() => false} listType="picture">

View File

@@ -1,6 +1,5 @@
import { useLazyQuery, useQuery } from "@apollo/client";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";

View File

@@ -1,7 +1,6 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Button, Checkbox, Form, Input, InputNumber, Select, Space, Switch, Table, Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -16,7 +15,7 @@ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
@@ -27,8 +26,7 @@ export function BillEnterModalLinesComponent({
discount,
form,
responsibilityCenters,
billEdit,
billid
billEdit
}) {
const { t } = useTranslation();
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
@@ -47,7 +45,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.jobline"),
dataIndex: "joblineid",
editable: true,
width: "20rem",
minWidth: "10rem",
formItemProps: (field) => {
return {
key: `${field.index}joblinename`,
@@ -73,9 +71,9 @@ export function BillEnterModalLinesComponent({
disabled={disabled}
options={lineData}
style={{
width: "20rem",
maxWidth: "20rem",
minWidth: "10rem",
//width: "10rem",
// maxWidth: "20rem",
minWidth: "20rem",
whiteSpace: "normal",
height: "auto",
minHeight: "32px" // default height of Ant Design inputs
@@ -112,7 +110,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.line_desc"),
dataIndex: "line_desc",
editable: true,
width: "20rem",
minWidth: "10rem",
formItemProps: (field) => {
return {
key: `${field.index}line_desc`,
@@ -126,7 +124,7 @@ export function BillEnterModalLinesComponent({
]
};
},
formInput: (record, index) => <Input.TextArea disabled={disabled} autoSize />
formInput: () => <Input.TextArea disabled={disabled} autoSize />
},
{
title: t("billlines.fields.quantity"),
@@ -158,7 +156,7 @@ export function BillEnterModalLinesComponent({
]
};
},
formInput: (record, index) => <InputNumber precision={0} min={1} disabled={disabled} />
formInput: () => <InputNumber precision={0} min={1} disabled={disabled} />
},
{
title: t("billlines.fields.actual_price"),
@@ -188,7 +186,7 @@ export function BillEnterModalLinesComponent({
if (idx === index) {
return {
...item,
actual_cost: !!item.actual_cost
actual_cost: item.actual_cost
? item.actual_cost
: Math.round((parseFloat(e.target.value) * (1 - discount) + Number.EPSILON) * 100) / 100
};
@@ -234,7 +232,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.actual_cost"),
dataIndex: "actual_cost",
editable: true,
width: "8rem",
width: "10rem",
formItemProps: (field) => {
return {
@@ -258,7 +256,7 @@ export function BillEnterModalLinesComponent({
<Form.Item shouldUpdate noStyle>
{() => {
const line = getFieldsValue(["billlines"]).billlines[index];
if (!!!line) return null;
if (!line) return null;
let lineDiscount = 1 - line.actual_cost / line.actual_price;
if (isNaN(lineDiscount)) lineDiscount = 0;
return (
@@ -322,7 +320,7 @@ export function BillEnterModalLinesComponent({
]
};
},
formInput: (record, index) => (
formInput: () => (
<Select showSearch style={{ minWidth: "3rem" }} disabled={disabled}>
{bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? CiecaSelect(true, false)
@@ -344,7 +342,7 @@ export function BillEnterModalLinesComponent({
name: [field.name, "location"]
};
},
formInput: (record, index) => (
formInput: () => (
<Select disabled={disabled}>
{bodyshop.md_parts_locations.map((loc, idx) => (
<Select.Option key={idx} value={loc}>
@@ -359,6 +357,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.labels.deductedfromlbr"),
dataIndex: "deductedfromlbr",
editable: true,
width: "40px",
formItemProps: (field) => {
return {
valuePropName: "checked",
@@ -366,7 +365,7 @@ export function BillEnterModalLinesComponent({
name: [field.name, "deductedfromlbr"]
};
},
formInput: (record, index) => <Switch disabled={disabled} />,
formInput: () => <Switch disabled={disabled} />,
additional: (record, index) => (
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}>
{() => {
@@ -466,7 +465,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.federal_tax_applicable"),
dataIndex: "applicable_taxes.federal",
editable: true,
width: "40px",
formItemProps: (field) => {
return {
key: `${field.index}fedtax`,
@@ -478,7 +477,7 @@ export function BillEnterModalLinesComponent({
name: [field.name, "applicable_taxes", "federal"]
};
},
formInput: (record, index) => <Switch disabled={disabled} />
formInput: () => <Switch disabled={disabled} />
}
]
}),
@@ -487,7 +486,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.state_tax_applicable"),
dataIndex: "applicable_taxes.state",
editable: true,
width: "40px",
formItemProps: (field) => {
return {
key: `${field.index}statetax`,
@@ -495,7 +494,7 @@ export function BillEnterModalLinesComponent({
name: [field.name, "applicable_taxes", "state"]
};
},
formInput: (record, index) => <Switch disabled={disabled} />
formInput: () => <Switch disabled={disabled} />
},
...InstanceRenderManager({
@@ -505,7 +504,7 @@ export function BillEnterModalLinesComponent({
title: t("billlines.fields.local_tax_applicable"),
dataIndex: "applicable_taxes.local",
editable: true,
width: "40px",
formItemProps: (field) => {
return {
key: `${field.index}localtax`,
@@ -513,7 +512,7 @@ export function BillEnterModalLinesComponent({
name: [field.name, "applicable_taxes", "local"]
};
},
formInput: (record, index) => <Switch disabled={disabled} />
formInput: () => <Switch disabled={disabled} />
}
]
}),
@@ -575,7 +574,7 @@ export function BillEnterModalLinesComponent({
}
]}
>
{(fields, { add, remove, move }) => {
{(fields, { add, remove }) => {
return (
<>
<Table
@@ -612,19 +611,7 @@ export function BillEnterModalLinesComponent({
export default connect(mapStateToProps, mapDispatchToProps)(BillEnterModalLinesComponent);
const EditableCell = ({
dataIndex,
title,
inputType,
record,
index,
children,
formInput,
formItemProps,
additional,
wrapper,
...restProps
}) => {
const EditableCell = ({ dataIndex, record, children, formInput, formItemProps, additional, wrapper, ...restProps }) => {
const propsFinal = formItemProps && formItemProps(record);
if (propsFinal && "key" in propsFinal) {
delete propsFinal.key;

View File

@@ -9,10 +9,10 @@ export const CalculateBillTotal = (invoice) => {
let stateTax = Dinero({ amount: 0 });
let localTax = Dinero({ amount: 0 });
if (!!!billlines) return null;
if (!billlines) return null;
billlines.forEach((i) => {
if (!!i) {
if (i) {
const itemTotal = Dinero({
amount: Math.round((i.actual_cost || 0) * 100)
}).multiply(i.quantity || 1);

View File

@@ -1,5 +1,5 @@
import { Checkbox, Form, Skeleton, Typography } from "antd";
import React, { useEffect } from "react";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-inventory-table.styles.scss";
@@ -13,7 +13,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
billEnterModal: selectBillEnterModal
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(BillInventoryTable);
@@ -22,7 +22,7 @@ export function BillInventoryTable({ billEnterModal, bodyshop, form, billEdit, i
const { t } = useTranslation();
useEffect(() => {
if (inventoryData && inventoryData.inventory) {
if (inventoryData?.inventory) {
form.setFieldsValue({
inventory: billEnterModal.context.consumeinventoryid
? inventoryData.inventory.map((i) => {
@@ -47,7 +47,7 @@ export function BillInventoryTable({ billEnterModal, bodyshop, form, billEdit, i
return (
<Form.List name="inventory">
{(fields, { add, remove, move }) => {
{(fields) => {
return (
<>
<Typography.Title level={4}>{t("inventory.labels.inventory")}</Typography.Title>

View File

@@ -6,7 +6,7 @@
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
border-bottom: 1px solid var(--table-border-color);
.ant-form-item {
margin-bottom: 0px !important;
@@ -14,6 +14,6 @@
}
tr:hover {
background-color: #f5f5f5;
background-color: var(--table-hover-bg);
}
}

View File

@@ -39,30 +39,32 @@ const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps },
style: {
...(item.removed ? { textDecoration: "line-through" } : {})
},
name: `${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim(),
label: (
<div style={{ whiteSpace: "normal", wordBreak: "break-word" }}>
<span>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
</span>
{InstanceRenderMgr({
rome: item.act_price === 0 && item.mod_lb_hrs > 0 && (
<span style={{ float: "right", paddingleft: "1rem" }}>{`${item.mod_lb_hrs} units`}</span>
)
})}
<span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
</span>
</div>
)
name: generateLineName(item),
label: generateLineName(item)
}))
]}
{...restProps}
></Select>
);
};
function generateLineName(item) {
return (
<div style={{ whiteSpace: "normal", wordBreak: "break-word" }}>
<span>
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
</span>
{InstanceRenderMgr({
rome: item.act_price === 0 && item.mod_lb_hrs > 0 && (
<span style={{ float: "right", paddingleft: "1rem" }}>{`${item.mod_lb_hrs} units`}</span>
)
})}
<span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price ? `$${item.act_price && item.act_price.toFixed(2)}` : ``}
</span>
</div>
);
}
export default forwardRef(BillLineSearchSelect);

View File

@@ -1,6 +1,6 @@
import { gql, useMutation } from "@apollo/client";
import { Button } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -15,7 +15,7 @@ const mapStateToProps = createStructuredSelector({
authLevel: selectAuthLevel,
currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});

View File

@@ -1,5 +1,5 @@
import { Button, Space } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
@@ -26,7 +26,7 @@ export default function BillPrintButton({ billid }) {
null,
notification
);
} catch (e) {
} catch {
console.warn("Warning: Error generating a document.");
}
setLoading(false);

View File

@@ -1,6 +1,6 @@
import { gql, useMutation } from "@apollo/client";
import { Button } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -13,7 +13,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});

View File

@@ -3,7 +3,7 @@ import { useMutation } from "@apollo/client";
import { Button, Tooltip } from "antd";
import { t } from "i18next";
import dayjs from "./../../utils/day";
import React, { useState } from "react";
import { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_INVENTORY_AND_CREDIT } from "../../graphql/inventory.queries";
@@ -17,7 +17,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(BilllineAddInventory);

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { FaTasks } from "react-icons/fa";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -75,6 +76,7 @@ export function BillsListTableComponent({
<Button
title={t("tasks.buttons.create")}
onClick={() => {
logImEXEvent("bills_create_task", {});
setTaskUpsertContext({
context: {
jobid: job.id,
@@ -109,6 +111,13 @@ export function BillsListTableComponent({
key: "vendorname",
sorter: (a, b) => alphaSort(a.vendor.name, b.vendor.name),
sortOrder: state.sortedInfo.columnKey === "vendorname" && state.sortedInfo.order,
filters: bills
? [...new Set(bills.map((bill) => bill.vendor.name))].map((name) => ({
text: name,
value: name
}))
: [],
onFilter: (value, record) => record.vendor.name === value,
render: (text, record) => <span>{record.vendor.name}</span>
},
{
@@ -160,6 +169,7 @@ export function BillsListTableComponent({
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
logImEXEvent("bills_list_sort_filter", { pagination, filters, sorter });
};
const filteredBills = bills
@@ -201,6 +211,7 @@ export function BillsListTableComponent({
<Button
disabled={!hasBillsAccess}
onClick={() => {
logImEXEvent("bills_reconcile", {});
setReconciliationContext({
actions: { refetch: billsQuery.refetch },
context: {

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { useQuery } from "@apollo/client";
import queryString from "query-string";
@@ -100,9 +100,9 @@ export default function BillsVendorsList() {
selectedRowKeys: [search.vendorid],
type: "radio"
}}
onRow={(record, rowIndex) => {
onRow={(record) => {
return {
onClick: (event) => {
onClick: () => {
handleOnRowClick(record);
} // click row
};

View File

@@ -1,10 +1,9 @@
import { HomeFilled } from "@ant-design/icons";
import { Breadcrumb, Col, Row } from "antd";
import React from "react";
import { selectBreadcrumbs, selectIsPartsEntry } from "../../redux/application/application.selectors";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import GlobalSearch from "../global-search/global-search.component";
import GlobalSearchOs from "../global-search/global-search-os.component";
@@ -13,18 +12,19 @@ import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs,
bodyshop: selectBodyshop
bodyshop: selectBodyshop,
isPartsEntry: selectIsPartsEntry
});
export function BreadCrumbs({ breadcrumbs, bodyshop }) {
export function BreadCrumbs({ breadcrumbs, bodyshop, isPartsEntry }) {
const {
treatments: { OpenSearch }
} = useSplitTreatments({
attributes: {},
names: ["OpenSearch"],
splitKey: bodyshop && bodyshop.imexshopid
splitKey: bodyshop?.imexshopid
});
// TODO - Client Update - Technically key is not doing anything here
return (
<Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}>
@@ -34,8 +34,8 @@ export function BreadCrumbs({ breadcrumbs, bodyshop }) {
{
key: "home",
title: (
<Link to={`/manage/`}>
<HomeFilled /> {(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) || ""}
<Link to={isPartsEntry ? `/parts/` : `/manage/`}>
<HomeFilled /> {(bodyshop?.shopname && `(${bodyshop.shopname})`) || ""}
</Link>
)
},

View File

@@ -1,5 +1,5 @@
import { Button, Form, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -32,12 +32,13 @@ export function ContractsFindModalContainer({ caBcEtfTableModal, toggleModalVisi
logImEXEvent("ca_bc_etf_table_parse");
setLoading(true);
const claimNumbers = [];
values.table.split("\n").forEach((row, idx, arr) => {
values.table.split("\n").forEach((row) => {
const { 1: claim, 2: shortclaim, 4: amount } = row.split("\t");
if (!claim || !shortclaim) return;
const trimmedShortClaim = shortclaim.trim();
// const trimmedClaim = claim.trim();
if (amount.slice(-1) === "-") {
// NO OP
}
claimNumbers.push({

View File

@@ -1,17 +1,13 @@
import { Form, Input, Radio } from "antd";
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";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapStateToProps = createStructuredSelector({});
export default connect(mapStateToProps, null)(PartsReceiveModalComponent);
export function PartsReceiveModalComponent({ bodyshop, form }) {
export function PartsReceiveModalComponent() {
const { t } = useTranslation();
return (

View File

@@ -1,6 +1,6 @@
import { CalculatorFilled } from "@ant-design/icons";
import { Button, Form, InputNumber, Popover, Space } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";

View File

@@ -2,7 +2,7 @@ import { CopyFilled, DeleteFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import { Button, Card, Col, Form, Input, message, Row, Space, Spin, Statistic } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -14,7 +14,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
import { getCurrentUser } from "../../firebase/firebase.utils";
import { getCurrentUser, logImEXEvent } from "../../firebase/firebase.utils";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
const mapStateToProps = createStructuredSelector({
@@ -124,6 +124,7 @@ const CardPaymentModalComponent = ({
const { payments } = form.getFieldsValue();
try {
logImEXEvent("payment_cc_lightbox");
const response = await axios.post("/intellipay/lightbox_credentials", {
bodyshop,
refresh: !!window.intellipay,
@@ -133,7 +134,6 @@ const CardPaymentModalComponent = ({
});
if (window.intellipay) {
// eslint-disable-next-line no-eval
eval(response.data);
pollForIntelliPay(() => {
SetIntellipayCallbackFunctions();
@@ -149,7 +149,7 @@ const CardPaymentModalComponent = ({
window.intellipay.initialize();
});
}
} catch (error) {
} catch {
notification.open({
type: "error",
message: t("job_payments.notifications.error.openingip")
@@ -172,6 +172,7 @@ const CardPaymentModalComponent = ({
try {
const { payments } = form.getFieldsValue();
logImEXEvent("payment_cc_shortlink");
const response = await axios.post("/intellipay/generate_payment_url", {
bodyshop,
amount: payments.reduce((acc, val) => acc + (val?.amount || 0), 0),
@@ -187,7 +188,7 @@ const CardPaymentModalComponent = ({
message.success(t("general.actions.copied"));
}
setLoading(false);
} catch (error) {
} catch {
notification.open({
type: "error",
message: t("job_payments.notifications.error.openingip")
@@ -359,7 +360,7 @@ function pollForIntelliPay(callbackFunction) {
const startTime = Date.now();
function checkFixAmount() {
if (window.intellipay && window.intellipay.fixAmount !== undefined) {
if (window.intellipay?.fixAmount) {
callbackFunction();
return;
}

View File

@@ -1,23 +1,20 @@
import { Button, Modal } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectCardPayment } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CardPaymentModalComponent from "./card-payment-modal.component";
const mapStateToProps = createStructuredSelector({
cardPaymentModal: selectCardPayment,
bodyshop: selectBodyshop
cardPaymentModal: selectCardPayment
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
});
function CardPaymentModalContainer({ cardPaymentModal, toggleModalVisible, bodyshop }) {
function CardPaymentModalContainer({ cardPaymentModal, toggleModalVisible }) {
const { open } = cardPaymentModal;
const { t } = useTranslation();

View File

@@ -9,13 +9,13 @@ import "./chat-affix.styles.scss";
import { registerMessagingHandlers, unregisterMessagingHandlers } from "./registerMessagingSocketHandlers";
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
export function ChatAffixContainer({ bodyshop, chatVisible }) {
export function ChatAffixContainer({ bodyshop, chatVisible, currentUser }) {
const { t } = useTranslation();
const client = useApolloClient();
const { socket } = useSocket();
useEffect(() => {
if (!bodyshop || !bodyshop.messagingservicesid) return;
if (!bodyshop?.messagingservicesid) return;
async function SubscribeToTopicForFCMNotification() {
try {
@@ -35,8 +35,8 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
SubscribeToTopicForFCMNotification();
// Register WebSocket handlers
if (socket && socket.connected) {
registerMessagingHandlers({ socket, client });
if (socket?.connected) {
registerMessagingHandlers({ socket, client, currentUser, bodyshop, t });
return () => {
unregisterMessagingHandlers({ socket });
@@ -44,11 +44,11 @@ export function ChatAffixContainer({ bodyshop, chatVisible }) {
}
}, [bodyshop, socket, t, client]);
if (!bodyshop || !bodyshop.messagingservicesid) return <></>;
if (!bodyshop?.messagingservicesid) return <></>;
return (
<div className={`chat-affix ${chatVisible ? "chat-affix-open" : ""}`}>
{bodyshop && bodyshop.messagingservicesid ? <ChatPopupComponent /> : null}
{bodyshop?.messagingservicesid ? <ChatPopupComponent /> : null}
</div>
);
}

View File

@@ -1,6 +1,11 @@
import { CONVERSATION_LIST_QUERY, GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries";
import { gql } from "@apollo/client";
import { playNewMessageSound } from "../../utils/soundManager.js";
import { isLeaderTab } from "../../utils/singleTabAudioLeader";
import { CONVERSATION_LIST_QUERY, GET_CONVERSATION_DETAILS } from "../../graphql/conversations.queries";
import { QUERY_ACTIVE_ASSOCIATION_SOUND } from "../../graphql/user.queries";
const logLocal = (message, ...args) => {
if (import.meta.env.VITE_APP_IS_TEST || !import.meta.env.PROD) {
console.log(`==================== ${message} ====================`);
@@ -26,16 +31,48 @@ const enrichConversation = (conversation, isOutbound) => ({
__typename: "conversations"
});
export const registerMessagingHandlers = ({ socket, client }) => {
// Can be uncommonted to test the playback of the notification sound
// window.testTone = () => {
// const notificationSound = new Audio(newMessageSound);
// notificationSound.play().catch((error) => {
// console.error("Error playing notification sound:", error);
// });
// };
export const registerMessagingHandlers = ({ socket, client, currentUser, bodyshop }) => {
if (!(socket && client)) return;
const handleNewMessageSummary = async (message) => {
const { conversationId, newConversation, existingConversation, isoutbound } = message;
// True only when DB value is strictly true; falls back to true on cache miss
const isNewMessageSoundEnabled = (client) => {
try {
const email = currentUser?.email;
if (!email) return true; // default allow if we can't resolve user
const res = client.readQuery({
query: QUERY_ACTIVE_ASSOCIATION_SOUND,
variables: { email }
});
const flag = res?.associations?.[0]?.new_message_sound;
return flag === true; // strictly true => enabled
} catch {
// If the query hasn't been seeded in cache yet, default ON
return true;
}
};
logLocal("handleNewMessageSummary - Start", { message, isNew: !existingConversation });
const queryVariables = { offset: 0 };
if (!isoutbound) {
// Play notification sound for new inbound message (scoped to bodyshop)
if (isLeaderTab(bodyshop.id) && isNewMessageSoundEnabled(client)) {
playNewMessageSound(bodyshop.id);
}
}
if (!existingConversation && conversationId) {
// Attempt to read from the cache to determine if this is actually a new conversation
try {
@@ -57,7 +94,7 @@ export const registerMessagingHandlers = ({ socket, client }) => {
existingConversation: true
});
}
} catch (error) {
} catch {
logLocal("handleNewMessageSummary - Cache miss", { conversationId });
}
}
@@ -291,8 +328,6 @@ export const registerMessagingHandlers = ({ socket, client }) => {
case "conversation-unarchived":
case "conversation-archived":
// Would like to someday figure out how to get this working without refetch queries,
// But I have but a solid 4 hours into it, and there are just too many weird occurrences
try {
const listQueryVariables = { offset: 0 };
const detailsQueryVariables = { conversationId };
@@ -328,7 +363,7 @@ export const registerMessagingHandlers = ({ socket, client }) => {
}
break;
case "tag-added":
case "tag-added": {
// Ensure `job_conversations` is properly formatted
const formattedJobConversations = job_conversations.map((jc) => ({
__typename: "job_conversations",
@@ -375,6 +410,7 @@ export const registerMessagingHandlers = ({ socket, client }) => {
});
break;
}
case "tag-removed":
try {
@@ -462,7 +498,7 @@ export const registerMessagingHandlers = ({ socket, client }) => {
logLocal("handlePhoneNumberOptedOut - Error", { error: error.message });
}
};
// New handler for phone number opt-in
const handlePhoneNumberOptedIn = async (data) => {
const { bodyshopid, phone_number } = data;

View File

@@ -29,9 +29,7 @@ const mapDispatchToProps = (dispatch) => ({
function ChatConversationListComponent({ conversationList, selectedConversation, setSelectedConversation, bodyshop }) {
const { t } = useTranslation();
const [, forceUpdate] = useState(false);
const phoneNumbers = conversationList.map((item) => phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, ""));
const { data: optOutData } = useQuery(GET_PHONE_NUMBER_OPT_OUTS, {
variables: {
bodyshopid: bodyshop.id,
@@ -64,15 +62,12 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
const item = sortedConversationList[index];
const normalizedPhone = phone(item.phone_num, "CA").phoneNumber.replace(/^\+1/, "");
const hasOptOutEntry = optOutMap.has(normalizedPhone);
const cardContentRight = <TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>;
const cardContentLeft =
item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => <Tag key={idx}>{j.job.ro_number}</Tag>)
: null;
const names = <>{_.uniq(item.job_conversations.map((j, idx) => OwnerNameDisplayFunction(j.job)))}</>;
const names = <>{_.uniq(item.job_conversations.map((j) => OwnerNameDisplayFunction(j.job)))}</>;
const cardTitle = (
<>
{item.label && <Tag color="blue">{item.label}</Tag>}
@@ -85,7 +80,6 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
)}
</>
);
const cardExtra = (
<>
<Badge count={item.messages_aggregate.aggregate.count} />
@@ -98,11 +92,10 @@ function ChatConversationListComponent({ conversationList, selectedConversation,
)}
</>
);
const getCardStyle = () =>
item.id === selectedConversation
? { backgroundColor: "rgba(128, 128, 128, 0.2)" }
: { backgroundColor: index % 2 === 0 ? "#f0f2f5" : "#ffffff" };
? { backgroundColor: "var(--card-selected-bg)" }
: { backgroundColor: index % 2 === 0 ? "var(--card-stripe-even-bg)" : "var(--card-stripe-odd-bg)" };
return (
<List.Item

View File

@@ -21,7 +21,7 @@ export function ChatConversationTitleTags({ jobConversations, bodyshop }) {
const handleRemoveTag = async (jobId) => {
const convId = jobConversations[0].conversationid;
if (!!convId) {
if (convId) {
await removeJobConversation({
variables: {
conversationId: convId,

View File

@@ -1,5 +1,4 @@
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";
@@ -16,10 +15,10 @@ const mapDispatchToProps = () => ({});
export function ChatConversationTitle({ conversation }) {
return (
<Space className="chat-title" wrap>
<PhoneNumberFormatter>{conversation && conversation.phone_num}</PhoneNumberFormatter>
<PhoneNumberFormatter>{conversation?.phone_num}</PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags jobConversations={(conversation && conversation.job_conversations) || []} />
<ChatConversationTitleTags jobConversations={conversation?.job_conversations || []} />
<ChatTagRoContainer conversation={conversation || []} />
<ChatArchiveButton conversation={conversation} />
</Space>

View File

@@ -1,4 +1,3 @@
import React from "react";
import AlertComponent from "../alert/alert.component";
import ChatConversationTitle from "../chat-conversation-title/chat-conversation-title.component";
import ChatMessageListComponent from "../chat-messages-list/chat-message-list.component";

View File

@@ -14,7 +14,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
const mapDispatchToProps = () => ({});
export function ChatLabel({ conversation, bodyshop }) {
const [loading, setLoading] = useState(false);

View File

@@ -19,7 +19,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({});
const mapDispatchToProps = () => ({});
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
@@ -40,7 +40,11 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
variables: {
jobId: conversation.job_conversations[0]?.jobid
},
skip: !open || !conversation.job_conversations || conversation.job_conversations.length === 0
skip:
!open ||
!conversation.job_conversations ||
conversation.job_conversations.length === 0 ||
bodyshop.uselocalmediaserver
});
const handleVisibleChange = (change) => {
@@ -48,7 +52,8 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
};
useEffect(() => {
setSelectedMedia([]);
// Instead of wiping the array (which holds media objects), just clear selection flags
setSelectedMedia((prev) => prev.map((m) => ({ ...m, isSelected: false })));
}, [setSelectedMedia, conversation]);
//Knowingly taking on the technical debt of poor implementation below. Done this way to avoid an edge case where no component may be displayed.
@@ -75,6 +80,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={conversation.job_conversations[0]?.jobid}
context="chat"
/>
)}
</>
@@ -90,6 +96,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={conversation.job_conversations[0]?.jobid}
context="chat"
/>
)}
</>
@@ -110,6 +117,7 @@ export function ChatMediaSelector({ bodyshop, selectedMedia, setSelectedMedia, c
trigger="click"
open={open}
onOpenChange={handleVisibleChange}
destroyOnHidden
classNames={{ root: "media-selector-popover" }}
>
<Badge count={selectedMedia.filter((s) => s.isSelected).length}>

View File

@@ -5,7 +5,7 @@
max-height: 480px;
overflow-y: auto;
padding: 8px;
background-color: #fff;
background-color: var(--popover-bg);
border-radius: 8px;
}
}
@@ -17,7 +17,7 @@
}
.error-message {
color: red;
color: var(--error-text);
font-size: 12px;
text-align: center;
margin-bottom: 8px;
@@ -25,14 +25,13 @@
.no-jobs-message {
font-size: 14px;
color: #888;
color: var(--no-jobs-text);
text-align: center;
padding: 8px;
}
/* Style images within gallery components */
.media-selector-content img {
object-fit: cover;
border-radius: 4px;
margin: 4px;
@@ -40,8 +39,8 @@
}
/* Grid layout for gallery components */
.media-selector-content .ant-image, /* Assuming gallery components use Ant Design's Image */
.media-selector-content .gallery-container { /* Fallback for custom gallery classes */
.media-selector-content .ant-image,
.media-selector-content .gallery-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 4px;

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import { Virtuoso } from "react-virtuoso";
import { renderMessage } from "./renderMessage";
import "./chat-message-list.styles.scss";
@@ -76,7 +76,7 @@ export default function ChatMessageListComponent({ messages }) {
<Virtuoso
ref={virtuosoRef}
data={messages}
overscan={!!messages.reduce((acc, message) => acc + (message.image_path?.length || 0), 0) ? messages.length : 0}
overscan={messages.reduce((acc, message) => acc + (message.image_path?.length || 0), 0) ? messages.length : 0}
itemContent={(index) => renderMessage(messages, index)}
followOutput={(isAtBottom) => handleScrollStateChange(isAtBottom)}
initialTopMostItemIndex={messages.length - 1}

View File

@@ -44,7 +44,6 @@
.chat-send-message-button {
margin: 0.3rem;
padding-left: 0.5rem;
}
.message-icon {
@@ -52,7 +51,7 @@
bottom: 0.1rem;
right: 0.3rem;
margin: 0 0.1rem;
color: whitesmoke;
color: var(--message-icon-color);
z-index: 5;
}
@@ -80,7 +79,7 @@
&:last-child:after {
width: 10px;
background: white;
background: var(--message-mine-tail-bg);
z-index: 1;
}
}
@@ -92,11 +91,11 @@
.message {
margin-right: 20%;
background-color: #eee;
background-color: var(--message-yours-bg);
&:last-child:before {
left: -7px;
background: #eee;
background: var(--message-yours-bg);
border-bottom-right-radius: 15px;
}
@@ -112,14 +111,14 @@
align-items: flex-end;
.message {
color: white;
color: var(--message-mine-text);
margin-left: 25%;
background: linear-gradient(to bottom, #00d0ea 0%, #0085d1 100%);
background: linear-gradient(to bottom, var(--message-mine-bg-start) 0%, var(--message-mine-bg-end) 100%);
padding-bottom: 0.6rem;
&:last-child:before {
right: -8px;
background: linear-gradient(to bottom, #00d0ea 0%, #0085d1 100%);
background: linear-gradient(to bottom, var(--message-mine-bg-start) 0%, var(--message-mine-bg-end) 100%);
border-bottom-left-radius: 15px;
}
@@ -135,32 +134,31 @@
margin: 0.5rem 10%;
.message {
background-color: #f5f5f5;
background-color: var(--system-message-bg);
border-radius: 10px;
padding: 0.5rem 1rem;
text-align: center;
font-style: italic;
color: #555;
color: var(--system-message-text);
width: fit-content;
max-width: 80%;
}
.system-label {
font-size: 0.75rem;
color: #888;
color: var(--system-label-text);
margin-bottom: 0.2rem;
display: block;
}
.system-date {
font-size: 0.75rem;
color: #888;
color: var(--system-label-text);
margin-top: 0.2rem;
text-align: center;
}
}
.virtuoso-container {
flex: 1;
overflow: auto;

View File

@@ -1,6 +1,5 @@
import { PlusCircleOutlined } from "@ant-design/icons";
import { Dropdown } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setMessage } from "../../redux/messaging/messaging.actions";

View File

@@ -1,6 +1,6 @@
import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
import { Space, Spin } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
@@ -31,7 +31,7 @@ export function ChatPrintButton({ conversation }) {
type,
conversation.id,
notification
).catch((e) => {
).catch(() => {
console.warn("Something went wrong generating a document.");
});
setLoading(false);

View File

@@ -1,6 +1,5 @@
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
import { Empty, Select, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";

View File

@@ -1,5 +1,4 @@
import { Checkbox, Form } from "antd";
import React from "react";
export default function JobIntakeFormCheckboxComponent({ formItem, readOnly }) {
const { name, label, required } = formItem;

View File

@@ -1,4 +1,3 @@
import React from "react";
import FormTypes from "./config-form-types";
export default function ConfirmFormComponents({ componentList, readOnly }) {
@@ -7,7 +6,7 @@ export default function ConfirmFormComponents({ componentList, readOnly }) {
{componentList.map((f, idx) => {
const Comp = FormTypes[f.type];
if (!!Comp) {
if (Comp) {
return <Comp key={idx} formItem={f} readOnly={readOnly} />;
} else {
return <div key={idx}>Error</div>;

View File

@@ -1,5 +1,4 @@
import { Form, Rate } from "antd";
import React from "react";
export default function JobIntakeFormCheckboxComponent({ formItem, readOnly }) {
const { name, label, required } = formItem;

View File

@@ -1,5 +1,4 @@
import { Form, Slider } from "antd";
import React from "react";
export default function JobIntakeFormCheckboxComponent({ formItem, readOnly }) {
const { name, label, required, min, max } = formItem;

View File

@@ -1,5 +1,4 @@
import { Form, Input } from "antd";
import React from "react";
export default function JobIntakeFormCheckboxComponent({ formItem, readOnly }) {
const { name, label, required } = formItem;

View File

@@ -1,5 +1,4 @@
import { Form, Input } from "antd";
import React from "react";
export default function JobIntakeFormCheckboxComponent({ formItem, readOnly }) {
const { name, label, required, rows } = formItem;

View File

@@ -1,4 +1,3 @@
import React from "react";
import { Button, Result } from "antd";
import { useTranslation } from "react-i18next";
import InstanceRenderManager from "../../utils/instanceRenderMgr";

View File

@@ -1,5 +1,5 @@
import { Card, Input, Table } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters";
@@ -114,9 +114,9 @@ export default function ContractsCarsComponent({ loading, data, selectedCarId, h
type: "radio",
selectedRowKeys: [selectedCarId]
}}
onRow={(record, rowIndex) => {
onRow={(record) => {
return {
onClick: (event) => {
onClick: () => {
handleSelect(record);
}
};

View File

@@ -1,6 +1,5 @@
import { useQuery } from "@apollo/client";
import dayjs from "../../utils/day";
import React from "react";
import { QUERY_AVAILABLE_CC } from "../../graphql/courtesy-car.queries";
import AlertComponent from "../alert/alert.component";
import ContractCarsComponent from "./contract-cars.component";

View File

@@ -2,7 +2,7 @@ import { useMutation } from "@apollo/client";
import { Button, Form, InputNumber, Popover, Radio, Select, Space } from "antd";
import axios from "axios";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useNavigate } from "react-router-dom";
@@ -16,7 +16,7 @@ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
const mapDispatchToProps = () => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
@@ -270,7 +270,7 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract, disabled
// awaitRefetchQueries: true,
});
if (!!result.errors) {
if (result.errors) {
notification["error"]({
message: t("jobs.errors.inserting", {
message: JSON.stringify(result.errors)

View File

@@ -1,5 +1,4 @@
import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import DataLabel from "../data-label/data-label.component";

View File

@@ -1,6 +1,6 @@
import { useLazyQuery } from "@apollo/client";
import { Button } from "antd";
import React, { useEffect } from "react";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { GET_JOB_FOR_CC_CONTRACT } from "../../graphql/jobs.queries";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";

View File

@@ -1,6 +1,5 @@
import { WarningFilled } from "@ant-design/icons";
import { Form, Input, InputNumber, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { DateFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";

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