From 2a2edeadb981c2b27ffdd5dcec11834ecde25f9f Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 09:25:58 -0800 Subject: [PATCH 01/16] IO-3535 Resolve federal tax default off on received parts order. --- .../src/components/bill-form/bill-form.lines.component.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/components/bill-form/bill-form.lines.component.jsx b/client/src/components/bill-form/bill-form.lines.component.jsx index d1fb03369..b838eb3ac 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -517,7 +517,11 @@ export function BillEnterModalLinesComponent({ formItemProps: (field) => ({ key: `${field.name}fedtax`, valuePropName: "checked", - name: [field.name, "applicable_taxes", "federal"] + name: [field.name, "applicable_taxes", "federal"], + initialValue: InstanceRenderManager({ + imex: true, + rome: false + }) }), formInput: () => } From 28a41f7637c5a0aa4aa5fa3934e30da7cdb0ef9c Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 09:33:37 -0800 Subject: [PATCH 02/16] IO-3534 Adjust value prop to content for antd prop change to fix color display. --- .../components/bill-form/bill-form.component.jsx | 14 +++++++++++--- .../total-production-hours.component.jsx | 2 +- .../dms-post-form/cdklike-dms-post-form.jsx | 2 +- .../payment-form.totalpayments.component.jsx | 2 +- .../src/pages/jobs-close/jobs-close.component.jsx | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/client/src/components/bill-form/bill-form.component.jsx b/client/src/components/bill-form/bill-form.component.jsx index d46035805..14d26570c 100644 --- a/client/src/components/bill-form/bill-form.component.jsx +++ b/client/src/components/bill-form/bill-form.component.jsx @@ -373,9 +373,16 @@ export function BillFormComponent({ "local_tax_rate" ]); let totals; - if (!!values.total && !!values.billlines && values.billlines.length > 0) + if (!!values.total && !!values.billlines && values.billlines.length > 0) { totals = CalculateBillTotal(values); - if (totals) + } + + if (totals) { + console.log( + "Discrep", + totals.discrepancy.getAmount(), + totals.discrepancy.getAmount() === 0 ? "green" : "red" + ); return ( // TODO: Align is not correct // eslint-disable-next-line react/no-unknown-property @@ -414,7 +421,7 @@ export function BillFormComponent({ ); + } return null; }} diff --git a/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx b/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx index 5c29a1962..b9716e84b 100644 --- a/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx +++ b/client/src/components/dashboard-components/total-production-hours/total-production-hours.component.jsx @@ -36,7 +36,7 @@ export function DashboardTotalProductionHours({ bodyshop, data, ...cardProps }) diff --git a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx index 43003a45f..9e006534b 100644 --- a/client/src/components/dms-post-form/cdklike-dms-post-form.jsx +++ b/client/src/components/dms-post-form/cdklike-dms-post-form.jsx @@ -404,7 +404,7 @@ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode, = + setCountsOnly(e.target.checked)}> + {t("parts.labels.view_counts_only")} + + setViewTimeStamp(e.target.checked)}> + {t("parts.labels.view_timestamps")} + { diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index c95876666..8d44f02f1 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -48,6 +48,7 @@ "arrivedon": "Arrived on: ", "arrivingjobs": "Arriving Jobs", "blocked": "Blocked", + "bp": "B/P", "cancelledappointment": "Canceled appointment for: ", "completingjobs": "Completing Jobs", "dataconsistency": "<0>{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.", @@ -59,18 +60,17 @@ "noarrivingjobs": "No Jobs are arriving.", "nocompletingjobs": "No Jobs scheduled for completion.", "nodateselected": "No date has been selected.", + "owner": "Owner", "priorappointments": "Previous Appointments", "reminder": "This is {{shopname}} reminding you about an appointment on {{date}} at {{time}}. Please let us know if you are not able to make the appointment. We look forward to seeing you soon. ", + "ro_number": "RO #", + "scheduled_completion": "Scheduled Completion", "scheduledfor": "Scheduled appointment for: ", "severalerrorsfound": "Several Jobs have issues which may prevent accurate smart scheduling. Click to expand.", "smartscheduling": "Smart Scheduling", "smspaymentreminder": "This is {{shopname}} reminding you about your remaining balance of {{amount}}. To pay for the said balance click the link {{payment_link}}.", "suggesteddates": "Suggested Dates", - "ro_number": "RO #", - "owner": "Owner", - "vehicle": "Vehicle", - "bp": "B/P", - "scheduled_completion": "Scheduled Completion" + "vehicle": "Vehicle" }, "successes": { "canceled": "Appointment canceled successfully.", @@ -90,6 +90,11 @@ "actions": "Actions" } }, + "audio": { + "manager": { + "description": "Click anywhere to enable the message ding." + } + }, "audit": { "fields": { "cc": "CC", @@ -149,11 +154,6 @@ "tasks_updated": "Task '{{title}}' updated by {{updatedBy}}" } }, - "audio": { - "manager": { - "description": "Click anywhere to enable the message ding." - } - }, "billlines": { "actions": { "newline": "New Line" @@ -281,9 +281,9 @@ }, "errors": { "creatingdefaultview": "Error creating default view.", + "duplicate_insurance_company": "Duplicate insurance company name. Each insurance company name must be unique", "loading": "Unable to load shop details. Please call technical support.", - "saving": "Error encountered while saving. {{message}}", - "duplicate_insurance_company": "Duplicate insurance company name. Each insurance company name must be unique" + "saving": "Error encountered while saving. {{message}}" }, "fields": { "ReceivableCustomField": "QBO Receivable Custom Field {{number}}", @@ -564,21 +564,18 @@ "responsibilitycenter_tax_tier": "Tax {{typeNum}} Tier {{typeNumIterator}}", "responsibilitycenter_tax_type": "Tax {{typeNum}} Type", "responsibilitycenters": { - "gogcode": "GOG Code (BreakOut)", - "item_type": "Item Type", - "item_type_gog": "GOG", - "item_type_paint": "Paint Materials", - "item_type_freight": "Freight", - "taxable_flag": "Taxable?", - "taxable": "Taxable", - "nontaxable": "Non-taxable", "ap": "Accounts Payable", "ar": "Accounts Receivable", "ats": "ATS", "federal_tax": "Federal Tax", "federal_tax_itc": "Federal Tax Credit", + "gogcode": "GOG Code (BreakOut)", "gst_override": "GST Override Account #", "invoiceexemptcode": "QuickBooks US - Invoice Tax Exempt Code", + "item_type": "Item Type", + "item_type_freight": "Freight", + "item_type_gog": "GOG", + "item_type_paint": "Paint Materials", "itemexemptcode": "QuickBooks US - Line Item Tax Exempt Code", "la1": "LA1", "la2": "LA2", @@ -597,6 +594,7 @@ "local_tax": "Local Tax", "mapa": "Paint Materials", "mash": "Shop Materials", + "nontaxable": "Non-taxable", "paa": "Aftermarket", "pac": "Chrome", "pag": "Glass", @@ -617,6 +615,8 @@ "state": "State Tax Applies" }, "state_tax": "State Tax", + "taxable": "Taxable", + "taxable_flag": "Taxable?", "tow": "Towing" }, "schedule_end_time": "Schedule Ending Time", @@ -678,8 +678,6 @@ "zip_post": "Zip/Postal Code" }, "labels": { - "parts_shop_management": "Shop Management", - "parts_vendor_management": "Vendor Management", "2tiername": "Name => RO", "2tiersetup": "2 Tier Setup", "2tiersource": "Source => RO", @@ -702,11 +700,11 @@ "payers": "Payers" }, "cdk_dealerid": "CDK Dealer ID", - "rr_dealerid": "Reynolds Store Number", "costsmapping": "Costs Mapping", "dms_allocations": "DMS Allocations", "pbs_serialnumber": "PBS Serial Number", "profitsmapping": "Profits Mapping", + "rr_dealerid": "Reynolds Store Number", "title": "DMS" }, "emaillater": "Email Later", @@ -733,6 +731,8 @@ "followers": "Notifications" }, "orderstatuses": "Order Statuses", + "parts_shop_management": "Shop Management", + "parts_vendor_management": "Vendor Management", "partslocations": "Parts Locations", "partsscan": "Parts Scanning", "printlater": "Print Later", @@ -1228,8 +1228,6 @@ }, "general": { "actions": { - "select": "Select", - "optional": "Optional", "add": "Add", "autoupdate": "{{app}} will automatically update in {{time}} seconds. Please save all changes.", "calculate": "Calculate", @@ -1249,6 +1247,7 @@ "login": "Login", "next": "Next", "ok": "Ok", + "optional": "Optional", "previous": "Previous", "print": "Print", "refresh": "Refresh", @@ -1259,6 +1258,7 @@ "save": "Save", "saveandnew": "Save and New", "saveas": "Save As", + "select": "Select", "selectall": "Select All", "send": "Send", "sendbysms": "Send by SMS", @@ -1288,8 +1288,6 @@ "vehicle": "Vehicle" }, "labels": { - "selected": "Selected", - "settings": "Settings", "actions": "Actions", "areyousure": "Are you sure?", "barcode": "Barcode", @@ -1343,8 +1341,10 @@ "search": "Search...", "searchresults": "Results for {{search}}", "selectdate": "Select date...", + "selected": "Selected", "sendagain": "Send Again", "sendby": "Send By", + "settings": "Settings", "signin": "Sign In", "sms": "SMS", "status": "Status", @@ -1587,13 +1587,13 @@ "labels": { "adjustmenttobeadded": "Adjustment to be added: {{adjustment}}", "billref": "Latest Bill", + "bulk_location_help": "This will set the same location on all selected lines.", "convertedtolabor": "This line has been converted to labor. Ensure you adjust the profit center for the amount accordingly.", "edit": "Edit Line", "ioucreated": "IOU", "new": "New Line", "nostatus": "No Status", - "presets": "Jobline Presets", - "bulk_location_help": "This will set the same location on all selected lines." + "presets": "Jobline Presets" }, "successes": { "created": "Job line created successfully.", @@ -1701,9 +1701,9 @@ "actual_delivery": "Actual Delivery", "actual_in": "Actual In", "acv_amount": "ACV Amount", - "admin_clerk": "Admin Clerk", "adjustment_bottom_line": "Adjustments", "adjustmenthours": "Adjustment Hours", + "admin_clerk": "Admin Clerk", "alt_transport": "Alt. Trans.", "area_of_damage_impact": { "10": "Left Front Side", @@ -1784,9 +1784,8 @@ "ded_status": "Deductible Status", "depreciation_taxes": "Betterment/Depreciation/Taxes", "dms": { - "first_name": "First Name", - "last_name": "Last Name", "address": "Customer Address", + "advisor": "Advisor #", "amount": "Amount", "center": "Center", "control_type": { @@ -1799,12 +1798,13 @@ "dms_model_override": "Override DMS Make/Model", "dms_unsold": "New, Unsold Vehicle", "dms_wip_acctnumber": "Cost WIP DMS Acct #", + "first_name": "First Name", "id": "DMS ID", "inservicedate": "In Service Date", "journal": "Journal #", - "make_override": "Make Override", - "advisor": "Advisor #", + "last_name": "Last Name", "lines": "Posting Lines", + "make_override": "Make Override", "name1": "Customer Name", "payer": { "amount": "Amount", @@ -1945,7 +1945,7 @@ "amount": "Amount", "name": "Name" }, - "queued_for_parts": "Queued for Parts", + "queued_for_parts": "Queued", "rate_ats": "ATS Rate", "rate_ats_flat": "ATS Flat Rate", "rate_la1": "LA1", @@ -2447,6 +2447,7 @@ "labels": { "addlabel": "Add a label to this conversation.", "archive": "Archive", + "mark_unread": "Mark as unread", "maxtenimages": "You can only select up to a maximum of 10 images at a time.", "messaging": "Messaging", "no_consent": "Opted-out", @@ -2459,8 +2460,7 @@ "selectmedia": "Select Media", "sentby": "Sent by {{by}} at {{time}}", "typeamessage": "Send a message...", - "unarchive": "Unarchive", - "mark_unread": "Mark as unread" + "unarchive": "Unarchive" }, "render": { "conversation_list": "Conversation List" @@ -2614,20 +2614,20 @@ "name": "Owner Details" }, "labels": { + "cell": "Cell", "create_new": "Create a new owner record.", "deleteconfirm": "Are you sure you want to delete this owner? This cannot be undone.", + "email": "Email", "existing_owners": "Existing Owners", "fromclaim": "Current Claim", "fromowner": "Historical Owner Record", - "relatedjobs": "Related Jobs", - "updateowner": "Update Owner", - "work": "Work", "home": "Home", - "cell": "Cell", "other": "Other", - "email": "Email", "phone": "Phone", - "sms": "SMS" + "relatedjobs": "Related Jobs", + "sms": "SMS", + "updateowner": "Update Owner", + "work": "Work" }, "successes": { "delete": "Owner deleted successfully.", @@ -2638,6 +2638,10 @@ "actions": { "order": "Order Parts", "orderinhouse": "Order as In House" + }, + "labels": { + "view_counts_only": "View Parts Counts Only", + "view_timestamps": "Show timestamps" } }, "parts_dispatch": { @@ -2987,8 +2991,6 @@ "settings": "Error saving board settings: {{error}}" }, "labels": { - "click_for_statuses": "Click to view parts statuses", - "partsreceived": "Parts Received", "actual_in": "Actual In", "addnewprofile": "Add New Profile", "alert": "Alert", @@ -3007,6 +3009,7 @@ "card_size": "Card Size", "cardcolor": "Colored Cards", "cardsettings": "Card Settings", + "click_for_statuses": "Click to view parts statuses", "clm_no": "Claim Number", "comment": "Comment", "compact": "Compact Cards", @@ -3027,6 +3030,7 @@ "orientation": "Board Orientation", "ownr_nm": "Customer Name", "paintpriority": "P/P", + "partsreceived": "Parts Received", "partsstatus": "Parts Status", "production_note": "Production Note", "refinishhours": "R", @@ -3573,17 +3577,12 @@ } }, "titles": { - "parts_settings": "Parts Management Settings | {{app}}", - "simplified-parts-jobs": "Parts Management | {{app}}", "accounting-payables": "Payables | {{app}}", "accounting-payments": "Payments | {{app}}", "accounting-receivables": "Receivables | {{app}}", "all_tasks": "All Tasks | {{app}}", "app": "", "bc": { - "simplified-parts-jobs": "Jobs", - "parts": "Parts", - "parts_settings": "Settings", "accounting-payables": "Payables", "accounting-payments": "Payments", "accounting-receivables": "Receivables", @@ -3615,7 +3614,9 @@ "my_tasks": "My Tasks", "owner-detail": "{{name}}", "owners": "Owners", + "parts": "Parts", "parts-queue": "Parts Queue", + "parts_settings": "Settings", "payments-all": "All Payments", "phonebook": "Phonebook", "productionboard": "Production Board - Visual", @@ -3627,6 +3628,7 @@ "shop-csi": "CSI Responses", "shop-templates": "Shop Templates", "shop-vendors": "Vendors", + "simplified-parts-jobs": "Jobs", "tasks": "Tasks", "temporarydocs": "Temporary Documents", "timetickets": "Time Tickets", @@ -3662,7 +3664,9 @@ "my_tasks": "My Tasks | {{app}}", "owners": "All Owners | {{app}}", "owners-detail": "{{name}} | {{app}}", + "parts": "", "parts-queue": "Parts Queue | {{app}}", + "parts_settings": "Parts Management Settings | {{app}}", "payments-all": "Payments | {{app}}", "phonebook": "Phonebook | {{app}}", "productionboard": "Production Board - Visual | {{app}}", @@ -3678,6 +3682,7 @@ "shop-csi": "CSI Responses | {{app}}", "shop-templates": "Shop Templates | {{app}}", "shop_vendors": "Vendors | {{app}}", + "simplified-parts-jobs": "Parts Management | {{app}}", "tasks": "Tasks", "techconsole": "Technician Console | {{app}}", "techjobclock": "Technician Job Clock | {{app}}", @@ -3838,10 +3843,10 @@ "user": { "actions": { "changepassword": "Change Password", - "signout": "Sign Out", - "updateprofile": "Update Profile", + "dark_theme": "Switch to Dark Theme", "light_theme": "Switch to Light Theme", - "dark_theme": "Switch to Dark Theme" + "signout": "Sign Out", + "updateprofile": "Update Profile" }, "errors": { "updating": "Error updating user or association {{message}}" @@ -3855,14 +3860,14 @@ "labels": { "actions": "Actions", "changepassword": "Change Password", - "profileinfo": "Profile Info", - "user_settings": "User Settings", - "play_sound_for_new_messages": "Play a sound for new messages", - "notification_sound_on": "Sound is ON", - "notification_sound_off": "Sound is OFF", - "notification_sound_enabled": "Notification sound enabled", "notification_sound_disabled": "Notification sound disabled", - "notification_sound_help": "Toggle the ding for incoming chat messages." + "notification_sound_enabled": "Notification sound enabled", + "notification_sound_help": "Toggle the ding for incoming chat messages.", + "notification_sound_off": "Sound is OFF", + "notification_sound_on": "Sound is ON", + "play_sound_for_new_messages": "Play a sound for new messages", + "profileinfo": "Profile Info", + "user_settings": "User Settings" }, "successess": { "passwordchanged": "Password changed successfully. " diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 70cd2b5da..d91462672 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -48,6 +48,7 @@ "arrivedon": "Llegado el:", "arrivingjobs": "", "blocked": "", + "bp": "", "cancelledappointment": "Cita cancelada para:", "completingjobs": "", "dataconsistency": "", @@ -59,18 +60,17 @@ "noarrivingjobs": "", "nocompletingjobs": "", "nodateselected": "No se ha seleccionado ninguna fecha.", + "owner": "", "priorappointments": "Nombramientos previos", "reminder": "", + "ro_number": "", + "scheduled_completion": "", "scheduledfor": "Cita programada para:", "severalerrorsfound": "", "smartscheduling": "", "smspaymentreminder": "", "suggesteddates": "", - "ro_number": "", - "owner": "", - "vehicle": "", - "bp": "", - "scheduled_completion": "" + "vehicle": "" }, "successes": { "canceled": "Cita cancelada con éxito.", @@ -90,6 +90,11 @@ "actions": "Comportamiento" } }, + "audio": { + "manager": { + "description": "" + } + }, "audit": { "fields": { "cc": "", @@ -149,11 +154,6 @@ "tasks_updated": "" } }, - "audio": { - "manager": { - "description": "" - } - }, "billlines": { "actions": { "newline": "" @@ -281,9 +281,9 @@ }, "errors": { "creatingdefaultview": "", + "duplicate_insurance_company": "", "loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico.", - "saving": "", - "duplicate_insurance_company": "" + "saving": "" }, "fields": { "ReceivableCustomField": "", @@ -564,21 +564,18 @@ "responsibilitycenter_tax_tier": "", "responsibilitycenter_tax_type": "", "responsibilitycenters": { - "gogcode": "", - "item_type": "Item Type", - "item_type_gog": "", - "item_type_paint": "", - "item_type_freight": "", - "taxable_flag": "", - "taxable": "", - "nontaxable": "", "ap": "", "ar": "", "ats": "", "federal_tax": "", "federal_tax_itc": "", + "gogcode": "", "gst_override": "", "invoiceexemptcode": "", + "item_type": "Item Type", + "item_type_freight": "", + "item_type_gog": "", + "item_type_paint": "", "itemexemptcode": "", "la1": "", "la2": "", @@ -597,6 +594,7 @@ "local_tax": "", "mapa": "", "mash": "", + "nontaxable": "", "paa": "", "pac": "", "pag": "", @@ -617,6 +615,8 @@ "state": "" }, "state_tax": "", + "taxable": "", + "taxable_flag": "", "tow": "" }, "schedule_end_time": "", @@ -678,8 +678,6 @@ "zip_post": "" }, "labels": { - "parts_shop_management": "", - "parts_vendor_management": "", "2tiername": "", "2tiersetup": "", "2tiersource": "", @@ -702,11 +700,11 @@ "payers": "" }, "cdk_dealerid": "", - "rr_dealerid": "", "costsmapping": "", "dms_allocations": "", "pbs_serialnumber": "", "profitsmapping": "", + "rr_dealerid": "", "title": "" }, "emaillater": "", @@ -733,6 +731,8 @@ "followers": "" }, "orderstatuses": "", + "parts_shop_management": "", + "parts_vendor_management": "", "partslocations": "", "partsscan": "", "printlater": "", @@ -1247,6 +1247,7 @@ "login": "", "next": "", "ok": "", + "optional": "", "previous": "", "print": "", "refresh": "", @@ -1257,6 +1258,7 @@ "save": "Salvar", "saveandnew": "", "saveas": "", + "select": "", "selectall": "", "send": "", "sendbysms": "", @@ -1286,9 +1288,7 @@ "vehicle": "" }, "labels": { - "selected": "", "actions": "Comportamiento", - "settings": "", "areyousure": "", "barcode": "código de barras", "cancel": "", @@ -1341,8 +1341,10 @@ "search": "Buscar...", "searchresults": "", "selectdate": "", + "selected": "", "sendagain": "", "sendby": "", + "settings": "", "signin": "", "sms": "", "status": "", @@ -1585,13 +1587,13 @@ "labels": { "adjustmenttobeadded": "", "billref": "", + "bulk_location_help": "", "convertedtolabor": "", "edit": "Línea de edición", "ioucreated": "", "new": "Nueva línea", "nostatus": "", - "presets": "", - "bulk_location_help": "" + "presets": "" }, "successes": { "created": "", @@ -1700,8 +1702,8 @@ "actual_in": "Real en", "acv_amount": "", "adjustment_bottom_line": "Ajustes", - "admin_clerk": "", "adjustmenthours": "", + "admin_clerk": "", "alt_transport": "", "area_of_damage_impact": { "10": "", @@ -1782,9 +1784,8 @@ "ded_status": "Estado deducible", "depreciation_taxes": "Depreciación / Impuestos", "dms": { - "first_name": "", - "last_name": "", "address": "", + "advisor": "", "amount": "", "center": "", "control_type": { @@ -1795,21 +1796,23 @@ "dms_make": "", "dms_model": "", "dms_model_override": "", - "make_override": "", - "advisor": "", "dms_unsold": "", "dms_wip_acctnumber": "", + "first_name": "", "id": "", "inservicedate": "", "journal": "", + "last_name": "", "lines": "", + "make_override": "", "name1": "", "payer": { "amount": "", "control_type": "", "controlnumber": "", "dms_acctnumber": "", - "name": "" + "name": "", + "payer_type": "" }, "sale": "", "sale_dms_acctnumber": "", @@ -2444,6 +2447,7 @@ "labels": { "addlabel": "", "archive": "", + "mark_unread": "", "maxtenimages": "", "messaging": "Mensajería", "no_consent": "", @@ -2456,8 +2460,7 @@ "selectmedia": "", "sentby": "", "typeamessage": "Enviar un mensaje...", - "unarchive": "", - "mark_unread": "" + "unarchive": "" }, "render": { "conversation_list": "" @@ -2611,20 +2614,20 @@ "name": "" }, "labels": { + "cell": "", "create_new": "Crea un nuevo registro de propietario.", "deleteconfirm": "", + "email": "", "existing_owners": "Propietarios existentes", "fromclaim": "", "fromowner": "", - "relatedjobs": "", - "updateowner": "", - "work": "", "home": "", - "cell": "", "other": "", - "email": "", "phone": "", - "sms": "" + "relatedjobs": "", + "sms": "", + "updateowner": "", + "work": "" }, "successes": { "delete": "", @@ -2635,6 +2638,10 @@ "actions": { "order": "Pedido de piezas", "orderinhouse": "" + }, + "labels": { + "view_counts_only": "", + "view_timestamps": "" } }, "parts_dispatch": { @@ -2984,8 +2991,6 @@ "settings": "" }, "labels": { - "click_for_statuses": "", - "partsreceived": "", "actual_in": "", "addnewprofile": "", "alert": "", @@ -3004,6 +3009,7 @@ "card_size": "", "cardcolor": "", "cardsettings": "", + "click_for_statuses": "", "clm_no": "", "comment": "", "compact": "", @@ -3024,6 +3030,7 @@ "orientation": "", "ownr_nm": "", "paintpriority": "", + "partsreceived": "", "partsstatus": "", "production_note": "", "refinishhours": "", @@ -3570,18 +3577,12 @@ } }, "titles": { - "simplified-parts-jobs": "", - "parts": "", - "parts_settings": "", "accounting-payables": "", "accounting-payments": "", "accounting-receivables": "", "all_tasks": "", "app": "", "bc": { - "simplified-parts-jobs": "", - "parts": "", - "parts_settings": "", "accounting-payables": "", "accounting-payments": "", "accounting-receivables": "", @@ -3613,7 +3614,9 @@ "my_tasks": "", "owner-detail": "", "owners": "", + "parts": "", "parts-queue": "", + "parts_settings": "", "payments-all": "", "phonebook": "", "productionboard": "", @@ -3625,6 +3628,7 @@ "shop-csi": "", "shop-templates": "", "shop-vendors": "", + "simplified-parts-jobs": "", "tasks": "", "temporarydocs": "", "timetickets": "", @@ -3660,7 +3664,9 @@ "my_tasks": "", "owners": "Todos los propietarios | {{app}}", "owners-detail": "", + "parts": "", "parts-queue": "", + "parts_settings": "", "payments-all": "", "phonebook": "", "productionboard": "", @@ -3676,6 +3682,7 @@ "shop-csi": "", "shop-templates": "", "shop_vendors": "Vendedores | {{app}}", + "simplified-parts-jobs": "", "tasks": "", "techconsole": "{{app}}", "techjobclock": "{{app}}", @@ -3836,10 +3843,10 @@ "user": { "actions": { "changepassword": "", - "signout": "desconectar", - "updateprofile": "Actualización del perfil", + "dark_theme": "", "light_theme": "", - "dark_theme": "" + "signout": "desconectar", + "updateprofile": "Actualización del perfil" }, "errors": { "updating": "" @@ -3853,14 +3860,14 @@ "labels": { "actions": "", "changepassword": "", - "profileinfo": "", - "user_settings": "", - "play_sound_for_new_messages": "", - "notification_sound_on": "", - "notification_sound_off": "", - "notification_sound_enabled": "", "notification_sound_disabled": "", - "notification_sound_help": "" + "notification_sound_enabled": "", + "notification_sound_help": "", + "notification_sound_off": "", + "notification_sound_on": "", + "play_sound_for_new_messages": "", + "profileinfo": "", + "user_settings": "" }, "successess": { "passwordchanged": "" diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index e199a33b2..9542b4b16 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -48,6 +48,7 @@ "arrivedon": "Arrivé le:", "arrivingjobs": "", "blocked": "", + "bp": "", "cancelledappointment": "Rendez-vous annulé pour:", "completingjobs": "", "dataconsistency": "", @@ -59,18 +60,17 @@ "noarrivingjobs": "", "nocompletingjobs": "", "nodateselected": "Aucune date n'a été sélectionnée.", + "owner": "", "priorappointments": "Rendez-vous précédents", "reminder": "", + "ro_number": "", + "scheduled_completion": "", "scheduledfor": "Rendez-vous prévu pour:", "severalerrorsfound": "", "smartscheduling": "", "smspaymentreminder": "", "suggesteddates": "", - "ro_number": "", - "owner": "", - "vehicle": "", - "bp": "", - "scheduled_completion": "" + "vehicle": "" }, "successes": { "canceled": "Rendez-vous annulé avec succès.", @@ -90,6 +90,11 @@ "actions": "actes" } }, + "audio": { + "manager": { + "description": "" + } + }, "audit": { "fields": { "cc": "", @@ -149,11 +154,6 @@ "tasks_updated": "" } }, - "audio": { - "manager": { - "description": "" - } - }, "billlines": { "actions": { "newline": "" @@ -281,9 +281,9 @@ }, "errors": { "creatingdefaultview": "", + "duplicate_insurance_company": "", "loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique.", - "saving": "", - "duplicate_insurance_company": "" + "saving": "" }, "fields": { "ReceivableCustomField": "", @@ -564,21 +564,18 @@ "responsibilitycenter_tax_tier": "", "responsibilitycenter_tax_type": "", "responsibilitycenters": { - "gogcode": "", - "item_type": "Item Type", - "item_type_gog": "", - "item_type_paint": "", - "item_type_freight": "", - "taxable_flag": "", - "taxable": "", - "nontaxable": "", "ap": "", "ar": "", "ats": "", "federal_tax": "", "federal_tax_itc": "", + "gogcode": "", "gst_override": "", "invoiceexemptcode": "", + "item_type": "Item Type", + "item_type_freight": "", + "item_type_gog": "", + "item_type_paint": "", "itemexemptcode": "", "la1": "", "la2": "", @@ -597,6 +594,7 @@ "local_tax": "", "mapa": "", "mash": "", + "nontaxable": "", "paa": "", "pac": "", "pag": "", @@ -617,6 +615,8 @@ "state": "" }, "state_tax": "", + "taxable": "", + "taxable_flag": "", "tow": "" }, "schedule_end_time": "", @@ -678,8 +678,6 @@ "zip_post": "" }, "labels": { - "parts_shop_management": "", - "parts_vendor_management": "", "2tiername": "", "2tiersetup": "", "2tiersource": "", @@ -702,11 +700,11 @@ "payers": "" }, "cdk_dealerid": "", - "rr_dealerid": "", "costsmapping": "", "dms_allocations": "", "pbs_serialnumber": "", "profitsmapping": "", + "rr_dealerid": "", "title": "" }, "emaillater": "", @@ -733,6 +731,8 @@ "followers": "" }, "orderstatuses": "", + "parts_shop_management": "", + "parts_vendor_management": "", "partslocations": "", "partsscan": "", "printlater": "", @@ -1247,6 +1247,7 @@ "login": "", "next": "", "ok": "", + "optional": "", "previous": "", "print": "", "refresh": "", @@ -1257,6 +1258,7 @@ "save": "sauvegarder", "saveandnew": "", "saveas": "", + "select": "", "selectall": "", "send": "", "sendbysms": "", @@ -1286,8 +1288,6 @@ "vehicle": "" }, "labels": { - "selected": "", - "settings": "", "actions": "actes", "areyousure": "", "barcode": "code à barre", @@ -1341,8 +1341,10 @@ "search": "Chercher...", "searchresults": "", "selectdate": "", + "selected": "", "sendagain": "", "sendby": "", + "settings": "", "signin": "", "sms": "", "status": "", @@ -1585,13 +1587,13 @@ "labels": { "adjustmenttobeadded": "", "billref": "", + "bulk_location_help": "", "convertedtolabor": "", "edit": "Ligne d'édition", "ioucreated": "", "new": "Nouvelle ligne", "nostatus": "", - "presets": "", - "bulk_location_help": "" + "presets": "" }, "successes": { "created": "", @@ -1699,9 +1701,9 @@ "actual_delivery": "Livraison réelle", "actual_in": "En réel", "acv_amount": "", - "admin_clerk": "", "adjustment_bottom_line": "Ajustements", "adjustmenthours": "", + "admin_clerk": "", "alt_transport": "", "area_of_damage_impact": { "10": "", @@ -1782,9 +1784,8 @@ "ded_status": "Statut de franchise", "depreciation_taxes": "Amortissement / taxes", "dms": { - "first_name": "", - "last_name": "", "address": "", + "advisor": "", "amount": "", "center": "", "control_type": { @@ -1795,21 +1796,23 @@ "dms_make": "", "dms_model": "", "dms_model_override": "", - "make_override": "", - "advisor": "", "dms_unsold": "", "dms_wip_acctnumber": "", + "first_name": "", "id": "", "inservicedate": "", "journal": "", + "last_name": "", "lines": "", + "make_override": "", "name1": "", "payer": { "amount": "", "control_type": "", "controlnumber": "", "dms_acctnumber": "", - "name": "" + "name": "", + "payer_type": "" }, "sale": "", "sale_dms_acctnumber": "", @@ -2433,7 +2436,6 @@ "actions": { "link": "", "new": "", - "openchat": "" }, "errors": { @@ -2445,6 +2447,7 @@ "labels": { "addlabel": "", "archive": "", + "mark_unread": "", "maxtenimages": "", "messaging": "Messagerie", "no_consent": "", @@ -2457,8 +2460,7 @@ "selectmedia": "", "sentby": "", "typeamessage": "Envoyer un message...", - "unarchive": "", - "mark_unread": "" + "unarchive": "" }, "render": { "conversation_list": "" @@ -2612,20 +2614,20 @@ "name": "" }, "labels": { + "cell": "", "create_new": "Créez un nouvel enregistrement de propriétaire.", "deleteconfirm": "", + "email": "", "existing_owners": "Propriétaires existants", "fromclaim": "", "fromowner": "", - "relatedjobs": "", - "updateowner": "", - "work": "", "home": "", - "cell": "", "other": "", - "email": "", "phone": "", - "sms": "" + "relatedjobs": "", + "sms": "", + "updateowner": "", + "work": "" }, "successes": { "delete": "", @@ -2636,6 +2638,10 @@ "actions": { "order": "Commander des pièces", "orderinhouse": "" + }, + "labels": { + "view_counts_only": "", + "view_timestamps": "" } }, "parts_dispatch": { @@ -2985,8 +2991,6 @@ "settings": "" }, "labels": { - "click_for_statuses": "", - "partsreceived": "", "actual_in": "", "addnewprofile": "", "alert": "", @@ -3005,6 +3009,7 @@ "card_size": "", "cardcolor": "", "cardsettings": "", + "click_for_statuses": "", "clm_no": "", "comment": "", "compact": "", @@ -3025,6 +3030,7 @@ "orientation": "", "ownr_nm": "", "paintpriority": "", + "partsreceived": "", "partsstatus": "", "production_note": "", "refinishhours": "", @@ -3571,18 +3577,12 @@ } }, "titles": { - "simplified-parts-jobs": "", - "parts": "", - "parts_settings": "", "accounting-payables": "", "accounting-payments": "", "accounting-receivables": "", "all_tasks": "", "app": "", "bc": { - "simplified-parts-jobs": "", - "parts": "", - "parts_settings": "", "accounting-payables": "", "accounting-payments": "", "accounting-receivables": "", @@ -3614,7 +3614,9 @@ "my_tasks": "", "owner-detail": "", "owners": "", + "parts": "", "parts-queue": "", + "parts_settings": "", "payments-all": "", "phonebook": "", "productionboard": "", @@ -3626,6 +3628,7 @@ "shop-csi": "", "shop-templates": "", "shop-vendors": "", + "simplified-parts-jobs": "", "tasks": "", "temporarydocs": "", "timetickets": "", @@ -3661,7 +3664,9 @@ "my_tasks": "", "owners": "Tous les propriétaires | {{app}}", "owners-detail": "", + "parts": "", "parts-queue": "", + "parts_settings": "", "payments-all": "", "phonebook": "", "productionboard": "", @@ -3677,6 +3682,7 @@ "shop-csi": "", "shop-templates": "", "shop_vendors": "Vendeurs | {{app}}", + "simplified-parts-jobs": "", "tasks": "", "techconsole": "{{app}}", "techjobclock": "{{app}}", @@ -3837,10 +3843,10 @@ "user": { "actions": { "changepassword": "", - "signout": "Déconnexion", - "updateprofile": "Mettre à jour le profil", + "dark_theme": "", "light_theme": "", - "dark_theme": "" + "signout": "Déconnexion", + "updateprofile": "Mettre à jour le profil" }, "errors": { "updating": "" @@ -3854,14 +3860,14 @@ "labels": { "actions": "", "changepassword": "", - "profileinfo": "", - "user_settings": "", - "play_sound_for_new_messages": "", - "notification_sound_on": "", - "notification_sound_off": "", - "notification_sound_enabled": "", "notification_sound_disabled": "", - "notification_sound_help": "" + "notification_sound_enabled": "", + "notification_sound_help": "", + "notification_sound_off": "", + "notification_sound_on": "", + "play_sound_for_new_messages": "", + "profileinfo": "", + "user_settings": "" }, "successess": { "passwordchanged": "" diff --git a/client/src/utils/DateFormatter.jsx b/client/src/utils/DateFormatter.jsx index 574c1d229..f261a0d6d 100644 --- a/client/src/utils/DateFormatter.jsx +++ b/client/src/utils/DateFormatter.jsx @@ -5,8 +5,10 @@ export function DateFormatter(props) { return props.children ? dayjs(props.children).format(props.includeDay ? "ddd MM/DD/YYYY" : "MM/DD/YYYY") : null; } -export function DateTimeFormatter(props) { - return props.children ? dayjs(props.children).format(props.format ? props.format : "MM/DD/YYYY hh:mm a") : null; +export function DateTimeFormatter({ hideTime, ...props }) { + return props.children + ? dayjs(props.children).format(props.format ? props.format : `MM/DD/YYYY${hideTime ? "" : " hh:mm a"}`) + : null; } export function DateTimeFormatterFunction(date) { @@ -17,11 +19,11 @@ export function TimeFormatter(props) { return props.children ? dayjs(props.children).format(props.format ? props.format : "hh:mm a") : null; } -export function TimeAgoFormatter(props) { +export function TimeAgoFormatter({ removeAgoString = false, ...props }) { const m = dayjs(props.children); return props.children ? ( - - {m.fromNow()} + + {m.fromNow(removeAgoString)} ) : null; } From e5b7fcb919aded77bf54f51dbf901c8397fe033a Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 12:02:11 -0800 Subject: [PATCH 06/16] IO-3531 Change global apollo config setting to prevent rerenders. --- .../card-payment-modal/card-payment-modal.component.jsx | 1 - client/src/components/chat-popup/chat-popup.component.jsx | 1 - .../jobs-detail-header-actions.component.jsx | 1 - .../notification-center/notification-center.container.jsx | 1 - client/src/utils/GraphQLClient.js | 3 ++- 5 files changed, 2 insertions(+), 5 deletions(-) diff --git a/client/src/components/card-payment-modal/card-payment-modal.component.jsx b/client/src/components/card-payment-modal/card-payment-modal.component.jsx index ab3384fce..11aed2922 100644 --- a/client/src/components/card-payment-modal/card-payment-modal.component.jsx +++ b/client/src/components/card-payment-modal/card-payment-modal.component.jsx @@ -57,7 +57,6 @@ const CardPaymentModalComponent = ({ QUERY_RO_AND_OWNER_BY_JOB_PKS, { fetchPolicy: "network-only", - notifyOnNetworkStatusChange: true } ); diff --git a/client/src/components/chat-popup/chat-popup.component.jsx b/client/src/components/chat-popup/chat-popup.component.jsx index f0805345a..1a3c03f2d 100644 --- a/client/src/components/chat-popup/chat-popup.component.jsx +++ b/client/src/components/chat-popup/chat-popup.component.jsx @@ -47,7 +47,6 @@ export function ChatPopupComponent({ chatVisible, selectedConversation, toggleCh const [getConversations, { loading, data, refetch, called }] = useLazyQuery(CONVERSATION_LIST_QUERY, { fetchPolicy: "network-only", nextFetchPolicy: "network-only", - notifyOnNetworkStatusChange: true, ...(pollInterval > 0 ? { pollInterval } : {}) }); diff --git a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx index 509539e8d..bdb08ef01 100644 --- a/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx +++ b/client/src/components/jobs-detail-header-actions/jobs-detail-header-actions.component.jsx @@ -157,7 +157,6 @@ export function JobsDetailHeaderActions({ variables: watcherVars, skip: !jobId, fetchPolicy: "cache-first", - notifyOnNetworkStatusChange: true }); const jobWatchersCount = jobWatchersData?.job_watchers?.length ?? job?.job_watchers?.length ?? 0; diff --git a/client/src/components/notification-center/notification-center.container.jsx b/client/src/components/notification-center/notification-center.container.jsx index c0bec13d0..109591228 100644 --- a/client/src/components/notification-center/notification-center.container.jsx +++ b/client/src/components/notification-center/notification-center.container.jsx @@ -56,7 +56,6 @@ const NotificationCenterContainer = ({ visible, onClose, bodyshop, unreadCount, where: whereClause }, fetchPolicy: "cache-and-network", - notifyOnNetworkStatusChange: true, errorPolicy: "all", pollInterval: isConnected ? 0 : day.duration(NOTIFICATION_POLL_INTERVAL_SECONDS, "seconds").asMilliseconds(), skip: skipQuery diff --git a/client/src/utils/GraphQLClient.js b/client/src/utils/GraphQLClient.js index a03d82eec..299c0e547 100644 --- a/client/src/utils/GraphQLClient.js +++ b/client/src/utils/GraphQLClient.js @@ -248,7 +248,8 @@ const client = new ApolloClient({ watchQuery: { fetchPolicy: "network-only", nextFetchPolicy: "network-only", - errorPolicy: "ignore" + errorPolicy: "ignore", + notifyOnNetworkStatusChange: false }, query: { fetchPolicy: "network-only", From 87d8a5d7463c648f18b11d555638efcdc58f662e Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 12:22:58 -0800 Subject: [PATCH 07/16] IO-3538 Resolve missing id on receive return. --- .../bill-cm-returns-table/bill-cm-returns-table.component.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/components/bill-cm-returns-table/bill-cm-returns-table.component.jsx b/client/src/components/bill-cm-returns-table/bill-cm-returns-table.component.jsx index 8a2263bda..951795a7b 100644 --- a/client/src/components/bill-cm-returns-table/bill-cm-returns-table.component.jsx +++ b/client/src/components/bill-cm-returns-table/bill-cm-returns-table.component.jsx @@ -52,6 +52,9 @@ export default function BillCmdReturnsTableComponent({ form, returnLoading, retu {fields.map((field, index) => ( + Date: Mon, 2 Feb 2026 12:38:29 -0800 Subject: [PATCH 08/16] IO-3539 resolve print center popoves. --- .../job-3rd-party-modal/job-3rd-party-modal.component.jsx | 2 +- .../print-center-jobs-labels.component.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx index 56b0d1c65..4e0d2fcd5 100644 --- a/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx +++ b/client/src/components/job-3rd-party-modal/job-3rd-party-modal.component.jsx @@ -97,7 +97,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) { return ( <> - + document.body}>
diff --git a/client/src/components/print-center-jobs-labels/print-center-jobs-labels.component.jsx b/client/src/components/print-center-jobs-labels/print-center-jobs-labels.component.jsx index 396c3dde5..9a10eceef 100644 --- a/client/src/components/print-center-jobs-labels/print-center-jobs-labels.component.jsx +++ b/client/src/components/print-center-jobs-labels/print-center-jobs-labels.component.jsx @@ -108,7 +108,7 @@ export function PrintCenterJobsLabels({ jobId }) { ); return ( - + trigger.parentElement}> ); From ff73a146105cfdef9e0b2618ec5ab71cb1d266ca Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 12:55:29 -0800 Subject: [PATCH 09/16] IO-3532 resolve tooltip on owner name. --- .../owner-name-display/owner-name-display.component.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/components/owner-name-display/owner-name-display.component.jsx b/client/src/components/owner-name-display/owner-name-display.component.jsx index 15172bd12..cc799f53d 100644 --- a/client/src/components/owner-name-display/owner-name-display.component.jsx +++ b/client/src/components/owner-name-display/owner-name-display.component.jsx @@ -22,10 +22,14 @@ export function OwnerNameDisplay({ bodyshop, ownerObject, withToolTip = false }) returnString = `${ownerObject?.ownr_ln || ""}, ${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_co_nm || ""}`.trim(); } else { - return `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${ownerObject.ownr_co_nm || ""}`.trim(); + returnString = `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${ownerObject.ownr_co_nm || ""}`.trim(); } if (withToolTip) { - return {returnString}; + return ( + + {returnString} + + ); } else { return returnString; } From 1f726aca4da562ba5ff2f68c852e1b50ea932acd Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 16:14:44 -0500 Subject: [PATCH 10/16] feature/IO-3532-parts-queue-ui-adjustments --- .../job-parts-queue-count.component.jsx | 74 +++++++++++-------- .../job-parts-received.component.jsx | 2 +- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx index 6921d4305..771d36f36 100644 --- a/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx +++ b/client/src/components/job-parts-queue-count/job-parts-queue-count.component.jsx @@ -3,14 +3,11 @@ import { Tooltip } from "antd"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { useTranslation } from "react-i18next"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop }); -const mapDispatchToProps = () => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); +const mapDispatchToProps = () => ({}); const colorMap = { gray: { bg: "#fafafa", border: "#d9d9d9", text: "#000000" }, @@ -21,8 +18,9 @@ const colorMap = { orange: { bg: "#fff7e6", border: "#ffd591", text: "#d46b08" } }; -function CompactTag({ color = "gray", children }) { +function CompactTag({ color = "gray", children, tooltip = "" }) { const colors = colorMap[color] || colorMap.gray; + return ( - {children} + {children} ); } @@ -49,15 +46,20 @@ function CompactTag({ color = "gray", children }) { export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount); export function JobPartsQueueCount({ bodyshop, parts }) { - const { t } = useTranslation(); const partsStatus = useMemo(() => { if (!parts) return null; + const statusKeys = ["default_bo", "default_ordered", "default_received", "default_returned"]; + return parts.reduce( (acc, val) => { if (val.part_type === "PAS" || val.part_type === "PASL") return acc; - acc.total = acc.total + val.count; - acc[val.status] = acc[val.status] + val.count; + + acc.total += val.count; + + // NOTE: if val.status is null, object key becomes "null" + acc[val.status] = (acc[val.status] ?? 0) + val.count; + return acc; }, { @@ -68,28 +70,38 @@ export function JobPartsQueueCount({ bodyshop, parts }) { ); }, [bodyshop, parts]); - if (!parts) return null; - return ( -
- - {partsStatus.total} - - - {partsStatus["null"]} - + if (!parts || !partsStatus) return null; - - {partsStatus[bodyshop.md_order_statuses.default_bo]} - - - {partsStatus[bodyshop.md_order_statuses.default_ordered]} - - - {partsStatus[bodyshop.md_order_statuses.default_received]} - - - {partsStatus[bodyshop.md_order_statuses.default_returned]} - + return ( +
+ + {partsStatus.total} + + + + {partsStatus["null"]} + + + + {partsStatus[bodyshop.md_order_statuses.default_bo]} + + + + {partsStatus[bodyshop.md_order_statuses.default_ordered]} + + + {partsStatus[bodyshop.md_order_statuses.default_received]} + + + {partsStatus[bodyshop.md_order_statuses.default_returned]} +
); } diff --git a/client/src/components/job-parts-received/job-parts-received.component.jsx b/client/src/components/job-parts-received/job-parts-received.component.jsx index 27ca915b2..dc6c42cbc 100644 --- a/client/src/components/job-parts-received/job-parts-received.component.jsx +++ b/client/src/components/job-parts-received/job-parts-received.component.jsx @@ -83,7 +83,7 @@ export function JobPartsReceived({ trigger={["click"]} placement={popoverPlacement} content={ -
+
} From e01a2af5a46334245f9345365244fb619531cc10 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 16:44:49 -0500 Subject: [PATCH 11/16] feature/IO-3542-fix-searches --- .../global-search/global-search.component.jsx | 11 ++++++++--- .../owner-search-select.component.jsx | 5 +++-- .../vehicle-search-select.component.jsx | 5 +++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/src/components/global-search/global-search.component.jsx b/client/src/components/global-search/global-search.component.jsx index 9cdafaf04..665245070 100644 --- a/client/src/components/global-search/global-search.component.jsx +++ b/client/src/components/global-search/global-search.component.jsx @@ -14,8 +14,11 @@ export default function GlobalSearch() { const [callSearch, { loading, error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY); const navigate = useNavigate(); - const executeSearch = (v) => { - if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v); + const executeSearch = (variables) => { + if (variables?.search !== "" && variables?.search?.length >= 3) + callSearch({ + variables + }); }; const debouncedExecuteSearch = _.debounce(executeSearch, 750); @@ -157,7 +160,9 @@ export default function GlobalSearch() { return ( { if (e.key !== "Enter") return; diff --git a/client/src/components/owner-search-select/owner-search-select.component.jsx b/client/src/components/owner-search-select/owner-search-select.component.jsx index 9f6f5030a..bc5d7b105 100644 --- a/client/src/components/owner-search-select/owner-search-select.component.jsx +++ b/client/src/components/owner-search-select/owner-search-select.component.jsx @@ -16,9 +16,10 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => { SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE ); - const executeSearch = (v) => { - if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch({ variables: v.variables }); + const executeSearch = (variables) => { + if (variables?.search !== "" && variables?.search?.length >= 2) callSearch({ variables }); }; + const debouncedExecuteSearch = _.debounce(executeSearch, 500); const handleSearch = (value) => { diff --git a/client/src/components/vehicle-search-select/vehicle-search-select.component.jsx b/client/src/components/vehicle-search-select/vehicle-search-select.component.jsx index cffe871a5..b11851588 100644 --- a/client/src/components/vehicle-search-select/vehicle-search-select.component.jsx +++ b/client/src/components/vehicle-search-select/vehicle-search-select.component.jsx @@ -18,9 +18,10 @@ const VehicleSearchSelect = ({ value, onChange, onBlur, disabled, ref }) => { SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE ); - const executeSearch = (v) => { - if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch({ variables: v.variables }); + const executeSearch = (variables) => { + if (variables?.search !== "" && variables?.search?.length >= 2) callSearch({ variables }); }; + const debouncedExecuteSearch = _.debounce(executeSearch, 500); const handleSearch = (value) => { From ae05692c46a2389cc1dc51e1b802565ba13716c0 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 2 Feb 2026 13:45:25 -0800 Subject: [PATCH 12/16] IO-3531 remove loading on parts order page. --- .../parts-order-modal.container.jsx | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/client/src/components/parts-order-modal/parts-order-modal.container.jsx b/client/src/components/parts-order-modal/parts-order-modal.container.jsx index 1d68645be..d5b647117 100644 --- a/client/src/components/parts-order-modal/parts-order-modal.container.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal.container.jsx @@ -17,7 +17,6 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import AlertComponent from "../alert/alert.component"; -import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import PartsOrderModalComponent from "./parts-order-modal.component"; import axios from "axios"; import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react"; @@ -66,7 +65,7 @@ export function PartsOrderModalContainer({ const sendTypeState = useState("e"); const sendType = sendTypeState[0]; - const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, { + const { error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, { skip: !open, variables: { jobId: jobId }, fetchPolicy: "network-only", @@ -372,6 +371,7 @@ export function PartsOrderModalContainer({ } }, [open, linesToOrder, form]); + //This used to have a loading component spinner for the vendor data. With Apollo 4, the NetworkState isn't emitting correctly, so loading just gets set to true the second time, and no longer works as expected. return ( {error ? : null} - {loading ? ( - - ) : ( - - )} + ); From 217a0b84ac7ff582ab78b9b3c844a4cf35675aa2 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 17:04:06 -0500 Subject: [PATCH 13/16] bugfix/IO-3533 - Fix --- .../bill-form/bill-form.lines.component.jsx | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/client/src/components/bill-form/bill-form.lines.component.jsx b/client/src/components/bill-form/bill-form.lines.component.jsx index 3d8967348..8599970b1 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -90,6 +90,7 @@ export function BillEnterModalLinesComponent({ }); }; + // Only fill actual_cost when the user forward-tabs out of Retail (actual_price) const autofillActualCost = (index) => { Promise.resolve().then(() => { const retailRaw = form.getFieldValue(["billlines", index, "actual_price"]); @@ -164,10 +165,9 @@ export function BillEnterModalLinesComponent({ }} allowRemoved={form.getFieldValue("is_credit_memo") || false} onSelect={(value, opt) => { - const d = normalizeDiscount(discount); - const retail = Number(opt.cost); - const computedActual = Number.isFinite(retail) ? round2(retail * (1 - d)) : null; - + // IMPORTANT: + // Do NOT autofill actual_cost here. It should only fill when the user forward-tabs + // from Retail (actual_price) -> Actual Cost (actual_cost). setFieldsValue({ billlines: (getFieldValue("billlines") || []).map((item, idx) => { if (idx !== index) return item; @@ -178,7 +178,7 @@ export function BillEnterModalLinesComponent({ quantity: opt.part_qty || 1, actual_price: opt.cost, original_actual_price: opt.cost, - actual_cost: isBlank(item.actual_cost) ? computedActual : item.actual_cost, + // actual_cost intentionally untouched here cost_center: opt.part_type ? bodyshopHasDmsKey(bodyshop) ? opt.part_type !== "PAE" @@ -251,10 +251,9 @@ export function BillEnterModalLinesComponent({ autofillActualCost(index)} + // NOTE: Autofill should only happen on forward Tab out of Retail onKeyDown={(e) => { - if (e.key === "Tab") autofillActualCost(index); + if (e.key === "Tab" && !e.shiftKey) autofillActualCost(index); }} /> ), @@ -330,10 +329,7 @@ export function BillEnterModalLinesComponent({ disabled={disabled} controls={false} style={{ width: "100%", height: CONTROL_HEIGHT }} - // TODO: Removed at customers request - // NOTE: onFocus autofill behavior disabled per user request (revert to tab-only behavior) - // This can be re-enabled as a user preference in the future - // onFocus={() => autofillActualCost(index)} + // NOTE: No auto-fill on focus/blur; only triggered from Retail on Tab />
From a84ad4ee327939451fd090e4e94a48fbed73d098 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 17:10:56 -0500 Subject: [PATCH 14/16] hotfix/2026-02-02 - remove check on missing line ids --- .../parts-order-modal/parts-order-modal.container.jsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/src/components/parts-order-modal/parts-order-modal.container.jsx b/client/src/components/parts-order-modal/parts-order-modal.container.jsx index d5b647117..d2e3deba3 100644 --- a/client/src/components/parts-order-modal/parts-order-modal.container.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal.container.jsx @@ -93,16 +93,6 @@ export function PartsOrderModalContainer({ }; }); - const missingIdx = forcedLines.findIndex((l) => !l?.job_line_id); - if (missingIdx !== -1) { - notification.error({ - title: t("parts_orders.errors.creating"), - description: `Missing job_line_id for parts line #${missingIdx + 1}` - }); - setSaving(false); - return; - } - let insertResult; try { insertResult = await insertPartOrder({ From 52171209947e9b52d36f72159f0615767ad71de5 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 17:39:47 -0500 Subject: [PATCH 15/16] hotfix/2026-02-02 - Parts order manual discounting box --- ...rts-order-modal-price-change.component.jsx | 179 ++++++++++-------- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 4 files changed, 106 insertions(+), 76 deletions(-) diff --git a/client/src/components/parts-order-modal/parts-order-modal-price-change.component.jsx b/client/src/components/parts-order-modal/parts-order-modal-price-change.component.jsx index aa6ef3384..dc613245b 100644 --- a/client/src/components/parts-order-modal/parts-order-modal-price-change.component.jsx +++ b/client/src/components/parts-order-modal/parts-order-modal-price-change.component.jsx @@ -1,94 +1,121 @@ import { DownOutlined } from "@ant-design/icons"; -import { Dropdown, InputNumber, Space } from "antd"; +import { Button, Divider, Dropdown, InputNumber, Space, theme } from "antd"; +import { useState } from "react"; import { useTranslation } from "react-i18next"; import { logImEXEvent } from "../../firebase/firebase.utils"; +const DISCOUNT_PRESETS = [5, 10, 15, 20, 25, 40]; + export default function PartsOrderModalPriceChange({ form, field }) { const { t } = useTranslation(); - const menu = { - items: [ - { - key: "5", - label: t("parts_orders.labels.discount", { percent: "5%" }) - }, - { - key: "10", - label: t("parts_orders.labels.discount", { percent: "10%" }) - }, - { - key: "15", - label: t("parts_orders.labels.discount", { percent: "15%" }) - }, - { - key: "20", - label: t("parts_orders.labels.discount", { percent: "20%" }) - }, - { - key: "25", - label: t("parts_orders.labels.discount", { percent: "25%" }) - }, - { - key: "40", - label: t("parts_orders.labels.discount", { percent: "40%" }) - }, - { - key: "custom", - label: ( - - e.stopPropagation()} - onKeyUp={(e) => { - if (e.key === "Enter") { - const values = form.getFieldsValue(); - const { parts_order_lines } = values; + const { token } = theme.useToken(); - form.setFieldsValue({ - parts_order_lines: { - data: parts_order_lines.data.map((p, idx) => { - if (idx !== field.name) return p; - console.log(p, e.target.value, (p.act_price || 0) * ((100 - (e.target.value || 0)) / 100)); - return { - ...p, - act_price: (p.act_price || 0) * ((100 - (e.target.value || 0)) / 100) - }; - }) - } - }); - e.target.value = 0; - } - }} - min={0} - max={100} - /> - % - - ) + const [open, setOpen] = useState(false); + const [customPercent, setCustomPercent] = useState(0); + + const applyDiscountPercent = (percent) => { + const pct = Number(percent) || 0; + + const values = form.getFieldsValue(); + const parts_order_lines = values?.parts_order_lines; + const data = Array.isArray(parts_order_lines?.data) ? parts_order_lines.data : []; + if (!data.length) return; + + form.setFieldsValue({ + parts_order_lines: { + data: data.map((p, idx) => { + if (idx !== field.name) return p; + return { + ...p, + act_price: (p.act_price || 0) * ((100 - pct) / 100) + }; + }) } - ], + }); + }; + + const applyCustom = () => { + logImEXEvent("parts_order_manual_discount", {}); + applyDiscountPercent(customPercent); + setCustomPercent(0); + setOpen(false); + }; + + const menu = { + // Kill the menu “card” styling so our wrapper becomes the single card. + style: { + background: "transparent", + boxShadow: "none" + }, + items: DISCOUNT_PRESETS.map((pct) => ({ + key: String(pct), + label: t("parts_orders.labels.discount", { percent: `${pct}%` }) + })), onClick: ({ key }) => { logImEXEvent("parts_order_manual_discount", {}); - if (key === "custom") return; - const values = form.getFieldsValue(); - const { parts_order_lines } = values; - form.setFieldsValue({ - parts_order_lines: { - data: parts_order_lines.data.map((p, idx) => { - if (idx !== field.name) return p; - return { - ...p, - act_price: (p.act_price || 0) * ((100 - key) / 100) - }; - }) - } - }); + applyDiscountPercent(key); + setOpen(false); } }; return ( - + setOpen(nextOpen)} + getPopupContainer={(triggerNode) => triggerNode?.parentElement ?? document.body} + popupRender={(menus) => ( +
e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + > + {menus} + + + +
+ + (v === null || v === undefined ? "" : `${v}%`)} + parser={(v) => + String(v ?? "") + .replace("%", "") + .trim() + } + onChange={(v) => setCustomPercent(v ?? 0)} + onKeyDown={(e) => { + e.stopPropagation(); + if (e.key === "Enter") { + e.preventDefault(); + applyCustom(); + } + }} + /> + + +
+
+ )} + > - % - + %
); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 8d44f02f1..af14320db 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1288,6 +1288,7 @@ "vehicle": "Vehicle" }, "labels": { + "apply": "Apply", "actions": "Actions", "areyousure": "Are you sure?", "barcode": "Barcode", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index d91462672..5c7a31350 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1288,6 +1288,7 @@ "vehicle": "" }, "labels": { + "apply": "", "actions": "Comportamiento", "areyousure": "", "barcode": "código de barras", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 9542b4b16..e74e06bf0 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1288,6 +1288,7 @@ "vehicle": "" }, "labels": { + "apply": "", "actions": "actes", "areyousure": "", "barcode": "code à barre", From 2746421c09336c8ebb22a35c45a91359ffae706a Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 2 Feb 2026 17:48:03 -0500 Subject: [PATCH 16/16] hotfix/2026-02-02 - --- client/src/pages/tech/tech.page.component.jsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client/src/pages/tech/tech.page.component.jsx b/client/src/pages/tech/tech.page.component.jsx index 678536ae4..4150f567f 100644 --- a/client/src/pages/tech/tech.page.component.jsx +++ b/client/src/pages/tech/tech.page.component.jsx @@ -17,7 +17,10 @@ import "./tech.page.styles.scss"; import UpsellComponent, { upsellEnum } from "../../components/upsell/upsell.component.jsx"; import { lazyDev } from "../../utils/lazyWithPreload.jsx"; -const TimeTicketModalContainer = lazyDev(() => import("../../components/time-ticket-modal/time-ticket-modal.container")); +const NoteUpsertModal = lazyDev(() => import("../../components/note-upsert-modal/note-upsert-modal.container.jsx")); +const TimeTicketModalContainer = lazyDev( + () => import("../../components/time-ticket-modal/time-ticket-modal.container") +); const EmailOverlayContainer = lazyDev(() => import("../../components/email-overlay/email-overlay.container.jsx")); const PrintCenterModalContainer = lazyDev( () => import("../../components/print-center-modal/print-center-modal.container") @@ -34,7 +37,9 @@ const TimeTicketModalTask = lazyDev( const TechAssignedProdJobs = lazyDev(() => import("../tech-assigned-prod-jobs/tech-assigned-prod-jobs.component")); const TechDispatchedParts = lazyDev(() => import("../tech-dispatched-parts/tech-dispatched-parts.page")); -const TaskUpsertModalContainer = lazyDev(() => import("../../components/task-upsert-modal/task-upsert-modal.container")); +const TaskUpsertModalContainer = lazyDev( + () => import("../../components/task-upsert-modal/task-upsert-modal.container") +); const { Content } = Layout; @@ -70,6 +75,8 @@ export function TechPage({ technician }) { + +