From 53d556a62137fda891dd2f9f02f7004c47d3b0c5 Mon Sep 17 00:00:00 2001 From: Dave Date: Tue, 13 Jan 2026 22:28:43 -0500 Subject: [PATCH] feature/IO-3499-React-19: Bug Fixes / Checkpoint --- client/eslint.config.js | 19 ++- client/package-lock.json | 91 ++++++++++++ client/package.json | 2 + .../allocations-assignment.container.jsx | 8 +- .../allocations-bulk-assignment.container.jsx | 4 +- .../allocations-employee-label.container.jsx | 6 +- .../bill-delete-button.component.jsx | 10 +- .../bill-detail-edit.container.jsx | 2 +- .../bill-enter-modal.container.jsx | 20 +-- .../bill-form/bill-form.lines.component.jsx | 41 ++++-- .../bill-mark-exported-button.component.jsx | 8 +- .../bill-reexport-button.component.jsx | 8 +- .../billline-add-inventory.component.jsx | 10 +- .../card-payment-modal.component.jsx | 135 +++++++++++------- .../chat-label/chat-label.component.jsx | 8 +- .../chat-open-button.component.jsx | 2 +- .../contract-convert-to-ro.component.jsx | 8 +- .../contract-form-job-prefill.component.jsx | 4 +- .../contract-status-select.component.jsx | 19 ++- .../contracts-list.component.jsx | 15 +- .../courtesy-car-return-modal.container.jsx | 4 +- .../dashboard-grid.component.jsx | 4 +- .../dms-allocations-summary-ap.component.jsx | 11 +- .../dms-allocations-summary.component.jsx | 13 +- .../rr-dms-allocations-summary.component.jsx | 23 +-- .../documents-local-upload.utility.js | 5 +- .../documents-upload-imgproxy.component.jsx | 2 +- .../documents-upload-imgproxy.utility.js | 12 +- .../documents-upload.component.jsx | 5 +- .../documents-upload.utility.js | 17 ++- .../email-overlay/email-overlay.container.jsx | 6 +- client/src/components/eula/eula.component.jsx | 2 +- .../inventory-line-delete.component.jsx | 6 +- .../inventory-list.component.jsx | 25 ++-- .../inventory-upsert-modal.container.jsx | 8 +- .../job-at-change/job-at-change.component.jsx | 4 +- .../schedule-event.color.component.jsx | 6 +- .../schedule-event.component.jsx | 8 +- .../schedule-event.container.jsx | 12 +- .../schedule-event.note.component.jsx | 4 +- .../job-calculate-totals.component.jsx | 6 +- .../job-checklist-form.component.jsx | 14 +- .../job-create-iou.component.jsx | 5 +- .../job-lines-part-price-change.component.jsx | 15 +- .../job-detail-lines/job-lines.component.jsx | 10 +- .../job-employee-assignments.container.jsx | 8 +- .../job-line-bulk-assign.component.jsx | 10 +- .../job-line-convert-to-labor.component.jsx | 8 +- .../job-line-dispatch-button.component.jsx | 10 +- .../job-line-location-popup.component.jsx | 10 +- .../job-line-note-popup.component.jsx | 6 +- .../job-line-status-popup.component.jsx | 6 +- .../job-line-team-assignmnent.component.jsx | 6 +- .../job-lines-upsert-modal.container.jsx | 16 +-- .../job-remove-from-parts-queue.component.jsx | 6 +- .../job-scoreboard-add-button.component.jsx | 8 +- .../job-search-select.component.jsx | 68 +++++---- .../job-send-parts-price-change.component.jsx | 5 +- .../job-sync-button.component.jsx | 4 +- ...totals.cash-discount-display.component.jsx | 4 +- .../jobs-admin-change.status.component.jsx | 4 +- .../jobs-admin-class.component.jsx | 6 +- .../jobs-admin-dates.component.jsx | 6 +- .../jobs-admin-delete-intake.component.jsx | 12 +- .../jobs-admin-mark-reexport.component.jsx | 18 +-- ...jobs-admin-owner-reassociate.component.jsx | 6 +- .../jobs-admin-remove-ar.component.jsx | 6 +- .../jobs-admin-unvoid.component.jsx | 6 +- ...bs-admin-vehicle-reassociate.component.jsx | 6 +- .../jobs-available-scan.component.jsx | 4 +- .../jobs-available-table.component.jsx | 12 +- .../jobs-available-table.container.jsx | 28 ++-- .../jobs-change-status.component.jsx | 4 +- .../jobs-close-export-button.component.jsx | 27 ++-- .../jobs-convert-button.component.jsx | 4 +- ...il-header-actions.addtoproduction.util.jsx | 8 +- .../jobs-detail-header-actions.component.jsx | 56 ++++---- ...etail-header-actions.toggle-production.jsx | 4 +- .../jobs-detail-header.component.jsx | 2 +- ...bs-document-gallery.reassign.component.jsx | 15 +- ...obs-documents-gallery.delete.component.jsx | 9 +- ...nt-imgproxy-gallery.reassign.component.jsx | 4 +- ...ents-imgproxy-gallery.delete.component.jsx | 6 +- ...cuments-local-gallery.delete.component.jsx | 9 +- .../jobs-export-all-button.component.jsx | 27 ++-- .../jobs-mark-selected-exported.jsx | 6 +- .../jobs-notes/jobs-notes.container.jsx | 4 +- ...-allocations-adjustment-edit.component.jsx | 8 +- ...or-allocations-table.payroll.component.jsx | 15 +- .../note-upsert-modal.container.jsx | 8 +- .../notification-settings-form.component.jsx | 8 +- .../owner-detail-form.container.jsx | 12 +- .../owner-detail-update-jobs.component.jsx | 6 +- .../owners-list/owners-list.component.jsx | 18 ++- .../partner-ping/partner-ping.component.jsx | 4 +- .../parts-dispatch-expander.component.jsx | 5 +- .../parts-order-backorder-eta.component.jsx | 4 +- .../parts-order-cm-received.component.jsx | 8 +- ...-order-line-backorder-button.component.jsx | 4 +- ...arts-order-list-table-drawer.component.jsx | 2 +- .../parts-order-modal.container.jsx | 21 ++- .../parts-receive-modal.container.jsx | 8 +- .../parts-shop-info.container.jsx | 6 +- .../payable-export-all-button.component.jsx | 26 ++-- .../payable-export-button.component.jsx | 26 ++-- ...yable-mark-selected-exported.component.jsx | 8 +- .../payment-expanded-row.component.jsx | 5 +- .../payment-export-button.component.jsx | 26 ++-- .../payment-mark-export-button-component.jsx | 9 +- ...yment-mark-selected-exported.component.jsx | 8 +- .../payment-modal/payment-modal.container.jsx | 8 +- .../payment-reexport-button.component.jsx | 9 +- .../payments-export-all-button.component.jsx | 26 ++-- .../phonebook-form.container.jsx | 61 +++++--- .../print-center-jobs-labels.component.jsx | 2 +- .../production-board-kanban.component.jsx | 8 +- ...uction-board-kanban.settings.component.jsx | 5 +- ...n-list-columns.empassignment.component.jsx | 8 +- ...n-list-columns.lastcontacted.component.jsx | 10 +- .../production-list-detail.component.jsx | 2 +- ...oduction-list-config-manager.component.jsx | 6 +- .../production-remove-button.component.jsx | 6 +- .../production-sublets-manage.component.jsx | 8 +- .../profile-my/profile-my.component.jsx | 8 +- .../schedule-block-day.component.jsx | 4 +- .../schedule-job-modal.container.jsx | 20 +-- .../scoreboard-entry-edit.component.jsx | 8 +- .../scorebard-remove-button.component.jsx | 8 +- .../shop-employees-add-vacation.component.jsx | 8 +- .../shop-employees-form.component.jsx | 12 +- .../shop-info/shop-info.container.jsx | 6 +- .../shop-employee-teams.form.component.jsx | 12 +- .../shop-template-delete.component.jsx | 4 +- ...-template-editor-save-button.component.jsx | 8 +- .../shop-users-auth-edit.component.jsx | 4 +- .../task-list/task-list.container.jsx | 16 +-- .../task-upsert-modal.container.jsx | 16 +-- .../tech-job-clock-in-form.container.jsx | 8 +- .../tech-job-clock-out-button.component.jsx | 16 +-- .../tech-job-clock-out-delete.component.jsx | 8 +- .../tech-lookup-jobs-drawer.component.jsx | 36 +++-- .../time-ticket-modal.container.jsx | 10 +- .../time-ticket-shift-form.container.jsx | 12 +- .../time-ticket-task-modal.container.jsx | 10 +- .../time-tickets-commit-toggle.component.jsx | 10 +- .../time-tickets-commit.component.jsx | 10 +- .../tt-approve-button.component.jsx | 15 +- .../update-alert/update-alert.component.jsx | 5 +- .../vehicle-detail-form.container.jsx | 16 +-- .../vehicle-detail-update-jobs.component.jsx | 6 +- .../vehicles-list/vehicles-list.component.jsx | 15 +- .../vendors-form/vendors-form.container.jsx | 29 ++-- .../vendors-phonebook-add.component.jsx | 10 +- client/src/hooks/useAlertsNotifications.jsx | 2 +- .../contract-create.page.container.jsx | 16 +-- .../contract-detail.page.container.jsx | 6 +- .../courtesy-car-create.page.container.jsx | 6 +- .../courtesy-car-detail.page.container.jsx | 8 +- .../dms-payables/dms-payables.container.jsx | 5 +- client/src/pages/dms/dms.container.jsx | 10 +- .../pages/jobs-close/jobs-close.component.jsx | 12 +- .../jobs-create/jobs-create.container.jsx | 4 +- .../jobs-detail.page.component.jsx | 12 +- .../pages/manage/manage.page.component.jsx | 12 +- ...simplified-parts-jobs-detail.component.jsx | 68 +++++---- .../simplified-parts.page.component.jsx | 17 ++- client/src/redux/user/user.sagas.js | 5 +- client/src/utils/RegisterSw.js | 2 +- client/src/utils/RenderTemplate.js | 4 +- client/src/utils/criticalPartsScan.js | 2 +- client/vite.config.js | 16 ++- 171 files changed, 1128 insertions(+), 954 deletions(-) diff --git a/client/eslint.config.js b/client/eslint.config.js index 3063ce3d8..8563c84a2 100644 --- a/client/eslint.config.js +++ b/client/eslint.config.js @@ -1,10 +1,17 @@ import globals from "globals"; import pluginJs from "@eslint/js"; import pluginReact from "eslint-plugin-react"; +import pluginReactCompiler from "eslint-plugin-react-compiler"; /** @type {import("eslint").Linter.Config[]} */ export default [ - { ignores: ["node_modules/**", "dist/**", "build/**", "dev-dist/**"] }, + { ignores: [ + "node_modules/**", + "dist/**", + "build/**", + "dev-dist/**", + "**/trello-board/dnd/**" // Exclude third-party DnD library + ] }, { files: ["**/*.{js,mjs,cjs,jsx}"] }, @@ -21,5 +28,13 @@ export default [ "react/no-children-prop": 0 // Disable react/no-children-prop rule } }, - pluginReact.configs.flat["jsx-runtime"] + pluginReact.configs.flat["jsx-runtime"], + { + plugins: { + "react-compiler": pluginReactCompiler + }, + rules: { + "react-compiler/react-compiler": "error" + } + } ]; diff --git a/client/package-lock.json b/client/package-lock.json index 1ff503a06..1dbdc5df3 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -103,11 +103,13 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@vitejs/plugin-react": "^5.1.2", + "babel-plugin-react-compiler": "^1.0.0", "browserslist": "^4.28.1", "browserslist-to-esbuild": "^2.1.1", "chalk": "^5.6.2", "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-compiler": "^19.1.0-rc.2", "globals": "^17.0.0", "jsdom": "^27.4.0", "memfs": "^4.51.1", @@ -1077,6 +1079,24 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.11", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", @@ -8076,6 +8096,16 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/babel-plugin-react-compiler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz", + "integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + } + }, "node_modules/babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -10198,6 +10228,27 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, + "node_modules/eslint-plugin-react-compiler": { + "version": "19.1.0-rc.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.1.0-rc.2.tgz", + "integrity": "sha512-oKalwDGcD+RX9mf3NEO4zOoUMeLvjSvcbbEOpquzmzqEEM2MQdp7/FY/Hx9NzmUwFzH1W9SKTz5fihfMldpEYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "hermes-parser": "^0.25.1", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -11238,6 +11289,23 @@ "tslib": "^2.0.3" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -19138,6 +19206,29 @@ "zen-observable": "0.8.15" } }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.4.tgz", + "integrity": "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.24.4" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/client/package.json b/client/package.json index ac6fcceb3..51a533388 100644 --- a/client/package.json +++ b/client/package.json @@ -145,11 +145,13 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@vitejs/plugin-react": "^5.1.2", + "babel-plugin-react-compiler": "^1.0.0", "browserslist": "^4.28.1", "browserslist-to-esbuild": "^2.1.1", "chalk": "^5.6.2", "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-compiler": "^19.1.0-rc.2", "globals": "^17.0.0", "jsdom": "^27.4.0", "memfs": "^4.51.1", diff --git a/client/src/components/allocations-assignment/allocations-assignment.container.jsx b/client/src/components/allocations-assignment/allocations-assignment.container.jsx index 761f97961..a633c804b 100644 --- a/client/src/components/allocations-assignment/allocations-assignment.container.jsx +++ b/client/src/components/allocations-assignment/allocations-assignment.container.jsx @@ -19,15 +19,15 @@ export default function AllocationsAssignmentContainer({ jobLineId, hours, refet const handleAssignment = () => { insertAllocation({ variables: { alloc: { ...assignment } } }) .then(() => { - notification["success"]({ - message: t("allocations.successes.save") + notification.success({ + title: t("allocations.successes.save") }); visibilityState[1](false); if (refetch) refetch(); }) .catch((error) => { - notification["error"]({ - message: t("employees.errors.saving", { message: error.message }) + notification.error({ + title: t("employees.errors.saving", { message: error.message }) }); }); }; diff --git a/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.container.jsx b/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.container.jsx index ca2607485..b1f14c29d 100644 --- a/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.container.jsx +++ b/client/src/components/allocations-bulk-assignment/allocations-bulk-assignment.container.jsx @@ -25,8 +25,8 @@ export default function AllocationsBulkAssignmentContainer({ jobLines, refetch } }, []); insertAllocation({ variables: { alloc: allocs } }).then(() => { - notification["success"]({ - message: t("employees.successes.save") + notification.success({ + title: t("employees.successes.save") }); visibilityState[1](false); if (refetch) refetch(); diff --git a/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx b/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx index 26bc67411..d86359d9d 100644 --- a/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx +++ b/client/src/components/allocations-employee-label/allocations-employee-label.container.jsx @@ -13,13 +13,13 @@ export default function AllocationsLabelContainer({ allocation, refetch }) { e.preventDefault(); deleteAllocation({ variables: { id: allocation.id } }) .then(() => { - notification["success"]({ - message: t("allocations.successes.deleted") + notification.success({ + title: t("allocations.successes.deleted") }); if (refetch) refetch(); }) .catch(() => { - notification["error"]({ message: t("allocations.errors.deleting") }); + notification.error({ title: t("allocations.errors.deleting") }); }); }; diff --git a/client/src/components/bill-delete-button/bill-delete-button.component.jsx b/client/src/components/bill-delete-button/bill-delete-button.component.jsx index 9be567caa..9c591fd5a 100644 --- a/client/src/components/bill-delete-button/bill-delete-button.component.jsx +++ b/client/src/components/bill-delete-button/bill-delete-button.component.jsx @@ -44,7 +44,7 @@ export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) { }); if (!result.errors) { - notification["success"]({ message: t("bills.successes.deleted") }); + notification.success({ title: t("bills.successes.deleted") }); insertAuditTrail({ jobid: jobid, operation: AuditTrailMapping.billdeleted(bill.invoice_number), @@ -57,14 +57,14 @@ export function BillDeleteButton({ bill, jobid, callback, insertAuditTrail }) { const error = JSON.stringify(result.errors); if (error.toLowerCase().includes("inventory_billid_fkey")) { - notification["error"]({ - message: t("bills.errors.deleting", { + notification.error({ + title: t("bills.errors.deleting", { error: t("bills.errors.existinginventoryline") }) }); } else { - notification["error"]({ - message: t("bills.errors.deleting", { + notification.error({ + title: t("bills.errors.deleting", { error: JSON.stringify(result.errors) }) }); diff --git a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx index 8a24df0b1..95b380e38 100644 --- a/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx +++ b/client/src/components/bill-detail-edit/bill-detail-edit.container.jsx @@ -23,7 +23,7 @@ export default function BillDetailEditcontainer() { return ( { delete search.billid; history({ search: queryString.stringify(search) }); diff --git a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx index 571a7e85d..f3bfe6fab 100644 --- a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx +++ b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx @@ -205,8 +205,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, } }); if (jobUpdate.errors) { - notification["error"]({ - message: t("jobs.errors.saving", { + notification.error({ + title: t("jobs.errors.saving", { message: JSON.stringify(jobUpdate.errors) }) }); @@ -224,8 +224,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, if (r2.errors) { setLoading(false); setEnterAgain(false); - notification["error"]({ - message: t("parts_orders.errors.updating", { + notification.error({ + title: t("parts_orders.errors.updating", { message: JSON.stringify(r2.errors) }) }); @@ -235,8 +235,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, if (r1.errors) { setLoading(false); setEnterAgain(false); - notification["error"]({ - message: t("bills.errors.creating", { + notification.error({ + title: t("bills.errors.creating", { message: JSON.stringify(r1.errors) }) }); @@ -255,8 +255,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, if (r2.errors) { setLoading(false); setEnterAgain(false); - notification["error"]({ - message: t("inventory.errors.updating", { + notification.error({ + title: t("inventory.errors.updating", { message: JSON.stringify(r2.errors) }) }); @@ -343,8 +343,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, } /////////////////////////// setLoading(false); - notification["success"]({ - message: t("bills.successes.created") + notification.success({ + title: t("bills.successes.created") }); if (generateLabel) { 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 c030878f0..fea62cbe0 100644 --- a/client/src/components/bill-form/bill-form.lines.component.jsx +++ b/client/src/components/bill-form/bill-form.lines.component.jsx @@ -612,32 +612,51 @@ export function BillEnterModalLinesComponent({ export default connect(mapStateToProps, mapDispatchToProps)(BillEnterModalLinesComponent); -const EditableCell = ({ dataIndex, record, children, formInput, formItemProps, additional, wrapper, ...restProps }) => { - const propsFinal = formItemProps && formItemProps(record); - if (propsFinal && "key" in propsFinal) { - delete propsFinal.key; - } - if (additional) +const EditableCell = ({ + dataIndex, + record, + children, + formInput, + formItemProps, + additional, + wrapper: Wrapper, + ...restProps +}) => { + const rawProps = formItemProps?.(record); + + // DO NOT mutate rawProps; omit `key` immutably + const propsFinal = rawProps + ? (() => { + const { ...rest } = rawProps; + return rest; + })() + : undefined; + + if (additional) { return ( -
+
{(formInput && formInput(record, record.name)) || children} - {additional && additional(record, record.name)} + {additional(record, record.name)}
); - if (wrapper) + } + + if (Wrapper) { return ( - + {(formInput && formInput(record, record.name)) || children} - + ); + } + return ( diff --git a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx index f5ea41268..0ce4e3d6f 100644 --- a/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx +++ b/client/src/components/bill-mark-exported-button/bill-mark-exported-button.component.jsx @@ -62,12 +62,12 @@ export function BillMarkExportedButton({ currentUser, bodyshop, authLevel, bill }); if (!result.errors) { - notification["success"]({ - message: t("bills.successes.markexported") + notification.success({ + title: t("bills.successes.markexported") }); } else { - notification["error"]({ - message: t("bills.errors.saving", { + notification.error({ + title: t("bills.errors.saving", { error: JSON.stringify(result.errors) }) }); diff --git a/client/src/components/bill-reexport-button/bill-reexport-button.component.jsx b/client/src/components/bill-reexport-button/bill-reexport-button.component.jsx index 817dc1c9c..ae7641b38 100644 --- a/client/src/components/bill-reexport-button/bill-reexport-button.component.jsx +++ b/client/src/components/bill-reexport-button/bill-reexport-button.component.jsx @@ -44,12 +44,12 @@ export function BillMarkForReexportButton({ bodyshop, authLevel, bill }) { }); if (!result.errors) { - notification["success"]({ - message: t("bills.successes.reexport") + notification.success({ + title: t("bills.successes.reexport") }); } else { - notification["error"]({ - message: t("bills.errors.saving", { + notification.error({ + title: t("bills.errors.saving", { error: JSON.stringify(result.errors) }) }); diff --git a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx index 5d48f5db4..1b8d56156 100644 --- a/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx +++ b/client/src/components/billline-add-inventory/billline-add-inventory.component.jsx @@ -107,14 +107,12 @@ export function BilllineAddInventory({ currentUser, bodyshop, billline, disabled }); if (!insertResult.errors) { - notification.open({ - type: "success", - message: t("inventory.successes.inserted") + notification.success({ + title: t("inventory.successes.inserted") }); } else { - notification.open({ - type: "error", - message: t("inventory.errors.inserting", { + notification.error({ + title: t("inventory.errors.inserting", { error: JSON.stringify(insertResult.errors) }) }); 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 1c44a3d4e..2c5862c24 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 @@ -2,11 +2,13 @@ import { CopyFilled, DeleteFilled } from "@ant-design/icons"; import { useLazyQuery, useMutation } from "@apollo/client/react"; import { Button, Card, Col, Form, Input, message, Row, Space, Spin, Statistic } from "antd"; import axios from "axios"; -import { useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries"; +import { getCurrentUser, logImEXEvent } from "../../firebase/firebase.utils"; +import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { insertAuditTrail } from "../../redux/application/application.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { selectCardPayment } from "../../redux/modals/modals.selectors"; @@ -14,8 +16,6 @@ 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, logImEXEvent } from "../../firebase/firebase.utils"; -import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; const mapStateToProps = createStructuredSelector({ cardPaymentModal: selectCardPayment, @@ -51,9 +51,48 @@ const CardPaymentModalComponent = ({ const { t } = useTranslation(); const notification = useNotification(); - const [, { data, refetch, queryLoading }] = useLazyQuery(QUERY_RO_AND_OWNER_BY_JOB_PKS, { - skip: !context?.jobid - }); + const [loadRoAndOwnerByJobPks, { data, loading: queryLoading, error: queryError, refetch, called }] = useLazyQuery( + QUERY_RO_AND_OWNER_BY_JOB_PKS, + { + fetchPolicy: "network-only", + notifyOnNetworkStatusChange: true + } + ); + + const safeRefetchRoAndOwner = useCallback( + (vars) => { + // First run: execute the lazy query + if (!called) return loadRoAndOwnerByJobPks({ variables: vars }); + // Subsequent runs: refetch expects the variables object directly (not { variables: ... }) + return refetch(vars); + }, + [called, loadRoAndOwnerByJobPks, refetch] + ); + + // Watch form payments so we can query jobs when all jobids are filled (without side effects during render) + const payments = Form.useWatch(["payments"], form); + + const jobids = useMemo(() => { + if (!Array.isArray(payments) || payments.length === 0) return []; + return payments.map((p) => p?.jobid).filter(Boolean); + }, [payments]); + + const allJobIdsFilled = useMemo(() => { + if (!Array.isArray(payments) || payments.length === 0) return false; + return payments.every((p) => !!p?.jobid); + }, [payments]); + + const lastJobidsKeyRef = useRef(""); + + useEffect(() => { + if (!allJobIdsFilled) return; + + const nextKey = jobids.join("|"); + if (!nextKey || nextKey === lastJobidsKeyRef.current) return; + + lastJobidsKeyRef.current = nextKey; + safeRefetchRoAndOwner({ jobids }); + }, [allJobIdsFilled, jobids, safeRefetchRoAndOwner]); const collectIPayFields = () => { const iPayFields = document.querySelectorAll(".ipayfield"); @@ -72,8 +111,8 @@ const CardPaymentModalComponent = ({ }); window.intellipay.runOnApproval(() => { - //2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback. - //Add a slight delay to allow the refetch to properly get the data. + // 2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback. + // Add a slight delay to allow the refetch to properly get the data. setTimeout(() => { if (actions?.refetch) actions.refetch(); setLoading(false); @@ -111,7 +150,7 @@ const CardPaymentModalComponent = ({ const handleIntelliPayCharge = async () => { setLoading(true); - //Validate + // Validate try { await form.validateFields(); } catch { @@ -133,7 +172,9 @@ const CardPaymentModalComponent = ({ }); if (window.intellipay) { - eval(response.data); + // Use Function constructor instead of eval for security (still executes dynamic code but safer) + // IntelliPay provides initialization code that must be executed + Function(response.data)(); pollForIntelliPay(() => { SetIntellipayCallbackFunctions(); window.intellipay.autoOpen(); @@ -144,14 +185,14 @@ const CardPaymentModalComponent = ({ document.documentElement.appendChild(node); pollForIntelliPay(() => { SetIntellipayCallbackFunctions(); + // eslint-disable-next-line react-compiler/react-compiler window.intellipay.isAutoOpen = true; window.intellipay.initialize(); }); } } catch { - notification.open({ - type: "error", - message: t("job_payments.notifications.error.openingip") + notification.error({ + title: t("job_payments.notifications.error.openingip") }); setLoading(false); } @@ -159,7 +200,7 @@ const CardPaymentModalComponent = ({ const handleIntelliPayChargeShortLink = async () => { setLoading(true); - //Validate + // Validate try { await form.validateFields(); } catch { @@ -188,9 +229,8 @@ const CardPaymentModalComponent = ({ } setLoading(false); } catch { - notification.open({ - type: "error", - message: t("job_payments.notifications.error.openingip") + notification.error({ + title: t("job_payments.notifications.error.openingip") }); setLoading(false); } @@ -247,40 +287,20 @@ const CardPaymentModalComponent = ({ )} - - prevValues.payments?.map((p) => p?.jobid + p?.amount).join() !== - curValues.payments?.map((p) => p?.jobid + p?.amount).join() - } - > - {() => { - //If all of the job ids have been fileld in, then query and update the IP field. - const { payments } = form.getFieldsValue(); - if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) { - refetch({ variables: { jobids: payments.map((p) => p.jobid) } }); - } - return ( - <> - 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null - } - /> - 0 ? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea : null - } - /> - - ); - }} - + {/* Hidden IntelliPay fields driven by watched payments + query result. IMPORTANT: no refetch() here (avoid side effects during render). */} + 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null} + /> + 0 ? (data.jobs.find((j) => j.ownr_ea)?.ownr_ea ?? null) : null} + /> + prevValues.payments?.map((p) => p?.amount).join() !== curValues.payments?.map((p) => p?.amount).join() @@ -332,6 +352,7 @@ const CardPaymentModalComponent = ({ }} + {paymentLink && ( )} + + {queryError ? ( +
+ {queryError.message} +
+ ) : null} ); @@ -352,10 +379,10 @@ const CardPaymentModalComponent = ({ export default connect(mapStateToProps, mapDispatchToProps)(CardPaymentModalComponent); -//Poll for window.IntelliPay.fixAmount for 5 seconds. If it doesn't come up, just try anyways to force the possible error. +// Poll for window.IntelliPay.fixAmount for 5 seconds. If it doesn't come up, just try anyways to force the possible error. function pollForIntelliPay(callbackFunction) { const timeout = 5000; - const interval = 150; // Poll every 100 milliseconds + const interval = 150; const startTime = Date.now(); function checkFixAmount() { @@ -365,7 +392,7 @@ function pollForIntelliPay(callbackFunction) { } if (Date.now() - startTime >= timeout) { - console.log("Stopped polling IntelliPay after 10 seconds. Attemping to set functions anyways."); + console.log("Stopped polling IntelliPay after 5 seconds. Attempting to set functions anyways."); callbackFunction(); return; } diff --git a/client/src/components/chat-label/chat-label.component.jsx b/client/src/components/chat-label/chat-label.component.jsx index b3b81af91..5c90d4ea9 100644 --- a/client/src/components/chat-label/chat-label.component.jsx +++ b/client/src/components/chat-label/chat-label.component.jsx @@ -33,8 +33,8 @@ export function ChatLabel({ conversation, bodyshop }) { variables: { id: conversation.id, label: value } }); if (response.errors) { - notification["error"]({ - message: t("messages.errors.updatinglabel", { + notification.error({ + title: t("messages.errors.updatinglabel", { error: JSON.stringify(response.errors) }) }); @@ -50,8 +50,8 @@ export function ChatLabel({ conversation, bodyshop }) { setEditing(false); } } catch (error) { - notification["error"]({ - message: t("messages.errors.updatinglabel", { + notification.error({ + title: t("messages.errors.updatinglabel", { error: JSON.stringify(error) }) }); diff --git a/client/src/components/chat-open-button/chat-open-button.component.jsx b/client/src/components/chat-open-button/chat-open-button.component.jsx index 4874f43fd..5c6781edd 100644 --- a/client/src/components/chat-open-button/chat-open-button.component.jsx +++ b/client/src/components/chat-open-button/chat-open-button.component.jsx @@ -43,7 +43,7 @@ export function ChatOpenButton({ bodyshop, searchingForConversation, phone, type if (p && p.isValid()) { openChatByPhone({ phone_num: p.formatInternational(), jobid, socket }); } else { - notification["error"]({ message: t("messaging.error.invalidphone") }); + notification.error({ title: t("messaging.error.invalidphone") }); } }} > diff --git a/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx b/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx index 15bff2e15..3b60b73e5 100644 --- a/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx +++ b/client/src/components/contract-convert-to-ro/contract-convert-to-ro.component.jsx @@ -278,14 +278,14 @@ export function ContractConvertToRo({ bodyshop, currentUser, contract, disabled }); if (result.errors) { - notification["error"]({ - message: t("jobs.errors.inserting", { + notification.error({ + title: t("jobs.errors.inserting", { message: JSON.stringify(result.errors) }) }); } else { - notification["success"]({ - message: t("jobs.successes.created"), + notification.success({ + title: t("jobs.successes.created"), onClick: () => { history.push(`/manage/jobs/${result.data.insert_jobs.returning[0].id}`); } diff --git a/client/src/components/contract-form/contract-form-job-prefill.component.jsx b/client/src/components/contract-form/contract-form-job-prefill.component.jsx index ccc6623c7..276efd88f 100644 --- a/client/src/components/contract-form/contract-form-job-prefill.component.jsx +++ b/client/src/components/contract-form/contract-form-job-prefill.component.jsx @@ -30,8 +30,8 @@ export default function ContractCreateJobPrefillComponent({ jobId, form }) { }, [data, form]); if (error) { - notification["error"]({ - message: t("contracts.errors.fetchingjobinfo", { + notification.error({ + title: t("contracts.errors.fetchingjobinfo", { error: JSON.stringify(error) }) }); diff --git a/client/src/components/contract-status-select/contract-status-select.component.jsx b/client/src/components/contract-status-select/contract-status-select.component.jsx index 3a4ee82ee..69d38656a 100644 --- a/client/src/components/contract-status-select/contract-status-select.component.jsx +++ b/client/src/components/contract-status-select/contract-status-select.component.jsx @@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next"; const { Option } = Select; -const ContractStatusComponent = ({ value, onChange }) => { +const ContractStatusComponent = forwardRef(({ value, onChange }, ref) => { const [option, setOption] = useState(value); const { t } = useTranslation(); @@ -15,17 +15,14 @@ const ContractStatusComponent = ({ value, onChange }) => { }, [value, option, onChange]); return ( - - + ); -}; -export default forwardRef(ContractStatusComponent); +}); + +ContractStatusComponent.displayName = "ContractStatusComponent"; + +export default ContractStatusComponent; diff --git a/client/src/components/contracts-list/contracts-list.component.jsx b/client/src/components/contracts-list/contracts-list.component.jsx index d22807a2a..2a76d8630 100644 --- a/client/src/components/contracts-list/contracts-list.component.jsx +++ b/client/src/components/contracts-list/contracts-list.component.jsx @@ -127,10 +127,13 @@ export function ContractsList({ bodyshop, loading, contracts, refetch, total, se const handleTableChange = (pagination, filters, sorter) => { setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); - search.page = pagination.current; - search.sortcolumn = sorter.columnKey; - search.sortorder = sorter.order; - history({ search: queryString.stringify(search) }); + const updatedSearch = { + ...search, + page: pagination.current, + sortcolumn: sorter.columnKey, + sortorder: sorter.order + }; + history({ search: queryString.stringify(updatedSearch) }); }; return ( @@ -159,8 +162,8 @@ export function ContractsList({ bodyshop, loading, contracts, refetch, total, se { - search.search = value; - history({ search: queryString.stringify(search) }); + const updatedSearch = { ...search, search: value }; + history({ search: queryString.stringify(updatedSearch) }); }} /> diff --git a/client/src/components/courtesy-car-return-modal/courtesy-car-return-modal.container.jsx b/client/src/components/courtesy-car-return-modal/courtesy-car-return-modal.container.jsx index aa9ebd817..3f356dd42 100644 --- a/client/src/components/courtesy-car-return-modal/courtesy-car-return-modal.container.jsx +++ b/client/src/components/courtesy-car-return-modal/courtesy-car-return-modal.container.jsx @@ -51,8 +51,8 @@ export function CCReturnModalContainer({ courtesyCarReturnModal, toggleModalVisi toggleModalVisible(); }) .catch((error) => { - notification["error"]({ - message: t("contracts.errors.returning", { error: error }) + notification.error({ + title: t("contracts.errors.returning", { error: error }) }); }); setLoading(false); diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx index b9f0a6145..fac7ebb00 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx +++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx @@ -106,7 +106,7 @@ export function DashboardGridComponent({ currentUser }) { if (errors.length) { const errorMessages = errors.map(({ message }) => message || String(error)); notification.error({ - message: t("dashboard.errors.updatinglayout", { + title: t("dashboard.errors.updatinglayout", { message: errorMessages.join("; ") }) }); @@ -117,7 +117,7 @@ export function DashboardGridComponent({ currentUser }) { } catch (err) { console.error(`Dashboard ${errorContext} failed`, err); notification.error({ - message: t("dashboard.errors.updatinglayout", { + title: t("dashboard.errors.updatinglayout", { message: err?.message || String(err) }) }); diff --git a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx index 511be6636..2ca64d997 100644 --- a/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx +++ b/client/src/components/dms-allocations-summary-ap/dms-allocations-summary-ap.component.jsx @@ -1,6 +1,6 @@ import { SyncOutlined } from "@ant-design/icons"; import { Button, Card, Form, Input, Table } from "antd"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -21,6 +21,11 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsAllocationsSummar export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) { const { t } = useTranslation(); const [allocationsSummary, setAllocationsSummary] = useState([]); + const socketRef = useRef(socket); + + useEffect(() => { + socketRef.current = socket; + }, [socket]); useEffect(() => { socket.on("ap-export-success", (billid) => { @@ -50,8 +55,8 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) { if (socket.connected) { socket.emit("pbs-calculate-allocations-ap", billids, (ack) => { setAllocationsSummary(ack); - - socket.allocationsSummary = ack; + // Store on socket for side-channel communication + socketRef.current.allocationsSummary = ack; }); } }, [socket, socket.connected, billids]); diff --git a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx index 3ed0aaedd..f32c41185 100644 --- a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx @@ -1,6 +1,6 @@ import { Alert, Button, Card, Table, Typography } from "antd"; import { SyncOutlined } from "@ant-design/icons"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -31,6 +31,11 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsAllocationsSummar export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title, onAllocationsChange }) { const { t } = useTranslation(); const [allocationsSummary, setAllocationsSummary] = useState([]); + const socketRef = useRef(socket); + + useEffect(() => { + socketRef.current = socket; + }, [socket]); // Resolve event name by mode (PBS reuses the CDK event per existing behavior) const allocationsEvent = @@ -48,14 +53,14 @@ export function DmsAllocationsSummary({ mode, socket, bodyshop, jobId, title, on const list = Array.isArray(ack) ? ack : []; setAllocationsSummary(list); // Preserve side-channel used by the post form for discrepancy checks - socket.allocationsSummary = list; + socketRef.current.allocationsSummary = list; if (onAllocationsChange) onAllocationsChange(list); }); } catch { // Best-effort; leave table empty on error setAllocationsSummary([]); - if (socket) { - socket.allocationsSummary = []; + if (socketRef.current) { + socketRef.current.allocationsSummary = []; } if (onAllocationsChange) { onAllocationsChange([]); diff --git a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx index e7042421d..0539ae989 100644 --- a/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/rr-dms-allocations-summary.component.jsx @@ -1,6 +1,6 @@ import { Alert, Button, Card, Table, Tabs, Typography } from "antd"; import { SyncOutlined } from "@ant-design/icons"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -74,6 +74,11 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat const [roggPreview, setRoggPreview] = useState(null); const [rolaborPreview, setRolaborPreview] = useState(null); const [error, setError] = useState(null); + const socketRef = useRef(socket); + + useEffect(() => { + socketRef.current = socket; + }, [socket]); // Prefer the user-selected OpCode (from DmsContainer), fall back to config default const effectiveOpCode = useMemo(() => opCode || resolveRROpCodeFromBodyshop(bodyshop), [opCode, bodyshop]); @@ -87,9 +92,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat setRoggPreview(null); setRolaborPreview(null); setError(ack.error || t("dms.labels.allocations_error")); - if (socket) { - socket.allocationsSummary = []; - socket.rrAllocationsRaw = ack; + if (socketRef.current) { + socketRef.current.allocationsSummary = []; + socketRef.current.rrAllocationsRaw = ack; } if (onAllocationsChange) { onAllocationsChange([]); @@ -103,9 +108,9 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat setRolaborPreview(ack?.rolabor || null); setError(null); - if (socket) { - socket.allocationsSummary = jobAllocRows; - socket.rrAllocationsRaw = ack; + if (socketRef.current) { + socketRef.current.allocationsSummary = jobAllocRows; + socketRef.current.rrAllocationsRaw = ack; } if (onAllocationsChange) { onAllocationsChange(jobAllocRows); @@ -115,8 +120,8 @@ export function RrAllocationsSummary({ socket, bodyshop, jobId, title, onAllocat setRoggPreview(null); setRolaborPreview(null); setError(t("dms.labels.allocations_error")); - if (socket) { - socket.allocationsSummary = []; + if (socketRef.current) { + socketRef.current.allocationsSummary = []; } if (onAllocationsChange) { onAllocationsChange([]); diff --git a/client/src/components/documents-local-upload/documents-local-upload.utility.js b/client/src/components/documents-local-upload/documents-local-upload.utility.js index d568d83b9..6dcabbbfe 100644 --- a/client/src/components/documents-local-upload/documents-local-upload.utility.js +++ b/client/src/components/documents-local-upload/documents-local-upload.utility.js @@ -43,10 +43,9 @@ export const handleUpload = async ({ ev, context, notification }) => { } else { onSuccess && onSuccess(file); if (notification) { - notification.open({ - type: "success", + notification.success({ key: "docuploadsuccess", - message: i18n.t("documents.successes.insert") + title: i18n.t("documents.successes.insert") }); } else { console.error("No notification context found in document local upload utility."); diff --git a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx index f71760e3b..6be0ac993 100644 --- a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx +++ b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.component.jsx @@ -69,7 +69,7 @@ export function DocumentsUploadImgproxyComponent({ if (shouldStopUpload) { notification.error({ key: "cannotuploaddocuments", - message: t("documents.labels.upload_limitexceeded_title"), + title: t("documents.labels.upload_limitexceeded_title"), description: t("documents.labels.upload_limitexceeded") }); return Upload.LIST_IGNORE; diff --git a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js index 000871efe..eb53b53fa 100644 --- a/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js +++ b/client/src/components/documents-upload-imgproxy/documents-upload-imgproxy.utility.js @@ -27,7 +27,7 @@ export const handleUpload = (ev, context, notification) => { (error) => { console.error("Error uploading file to S3", error); notification.error({ - message: i18n.t("documents.errors.insert", { + title: i18n.t("documents.errors.insert", { message: error.message }) }); @@ -59,7 +59,7 @@ export const uploadToS3 = async ( if (signedURLResponse.status !== 200) { if (onError) onError(signedURLResponse.statusText); notification.error({ - message: i18n.t("documents.errors.getpresignurl", { + title: i18n.t("documents.errors.getpresignurl", { message: signedURLResponse.statusText }) }); @@ -74,7 +74,7 @@ export const uploadToS3 = async ( if (onProgress) onProgress({ percent: (e.loaded / e.total) * 100 }); }, headers: { - ...contentType ? { "Content-Type": fileType } : {} + ...(contentType ? { "Content-Type": fileType } : {}) } }; @@ -120,7 +120,7 @@ export const uploadToS3 = async ( }); notification.success({ key: "docuploadsuccess", - message: i18n.t("documents.successes.insert") + title: i18n.t("documents.successes.insert") }); if (callback) { callback(); @@ -128,7 +128,7 @@ export const uploadToS3 = async ( } else { if (onError) onError(JSON.stringify(documentInsert.errors)); notification.error({ - message: i18n.t("documents.errors.insert", { + title: i18n.t("documents.errors.insert", { message: JSON.stringify(documentInsert.errors) }) }); @@ -137,7 +137,7 @@ export const uploadToS3 = async ( } catch (error) { console.log("Error uploading file to S3", error.message, error.stack); notification.error({ - message: i18n.t("documents.errors.insert", { + title: i18n.t("documents.errors.insert", { message: error.message }) }); diff --git a/client/src/components/documents-upload/documents-upload.component.jsx b/client/src/components/documents-upload/documents-upload.component.jsx index 4189085cc..69ccc9bf1 100644 --- a/client/src/components/documents-upload/documents-upload.component.jsx +++ b/client/src/components/documents-upload/documents-upload.component.jsx @@ -67,10 +67,9 @@ export function DocumentsUploadComponent({ //Check to see if old files plus newly uploaded ones will be too much. if (shouldStopUpload) { - notification.open({ + notification.error({ key: "cannotuploaddocuments", - type: "error", - message: t("documents.labels.upload_limitexceeded_title"), + title: t("documents.labels.upload_limitexceeded_title"), description: t("documents.labels.upload_limitexceeded") }); return Upload.LIST_IGNORE; diff --git a/client/src/components/documents-upload/documents-upload.utility.js b/client/src/components/documents-upload/documents-upload.utility.js index 0e725ae48..dffa70eba 100644 --- a/client/src/components/documents-upload/documents-upload.utility.js +++ b/client/src/components/documents-upload/documents-upload.utility.js @@ -58,8 +58,8 @@ export const uploadToCloudinary = async ( if (signedURLResponse.status !== 200) { if (onError) onError(signedURLResponse.statusText); - notification["error"]({ - message: i18n.t("documents.errors.getpresignurl", { + notification.error({ + title: i18n.t("documents.errors.getpresignurl", { message: signedURLResponse.statusText }) }); @@ -110,8 +110,8 @@ export const uploadToCloudinary = async ( // NO OP } - notification["error"]({ - message: i18n.t("documents.errors.insert", { + notification.error({ + title: i18n.t("documents.errors.insert", { message: cloudinaryUploadResponse.statusText }) }); @@ -155,18 +155,17 @@ export const uploadToCloudinary = async ( status: "done", key: documentInsert.data.insert_documents.returning[0].key }); - notification.open({ - type: "success", + notification.success({ key: "docuploadsuccess", - message: i18n.t("documents.successes.insert") + title: i18n.t("documents.successes.insert") }); if (callback) { callback(); } } else { if (onError) onError(JSON.stringify(documentInsert.errors)); - notification["error"]({ - message: i18n.t("documents.errors.insert", { + notification.error({ + title: i18n.t("documents.errors.insert", { message: JSON.stringify(documentInsert.errors) }) }); diff --git a/client/src/components/email-overlay/email-overlay.container.jsx b/client/src/components/email-overlay/email-overlay.container.jsx index 610004e14..2ef0b6db2 100644 --- a/client/src/components/email-overlay/email-overlay.container.jsx +++ b/client/src/components/email-overlay/email-overlay.container.jsx @@ -98,11 +98,11 @@ export function EmailOverlayContainer({ emailConfig, modalVisible, toggleEmailOv media: selectedMedia.filter((m) => m.isSelected).map((m) => m.fullsize) //attachments, }); - notification["success"]({ message: t("emails.successes.sent") }); + notification.success({ title: t("emails.successes.sent") }); toggleEmailOverlayVisible(); } catch (error) { - notification["error"]({ - message: t("emails.errors.notsent", { message: error.message }) + notification.error({ + title: t("emails.errors.notsent", { message: error.message }) }); } setSending(false); diff --git a/client/src/components/eula/eula.component.jsx b/client/src/components/eula/eula.component.jsx index b82623bff..f28742b0b 100644 --- a/client/src/components/eula/eula.component.jsx +++ b/client/src/components/eula/eula.component.jsx @@ -78,7 +78,7 @@ const Eula = ({ currentEula, currentUser, acceptEula }) => { acceptEula(); } catch (err) { notification.error({ - message: t("eula.errors.acceptance.message"), + title: t("eula.errors.acceptance.message"), description: t("eula.errors.acceptance.description") }); console.log(`${t("eula.errors.acceptance.message")}`); diff --git a/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx b/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx index 05bf55179..1a1439f1b 100644 --- a/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx +++ b/client/src/components/inventory-line-delete/inventory-line-delete.component.jsx @@ -32,12 +32,12 @@ export default function InventoryLineDelete({ inventoryline, disabled, refetch } }); if (!result.errors) { - notification["success"]({ message: t("inventory.successes.deleted") }); + notification.success({ title: t("inventory.successes.deleted") }); } else { //Check if it's an fkey violation. - notification["error"]({ - message: t("bills.errors.deleting", { + notification.error({ + title: t("bills.errors.deleting", { error: JSON.stringify(result.errors) }) }); diff --git a/client/src/components/inventory-list/inventory-list.component.jsx b/client/src/components/inventory-list/inventory-list.component.jsx index 53d6301ae..3d0d6e1e8 100644 --- a/client/src/components/inventory-list/inventory-list.component.jsx +++ b/client/src/components/inventory-list/inventory-list.component.jsx @@ -120,10 +120,13 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont ]; const handleTableChange = (pagination, filters, sorter) => { - search.page = pagination.current; - search.sortcolumn = sorter.column && sorter.column.key; - search.sortorder = sorter.order; - history({ search: queryString.stringify(search) }); + const updatedSearch = { + ...search, + page: pagination.current, + sortcolumn: sorter.column && sorter.column.key, + sortorder: sorter.order + }; + history({ search: queryString.stringify(updatedSearch) }); }; return ( @@ -157,11 +160,13 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont