feature/IO-3499-React-19: Bug Fixes / Checkpoint

This commit is contained in:
Dave
2026-01-13 22:28:43 -05:00
parent 7bdfbfabe9
commit 53d556a621
171 changed files with 1128 additions and 954 deletions

View File

@@ -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"
}
}
];

View File

@@ -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",

View File

@@ -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",

View File

@@ -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 })
});
});
};

View File

@@ -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();

View File

@@ -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") });
});
};

View File

@@ -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)
})
});

View File

@@ -23,7 +23,7 @@ export default function BillDetailEditcontainer() {
return (
<Drawer
width={drawerPercentage}
size={drawerPercentage}
onClose={() => {
delete search.billid;
history({ search: queryString.stringify(search) });

View File

@@ -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) {

View File

@@ -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 (
<td {...restProps}>
<div size="small">
<div>
<Form.Item name={dataIndex} labelCol={{ span: 0 }} {...propsFinal}>
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
{additional && additional(record, record.name)}
{additional(record, record.name)}
</div>
</td>
);
if (wrapper)
}
if (Wrapper) {
return (
<wrapper>
<Wrapper>
<td {...restProps}>
<Form.Item labelCol={{ span: 0 }} name={dataIndex} {...propsFinal}>
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
</td>
</wrapper>
</Wrapper>
);
}
return (
<td {...restProps}>
<Form.Item labelCol={{ span: 0 }} name={dataIndex} {...propsFinal}>

View File

@@ -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)
})
});

View File

@@ -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)
})
});

View File

@@ -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)
})
});

View File

@@ -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 = ({
)}
</Form.List>
<Form.Item
shouldUpdate={(prevValues, curValues) =>
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 (
<>
{/* Hidden IntelliPay fields driven by watched payments + query result. IMPORTANT: no refetch() here (avoid side effects during render). */}
<Input
className="ipayfield"
data-ipayname="account"
type="hidden"
value={
payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null
}
value={data?.jobs?.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null}
/>
<Input
className="ipayfield"
data-ipayname="email"
type="hidden"
value={
payments && data && data.jobs.length > 0 ? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea : null
}
value={data?.jobs?.length > 0 ? (data.jobs.find((j) => j.ownr_ea)?.ownr_ea ?? null) : null}
/>
</>
);
}}
</Form.Item>
<Form.Item
shouldUpdate={(prevValues, curValues) =>
prevValues.payments?.map((p) => p?.amount).join() !== curValues.payments?.map((p) => p?.amount).join()
@@ -332,6 +352,7 @@ const CardPaymentModalComponent = ({
}}
</Form.Item>
</Form>
{paymentLink && (
<Space
style={{ cursor: "pointer", float: "right" }}
@@ -345,6 +366,12 @@ const CardPaymentModalComponent = ({
<CopyFilled />
</Space>
)}
{queryError ? (
<div style={{ marginTop: 12 }}>
<span style={{ color: "red" }}>{queryError.message}</span>
</div>
) : null}
</Spin>
</Card>
);
@@ -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;
}

View File

@@ -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)
})
});

View File

@@ -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") });
}
}}
>

View File

@@ -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}`);
}

View File

@@ -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)
})
});

View File

@@ -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 (
<Select
value={option}
style={{
width: 100
}}
onChange={setOption}
>
<Select ref={ref} value={option} style={{ width: 100 }} onChange={setOption}>
<Option value="contracts.status.new">{t("contracts.status.new")}</Option>
<Option value="contracts.status.out">{t("contracts.status.out")}</Option>
<Option value="contracts.status.returned">{t("contracts.status.returned")}</Option>
<Option value="contracts.status.returned">{t("contracts.status.out")}</Option>
</Select>
);
};
export default forwardRef(ContractStatusComponent);
});
ContractStatusComponent.displayName = "ContractStatusComponent";
export default ContractStatusComponent;

View File

@@ -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
<Input.Search
placeholder={search.searh || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history({ search: queryString.stringify(search) });
const updatedSearch = { ...search, search: value };
history({ search: queryString.stringify(updatedSearch) });
}}
/>
</Space>

View File

@@ -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);

View File

@@ -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)
})
});

View File

@@ -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]);

View File

@@ -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([]);

View File

@@ -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([]);

View File

@@ -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.");

View File

@@ -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;

View File

@@ -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
})
});

View File

@@ -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;

View File

@@ -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)
})
});

View File

@@ -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);

View File

@@ -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")}`);

View File

@@ -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)
})
});

View File

@@ -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
</Button>
<Button
onClick={() => {
if (search.showall) delete search.showall;
else {
search.showall = true;
const updatedSearch = { ...search };
if (updatedSearch.showall) {
delete updatedSearch.showall;
} else {
updatedSearch.showall = true;
}
history({ search: queryString.stringify(search) });
history({ search: queryString.stringify(updatedSearch) });
}}
>
{search.showall ? t("inventory.labels.showavailable") : t("inventory.labels.showall")}
@@ -173,8 +178,8 @@ export function JobsList({ refetch, loading, jobs, total, setInventoryUpsertCont
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
history({ search: queryString.stringify(search) });
const updatedSearch = { ...search, search: value };
history({ search: queryString.stringify(updatedSearch) });
}}
enterButton
/>

View File

@@ -53,8 +53,8 @@ export function InventoryUpsertModalContainer({ bodyshop, inventoryUpsertModal,
inventoryItem: values
}
}).then(() => {
notification["success"]({
message: t("inventory.successes.updated")
notification.success({
title: t("inventory.successes.updated")
});
});
// if (refetch) refetch();
@@ -80,8 +80,8 @@ export function InventoryUpsertModalContainer({ bodyshop, inventoryUpsertModal,
if (refetch) refetch();
form.resetFields();
toggleModalVisible();
notification["success"]({
message: t("inventory.successes.inserted")
notification.success({
title: t("inventory.successes.inserted")
});
}
};

View File

@@ -29,8 +29,8 @@ export function JobAltTransportChange({ bodyshop, job }) {
if (!result.errors) {
// notification["success"]({ message: t("appointments.successes.saved") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -27,10 +27,10 @@ export function ScheduleEventColor({ bodyshop, event }) {
});
if (!result.errors) {
notification["success"]({ message: t("appointments.successes.saved") });
notification.success({ title: t("appointments.successes.saved") });
} else {
notification["error"]({
message: t("appointments.errors.saving", {
notification.error({
title: t("appointments.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -131,8 +131,8 @@ export function ScheduleEventComponent({
}
});
if (!res.errors) {
notification["success"]({
message: t("jobs.successes.converted")
notification.success({
title: t("jobs.successes.converted")
});
insertAuditTrail({
jobid: event.job.id,
@@ -309,8 +309,8 @@ export function ScheduleEventComponent({
);
setOpen(false);
} else {
notification["error"]({
message: t("messaging.error.invalidphone")
notification.error({
title: t("messaging.error.invalidphone")
});
}
}

View File

@@ -22,13 +22,13 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
const cancelAppt = await cancelAppointment({
variables: { appid: event.id }
});
notification["success"]({
message: t("appointments.successes.canceled")
notification.success({
title: t("appointments.successes.canceled")
});
if (cancelAppt.errors) {
notification["error"]({
message: t("appointments.errors.canceling", {
notification.error({
title: t("appointments.errors.canceling", {
message: JSON.stringify(cancelAppt.errors)
})
});
@@ -59,8 +59,8 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
);
}
if (jobUpdate.errors) {
notification["error"]({
message: t("jobs.errors.updating", {
notification.error({
title: t("jobs.errors.updating", {
message: JSON.stringify(jobUpdate.errors)
})
});

View File

@@ -39,8 +39,8 @@ export function ScheduleEventNote({ event }) {
if (!result.errors) {
// notification["success"]({ message: t("appointments.successes.saved") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -35,15 +35,15 @@ export default function JobCalculateTotals({ job, disabled, refetch }) {
// if (!!!result.errors) {
// notification["success"]({ message: t("jobs.successes.updated") });
// } else {
// notification["error"]({
// notification.error({
// message: t("jobs.errors.updating", {
// error: JSON.stringify(result.errors),
// }),
// });
// }
} catch (error) {
notification["error"]({
message: t("jobs.errors.updating", {
notification.error({
title: t("jobs.errors.updating", {
error: JSON.stringify(error)
})
});

View File

@@ -107,8 +107,8 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
});
if (appUpdate.errors) {
notification["error"]({
message: t("checklist.errors.complete", {
notification.error({
title: t("checklist.errors.complete", {
error: JSON.stringify(result.errors)
})
});
@@ -119,8 +119,8 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
});
if (appUpdate.errors) {
notification["error"]({
message: t("checklist.errors.complete", {
notification.error({
title: t("checklist.errors.complete", {
error: JSON.stringify(result.errors)
})
});
@@ -130,7 +130,7 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
setLoading(false);
if (!result.errors) {
notification["success"]({ message: t("checklist.successes.completed") });
notification.success({ title: t("checklist.successes.completed") });
history(`/manage/jobs/${jobId}`);
insertAuditTrail({
@@ -145,8 +145,8 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
type: "jobchecklist"
});
} else {
notification["error"]({
message: t("checklist.errors.complete", {
notification.error({
title: t("checklist.errors.complete", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -55,9 +55,8 @@ export function JobCreateIOU({ bodyshop, currentUser, job, selectedJobLines, tec
currentUser
});
notification.open({
type: "success",
message: t("jobs.successes.ioucreated"),
notification.success({
title: t("jobs.successes.ioucreated"),
onClick: () => history(`/manage/jobs/${iouId}`)
});
const selectedJobLinesIds = selectedJobLines.map((l) => l.id);

View File

@@ -41,23 +41,20 @@ export function JobLinesPartPriceChange({ job, line, refetch, technician, isPart
id: job.id
});
if (result.errors) {
notification.open({
type: "error",
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
error: JSON.stringify(result.errors)
})
});
if (refetch) refetch();
} else {
notification.open({
type: "success",
message: t("joblines.successes.saved")
notification.success({
title: t("joblines.successes.saved")
});
}
} catch (error) {
notification.open({
type: "error",
message: t("joblines.errors.saving", { error: JSON.stringify(error) })
notification.error({
title: t("joblines.errors.saving", { error: JSON.stringify(error) })
});
} finally {
setLoading(false);

View File

@@ -174,7 +174,7 @@ export function JobLinesComponent({
prev.map((l) => (l && selectedLineIds.includes(l.id) ? { ...l, location: locationToSave } : l))
);
notification["success"]({ message: t("joblines.successes.saved") });
notification.success({ title: t("joblines.successes.saved") });
logImEXEvent("joblines_bulk_location_saved", {
count: selectedLineIds.length,
@@ -184,13 +184,13 @@ export function JobLinesComponent({
closeBulkLocationModal();
if (refetch) refetch();
} else {
notification["error"]({
message: t("joblines.errors.saving", { error: JSON.stringify(result.errors) })
notification.error({
title: t("joblines.errors.saving", { error: JSON.stringify(result.errors) })
});
}
} catch (error) {
notification["error"]({
message: t("joblines.errors.saving", { error: error?.message || String(error) })
notification.error({
title: t("joblines.errors.saving", { error: error?.message || String(error) })
});
} finally {
setBulkLocationSaving(false);

View File

@@ -44,8 +44,8 @@ export function JobEmployeeAssignmentsContainer({ job, refetch, insertAuditTrail
type: "jobassignmentchange"
});
} else {
notification["error"]({
message: t("jobs.errors.assigning", {
notification.error({
title: t("jobs.errors.assigning", {
message: JSON.stringify(result.errors)
})
});
@@ -68,8 +68,8 @@ export function JobEmployeeAssignmentsContainer({ job, refetch, insertAuditTrail
type: "jobassignmentremoved"
});
} else {
notification["error"]({
message: t("jobs.errors.assigning", {
notification.error({
title: t("jobs.errors.assigning", {
message: JSON.stringify(result.errors)
})
});

View File

@@ -43,9 +43,8 @@ export function JoblineBulkAssign({ setSelectedLines, selectedLines, insertAudit
}
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
notification.error({
title: t("parts_dispatch.errors.creating", {
error: JSON.stringify(result.errors)
})
});
@@ -64,9 +63,8 @@ export function JoblineBulkAssign({ setSelectedLines, selectedLines, insertAudit
setVisible(false);
}
} catch (error) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
notification.error({
title: t("parts_dispatch.errors.creating", {
error: error
})
});

View File

@@ -82,16 +82,16 @@ export function JobLineConvertToLabor({
});
if (jobUpdate.errors) {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
message: JSON.stringify(jobUpdate.errors)
})
});
return;
}
if (lineUpdate.errors) {
notification["error"]({
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
message: JSON.stringify(lineUpdate.errors)
})
});

View File

@@ -67,9 +67,8 @@ export function JobLineDispatchButton({
});
if (result.errors) {
console.log("🚀 ~ handleConvert ~ result.errors:", result.errors);
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
notification.error({
title: t("parts_dispatch.errors.creating", {
error: result.errors
})
});
@@ -91,9 +90,8 @@ export function JobLineDispatchButton({
setVisible(false);
} catch (error) {
console.log("🚀 ~ handleConvert ~ error:", error);
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
notification.error({
title: t("parts_dispatch.errors.creating", {
error: error
})
});

View File

@@ -44,17 +44,17 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
});
if (!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
notification.success({ title: t("joblines.successes.saved") });
} else {
notification["error"]({
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
error: JSON.stringify(result.errors)
})
});
}
} catch (error) {
notification["error"]({
message: t("joblines.errors.saving", { error: error?.message || String(error) })
notification.error({
title: t("joblines.errors.saving", { error: error?.message || String(error) })
});
} finally {
setSaving(false);

View File

@@ -32,10 +32,10 @@ export default function JobLineNotePopup({ jobline, disabled }) {
});
if (!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
notification.success({ title: t("joblines.successes.saved") });
} else {
notification["error"]({
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -40,10 +40,10 @@ export function JobLineStatusPopup({ bodyshop, jobline, disabled }) {
});
if (!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
notification.success({ title: t("joblines.successes.saved") });
} else {
notification["error"]({
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -45,7 +45,7 @@ export function JoblineTeamAssignment({ bodyshop, jobline, disabled, jobId, inse
});
if (!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
notification.success({ title: t("joblines.successes.saved") });
//insert the audit trail here.
const teamName = bodyshop.employee_teams.find((et) => et.id === assignedTeam)?.name;
insertAuditTrail({
@@ -53,8 +53,8 @@ export function JoblineTeamAssignment({ bodyshop, jobline, disabled, jobId, inse
operation: AuditTrailMapping.assignedlinehours(teamName, jobline.mod_lb_hrs)
});
} else {
notification["error"]({
message: t("joblines.errors.saving", {
notification.error({
title: t("joblines.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -71,12 +71,12 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
if (jobLineEditModal.actions.refetch) jobLineEditModal.actions.refetch();
//Need to recalcuate totals.
toggleModalVisible();
notification["success"]({
message: t("joblines.successes.created")
notification.success({
title: t("joblines.successes.created")
});
} else {
notification["error"]({
message: t("joblines.errors.creating", {
notification.error({
title: t("joblines.errors.creating", {
message: JSON.stringify(r.errors.message)
})
});
@@ -100,12 +100,12 @@ function JobLinesUpsertModalContainer({ jobLineEditModal, toggleModalVisible, bo
refetchQueries: ["GET_LINE_TICKET_BY_PK"]
});
if (!r.errors) {
notification["success"]({
message: t("joblines.successes.updated")
notification.success({
title: t("joblines.successes.updated")
});
} else {
notification["success"]({
message: t("joblines.errors.updating", {
notification.success({
title: t("joblines.errors.updating", {
message: JSON.stringify(r.errors.message)
})
});

View File

@@ -21,10 +21,10 @@ export default function JobRemoveFromPartsQueue({ checked, jobId }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -69,14 +69,14 @@ export function ScoreboardAddButton({ bodyshop, job, disabled, ...otherBtnProps
}
if (result.errors) {
notification["error"]({
message: t("scoreboard.errors.adding", {
notification.error({
title: t("scoreboard.errors.adding", {
message: JSON.stringify(result.errors)
})
});
} else {
notification["success"]({
message: t("scoreboard.successes.added")
notification.success({
title: t("scoreboard.successes.added")
});
}
setLoading(false);

View File

@@ -2,7 +2,7 @@ import { LoadingOutlined } from "@ant-design/icons";
import { useLazyQuery } from "@apollo/client/react";
import { Select, Space, Spin, Tag } from "antd";
import _ from "lodash";
import { forwardRef, useEffect, useState } from "react";
import { forwardRef, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE, SEARCH_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries";
import AlertComponent from "../alert/alert.component";
@@ -16,19 +16,17 @@ const JobSearchSelect = (
) => {
const { t } = useTranslation();
const [theOptions, setTheOptions] = useState([]);
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_JOBS_FOR_AUTOCOMPLETE, {});
const [callSearch, { loading, error, data }] = useLazyQuery(SEARCH_JOBS_FOR_AUTOCOMPLETE);
const [callIdSearch, { loading: idLoading, error: idError, data: idData }] = useLazyQuery(
SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE
);
const executeSearch = (v) => {
if (v && v.variables?.search !== "" && v.variables.search.length >= 2) callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);
const debouncedExecuteSearch = useMemo(() => {
return _.debounce((value) => {
if (value == null || value === "" || value.length < 2) return;
const handleSearch = (value) => {
debouncedExecuteSearch({
const variables = {
search: value,
...(convertedOnly || notExported
? {
@@ -37,12 +35,26 @@ const JobSearchSelect = (
...(notInvoiced ? { notInvoiced: true } : {})
}
: {})
});
};
callSearch({ variables });
}, 500);
}, [callSearch, convertedOnly, notExported, notInvoiced]);
useEffect(() => {
return () => {
debouncedExecuteSearch.cancel();
};
}, [debouncedExecuteSearch]);
const handleSearch = (value) => {
debouncedExecuteSearch(value);
};
useEffect(() => {
// Keep OLD "truthy" semantics (only fetch by id when value is truthy)
if (restProps.value) {
callIdSearch({ id: restProps.value }); // Sometimes results in a no-op. Not sure how to fix.
callIdSearch({ variables: { id: restProps.value } });
}
}, [restProps.value, callIdSearch]);
@@ -58,6 +70,7 @@ const JobSearchSelect = (
return (
<div>
<Select
{...restProps}
ref={ref}
disabled={disabled}
showSearch={{
@@ -65,25 +78,19 @@ const JobSearchSelect = (
onSearch: handleSearch
}}
autoFocus
allowClear={!loading}
style={{
width: "100%"
}}
//loading={loading || idLoading}
suffixIcon={(loading || idLoading) && <Spin />}
notFoundContent={loading ? <LoadingOutlined /> : null}
{...restProps}
allowClear={!loading} // matches OLD
style={{ width: "100%" }}
suffixIcon={(loading || idLoading) && <Spin />} // matches OLD spinner semantics
notFoundContent={loading ? <LoadingOutlined /> : null} // matches OLD (loading only)
>
{theOptions
? theOptions.map((o) => (
<Option key={o.id} value={o.id} status={o.status}>
<Space align="center">
<span>
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
o.ro_number || t("general.labels.na")
} | ${OwnerNameDisplayFunction(o)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
o.v_model_desc || ""
}`}
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${o.ro_number || t("general.labels.na")} | ${OwnerNameDisplayFunction(
o
)} | ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`}
</span>
<Tag>
<strong>{o.status}</strong>
@@ -99,4 +106,5 @@ const JobSearchSelect = (
</div>
);
};
export default forwardRef(JobSearchSelect);

View File

@@ -15,9 +15,8 @@ export default function JobSendPartPriceChangeComponent({ job, disabled }) {
const ppcData = await axios.post("/job/ppc", { jobid: job.id });
await axios.post("http://localhost:1337/ppc/", ppcData.data);
} catch (error) {
notification.open({
type: "error",
message: t("jobs.errors.partspricechange", {
notification.error({
title: t("jobs.errors.partspricechange", {
error: JSON.stringify(error)
})
});

View File

@@ -7,9 +7,9 @@ export default function JobSyncButton({ job }) {
const { t } = useTranslation();
const history = useNavigate();
const handleClick = () => {
history(`/manage/available?availableJobId=${job.available_jobs[0].id}&clm_no=${job.clm_no}`);
history(`/manage/available?availableJobId=${job?.available_jobs?.[0]?.id}&clm_no=${job?.clm_no}`);
};
if (job?.available_jobs && job.available_jobs.length > 0)
if (job?.available_jobs && job?.available_jobs?.length > 0)
return (
<Button onClick={handleClick}>
<SyncOutlined />

View File

@@ -33,12 +33,12 @@ export function JobTotalsCashDiscount({ bodyshop, amountDinero }) {
});
if (data?.error) {
notification.open({ type: "error", message: data.error || errorMessage });
notification.error({ title: data.error || errorMessage });
} else {
setFee(data?.fee ?? 0);
}
} catch (error) {
notification.open({ type: "error", message: error.response?.data?.error || errorMessage });
notification.error({ title: error.response?.data?.error || errorMessage });
} finally {
setLoading(false);
}

View File

@@ -28,7 +28,7 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
variables: { jobId: job.id, status: status }
})
.then(() => {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobstatuschange(status),
@@ -37,7 +37,7 @@ export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
// refetch();
})
.catch(() => {
notification["error"]({ message: t("jobs.errors.saving") });
notification.error({ title: t("jobs.errors.saving") });
});
};

View File

@@ -30,10 +30,10 @@ export function JobsAdminClass({ bodyshop, job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -61,10 +61,10 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -21,10 +21,10 @@ export default function JobAdminDeleteIntake({ job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});
@@ -39,10 +39,10 @@ export default function JobAdminDeleteIntake({ job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -41,15 +41,15 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, currentUser,
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkforreexport(),
type: "admin_jobmarkforreexport"
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});
@@ -82,15 +82,15 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, currentUser,
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkexported(),
type: "admin_jobmarkexported"
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});
@@ -108,15 +108,15 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, currentUser,
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobuninvoice(),
type: "admin_jobuninvoice"
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -20,10 +20,10 @@ export default function JobAdminOwnerReassociate({ job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -31,7 +31,7 @@ export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_job_remove_from_ar(value),
@@ -39,8 +39,8 @@ export function JobsAdminRemoveAR({ insertAuditTrail, job }) {
});
setSwitchValue(value);
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -37,7 +37,7 @@ export function JobsAdminUnvoid({ insertAuditTrail, bodyshop, job, currentUser }
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
@@ -45,8 +45,8 @@ export function JobsAdminUnvoid({ insertAuditTrail, bodyshop, job, currentUser }
type: "admin_jobunvoid"
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -20,10 +20,10 @@ export default function JobAdminOwnerReassociate({ job }) {
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -47,8 +47,8 @@ export function JobsAvailableScan({ partnerVersion, refetch }) {
//Came through
if (refetch) refetch();
} else {
notification["error"]({
message: t("jobs.errors.scanimport", { message: response.data.error })
notification.error({
title: t("jobs.errors.scanimport", { message: response.data.error })
});
}
setLoading(false);

View File

@@ -129,8 +129,8 @@ export function JobsAvailableComponent({ bodyshop, loading, data, refetch, addJo
<Button
onClick={() => {
deleteJob({ variables: { id: record.id } }).then(() => {
notification["success"]({
message: t("jobs.successes.deleted")
notification.success({
title: t("jobs.successes.deleted")
});
refetch();
});
@@ -182,16 +182,16 @@ export function JobsAvailableComponent({ bodyshop, loading, data, refetch, addJo
onClick={() => {
deleteAllAvailableJobs()
.then((r) => {
notification["success"]({
message: t("jobs.successes.all_deleted", {
notification.success({
title: t("jobs.successes.all_deleted", {
count: r.data.delete_available_jobs.affected_rows
})
});
refetch();
})
.catch((r) => {
notification["error"]({
message: t("jobs.errors.deleted") + " " + r.message
notification.error({
title: t("jobs.errors.deleted") + " " + r.message
});
});
}}

View File

@@ -103,8 +103,8 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
if (!(estData && estData.est_data)) {
//We don't have the right data. Error!
setInsertLoading(false);
notification["error"]({
message: t("jobs.errors.creating", { error: "No job data present." })
notification.error({
title: t("jobs.errors.creating", { error: "No job data present." })
});
return;
}
@@ -184,8 +184,8 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
id: r.data.insert_jobs.returning[0].id
});
notification["success"]({
message: t("jobs.successes.created"),
notification.success({
title: t("jobs.successes.created"),
onClick: () => {
history(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
}
@@ -208,8 +208,8 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} catch (r) {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", { error: r.message })
notification.error({
title: t("jobs.errors.creating", { error: r.message })
});
refetch().catch((err) => {
console.error(`Something went wrong in jobs available table container - ${err.message || ""}`);
@@ -231,8 +231,8 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
if (!(estData && estData.est_data)) {
//We don't have the right data. Error!
setInsertLoading(false);
notification["error"]({
message: t("jobs.errors.creating", { error: "No job data present." })
notification.error({
title: t("jobs.errors.creating", { error: "No job data present." })
});
} else {
//create upsert job
@@ -289,8 +289,8 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
if (updateResult.errors) {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", {
notification.error({
title: t("jobs.errors.creating", {
error: JSON.stringify(updateResult.errors)
})
});
@@ -304,14 +304,14 @@ export function JobsAvailableContainer({ bodyshop, currentUser, insertAuditTrail
});
if (newTotals.status !== 200) {
notification["error"]({
message: t("jobs.errors.totalscalc")
notification.error({
title: t("jobs.errors.totalscalc")
});
setInsertLoading(false);
return;
}
notification["success"]({
message: t("jobs.successes.supplemented"),
notification.success({
title: t("jobs.successes.supplemented"),
onClick: () => {
history(`/manage/jobs/${updateResult.data.update_jobs.returning[0].id}`);
}

View File

@@ -33,7 +33,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail, isPar
variables: { jobId: job.id, status: status }
})
.then(() => {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobstatuschange(status),
@@ -42,7 +42,7 @@ export function JobsChangeStatus({ job, bodyshop, jobRO, insertAuditTrail, isPar
// refetch();
})
.catch(() => {
notification["error"]({ message: t("jobs.errors.saving") });
notification.error({ title: t("jobs.errors.saving") });
});
};

View File

@@ -77,8 +77,8 @@ export function JobsCloseExportButton({ bodyshop, currentUser, jobId, disabled,
console.log("handle -> XML", QbXmlResponse);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("jobs.errors.exporting", {
notification.error({
title: t("jobs.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message)
})
});
@@ -99,8 +99,8 @@ export function JobsCloseExportButton({ bodyshop, currentUser, jobId, disabled,
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("jobs.errors.exporting-partner")
notification.error({
title: t("jobs.errors.exporting-partner")
});
setLoading(false);
return;
@@ -116,10 +116,9 @@ export function JobsCloseExportButton({ bodyshop, currentUser, jobId, disabled,
//Uh oh. At least one was no good.
failedTransactions.forEach((ft) => {
//insert failed export log
notification.open({
notification.error({
// key: "failedexports",
type: "error",
message: t("jobs.errors.exporting", {
title: t("jobs.errors.exporting", {
error: ft.errorMessage || ""
})
});
@@ -169,10 +168,9 @@ export function JobsCloseExportButton({ bodyshop, currentUser, jobId, disabled,
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
notification.success({
key: "jobsuccessexport",
message: t("jobs.successes.exported")
title: t("jobs.successes.exported")
});
insertAuditTrail({
jobid: jobId,
@@ -181,18 +179,17 @@ export function JobsCloseExportButton({ bodyshop, currentUser, jobId, disabled,
});
updateJobCache(jobUpdateResponse.data.update_jobs.returning.map((job) => job.id));
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
notification.error({
title: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error)
})
});
}
}
if (bodyshop.accountingconfig?.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
notification.success({
key: "jobsuccessexport",
message: t("jobs.successes.exported")
title: t("jobs.successes.exported")
});
insertAuditTrail({
jobid: jobId,

View File

@@ -65,8 +65,8 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
if (!res.errors) {
refetch();
notification["success"]({
message: t("jobs.successes.converted")
notification.success({
title: t("jobs.successes.converted")
});
insertAuditTrail({

View File

@@ -15,8 +15,8 @@ export default function AddToProduction(apolloClient, jobId, completionCallback,
variables: { jobId: jobId, job: { inproduction: !remove } }
})
.then(() => {
notification["success"]({
message: i18n.t("jobs.successes.save")
notification.success({
title: i18n.t("jobs.successes.save")
});
store.dispatch(
@@ -29,8 +29,8 @@ export default function AddToProduction(apolloClient, jobId, completionCallback,
if (completionCallback) completionCallback();
})
.catch((error) => {
notification["errors"]({
message: i18n.t("jobs.errors.saving", {
notification.error({
title: i18n.t("jobs.errors.saving", {
error: JSON.stringify(error)
})
});

View File

@@ -182,7 +182,7 @@ export function JobsDetailHeaderActions({
completionCallback: (newJobId) => {
history(`/manage/jobs/${newJobId}`);
notification.success({
message: t("jobs.successes.duplicated")
title: t("jobs.successes.duplicated")
});
},
keepJobLines: true,
@@ -197,7 +197,7 @@ export function JobsDetailHeaderActions({
completionCallback: (newJobId) => {
history(`/manage/jobs/${newJobId}`);
notification.success({
message: t("jobs.successes.duplicated")
title: t("jobs.successes.duplicated")
});
},
keepJobLines: false,
@@ -215,14 +215,12 @@ export function JobsDetailHeaderActions({
},
refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"]
});
notification.open({
type: "success",
message: t("appointments.successes.created")
notification.success({
title: t("appointments.successes.created")
});
} catch (error) {
notification.open({
type: "error",
message: t("appointments.errors.saving", { error: error.message })
notification.error({
title: t("appointments.errors.saving", { error: error.message })
});
} finally {
setLoading(false);
@@ -236,13 +234,13 @@ export function JobsDetailHeaderActions({
if (!result.errors) {
notification.success({
message: t("jobs.successes.delete")
title: t("jobs.successes.delete")
});
//go back to jobs list.
history(`/manage/`);
} else {
notification.error({
message: t("jobs.errors.deleted", {
title: t("jobs.errors.deleted", {
error: JSON.stringify(result.errors)
})
});
@@ -293,10 +291,10 @@ export function JobsDetailHeaderActions({
});
if (!result.errors) {
notification.success({ message: t("csi.successes.created") });
notification.success({ title: t("csi.successes.created") });
} else {
notification.error({
message: t("csi.errors.creating", {
title: t("csi.errors.creating", {
message: JSON.stringify(result.errors)
})
});
@@ -335,7 +333,7 @@ export function JobsDetailHeaderActions({
);
} else {
notification.error({
message: t("messaging.error.invalidphone")
title: t("messaging.error.invalidphone")
});
}
}
@@ -347,7 +345,7 @@ export function JobsDetailHeaderActions({
}
} else {
notification.error({
message: t("csi.errors.notconfigured")
title: t("csi.errors.notconfigured")
});
}
} else {
@@ -377,7 +375,7 @@ export function JobsDetailHeaderActions({
setMessage(`${window.location.protocol}//${window.location.host}/csi/${job.csiinvites[0].id}`);
} else {
notification.error({
message: t("messaging.error.invalidphone")
title: t("messaging.error.invalidphone")
});
}
}
@@ -417,7 +415,7 @@ export function JobsDetailHeaderActions({
if (!result.errors) {
notification.success({
message: t("jobs.successes.voided")
title: t("jobs.successes.voided")
});
insertAuditTrail({
jobid: job.id,
@@ -428,7 +426,7 @@ export function JobsDetailHeaderActions({
history(`/manage/`);
} else {
notification.error({
message: t("jobs.errors.voiding", {
title: t("jobs.errors.voiding", {
error: JSON.stringify(result.errors)
})
});
@@ -461,7 +459,7 @@ export function JobsDetailHeaderActions({
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification.error({
message: t("jobs.errors.exporting", {
title: t("jobs.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message)
})
});
@@ -479,7 +477,7 @@ export function JobsDetailHeaderActions({
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification.error({
message: t("jobs.errors.exporting-partner")
title: t("jobs.errors.exporting-partner")
});
return;
@@ -491,10 +489,9 @@ export function JobsDetailHeaderActions({
//Uh oh. At least one was no good.
failedTransactions.forEach((ft) => {
//insert failed export log
notification.open({
notification.error({
// key: "failedexports",
type: "error",
message: t("jobs.errors.exporting", {
title: t("jobs.errors.exporting", {
error: ft.errorMessage || ""
})
});
@@ -504,10 +501,9 @@ export function JobsDetailHeaderActions({
} else {
//Insert success export log.
notification.open({
type: "success",
notification.success({
key: "jobsuccessexport",
message: t("jobs.successes.exported")
title: t("jobs.successes.exported")
});
}
};
@@ -575,7 +571,7 @@ export function JobsDetailHeaderActions({
});
if (!jobUpdate.errors) {
notification.success({
message: t("appointments.successes.canceled")
title: t("appointments.successes.canceled")
});
insertAuditTrail({
jobid: job.id,
@@ -952,11 +948,11 @@ export function JobsDetailHeaderActions({
if (!result.errors) {
notification.success({
message: t("jobs.successes.partsqueue")
title: t("jobs.successes.partsqueue")
});
} else {
notification.error({
message: t("jobs.errors.saving", {
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});
@@ -1145,12 +1141,12 @@ export function JobsDetailHeaderActions({
try {
await axios.post("/job/totals-recorder", { id: job.id });
notification.success({
message: t("general.messages.submit-for-testing")
title: t("general.messages.submit-for-testing")
});
} catch (err) {
console.error(`Error submitting job for testing: ${err?.message}`);
notification.error({
message: t("general.errors.submit-for-testing-error")
title: t("general.errors.submit-for-testing-error")
});
}
}

View File

@@ -91,8 +91,8 @@ export function JobsDetailHeaderActionsToggleProduction({
});
if (!res.errors) {
notification["success"]({
message: t("jobs.successes.converted")
notification.success({
title: t("jobs.successes.converted")
});
insertAuditTrail({

View File

@@ -104,7 +104,7 @@ export function JobsDetailHeader({ job, bodyshop, disabled, insertAuditTrail, is
});
} catch (error) {
notification.error({
message: t("jobs.errors.saving", { error: error.message })
title: t("jobs.errors.saving", { error: error.message })
});
}
};

View File

@@ -55,7 +55,7 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages, callback
// });
// if (!!result.errors) {
// notification["error"]({
// notification.error({
// message: t("documents.errors.updating", {
// message: JSON.stringify(result.errors),
// }),
@@ -82,10 +82,9 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages, callback
bodyshop.jobsizelimit - newJobData.data.documents_aggregate.aggregate.sum.size < transferedDocSizeTotal;
if (shouldPreventTransfer) {
notification.open({
notification.error({
key: "cannotuploaddocuments",
type: "error",
message: t("documents.labels.reassign_limitexceeded_title"),
title: t("documents.labels.reassign_limitexceeded_title"),
description: t("documents.labels.reassign_limitexceeded")
});
setLoading(false);
@@ -112,15 +111,15 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages, callback
if (callback) callback();
if (res.errors) {
notification["error"]({
message: t("documents.errors.updating", {
notification.error({
title: t("documents.errors.updating", {
message: JSON.stringify(res.errors)
})
});
}
if (!res.mutationResult?.errors) {
notification["success"]({
message: t("documents.successes.updated")
notification.success({
title: t("documents.successes.updated")
});
}
setOpen(false);

View File

@@ -30,16 +30,15 @@ export default function JobsDocumentsDeleteButton({ galleryImages, deletionCallb
});
if (res.data.error) {
notification["error"]({
message: t("documents.errors.deleting", {
notification.error({
title: t("documents.errors.deleting", {
error: JSON.stringify(res.data.error.response.errors)
})
});
} else {
notification.open({
notification.success({
key: "docdeletedsuccesfully",
type: "success",
message: t("documents.successes.delete")
title: t("documents.successes.delete")
});
if (deletionCallback) deletionCallback();

View File

@@ -58,7 +58,7 @@ export function JobsDocumentsImgproxyGalleryReassign({ bodyshop, galleryImages,
if (shouldPreventTransfer) {
notification.error({
key: "cannotuploaddocuments",
message: t("documents.labels.reassign_limitexceeded_title"),
title: t("documents.labels.reassign_limitexceeded_title"),
description: t("documents.labels.reassign_limitexceeded")
});
setLoading(false);
@@ -86,7 +86,7 @@ export function JobsDocumentsImgproxyGalleryReassign({ bodyshop, galleryImages,
if (res.errors) {
notification.error({
message: t("documents.errors.updating", {
title: t("documents.errors.updating", {
message: JSON.stringify(res.errors)
})
});

View File

@@ -35,21 +35,21 @@ export default function JobsDocumentsImgproxyDeleteButton({ galleryImages, delet
if (res.data.error) {
notification.error({
message: t("documents.errors.deleting", {
title: t("documents.errors.deleting", {
error: JSON.stringify(res.data.error.response.errors)
})
});
} else {
notification.success({
key: "docdeletedsuccesfully",
message: t("documents.successes.delete")
title: t("documents.successes.delete")
});
if (isFunction(deletionCallback)) deletionCallback();
}
} catch (error) {
notification.error({
message: t("documents.errors.deleting", {
title: t("documents.errors.deleting", {
error: error.message
})
});

View File

@@ -44,16 +44,15 @@ export function JobsDocumentsLocalDeleteButton({ bodyshop, getJobMedia, allMedia
);
if (delres.errors) {
notification["error"]({
message: t("documents.errors.deleting", {
notification.error({
title: t("documents.errors.deleting", {
message: JSON.stringify(delres.errors)
})
});
} else {
notification.open({
notification.success({
key: "docdeletedsuccesfully",
type: "success",
message: t("documents.successes.delete")
title: t("documents.successes.delete")
});
}
getJobMedia(jobid);

View File

@@ -73,8 +73,8 @@ export function JobsExportAllButton({
);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("jobs.errors.exporting", {
notification.error({
title: t("jobs.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message)
})
});
@@ -95,8 +95,8 @@ export function JobsExportAllButton({
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("jobs.errors.exporting-partner")
notification.error({
title: t("jobs.errors.exporting-partner")
});
setLoading(false);
return;
@@ -114,10 +114,9 @@ export function JobsExportAllButton({
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.forEach((ft) => {
notification.open({
notification.error({
// key: "failedexports",
type: "error",
message: t("jobs.errors.exporting", {
title: t("jobs.errors.exporting", {
error: ft.errorMessage || ""
})
});
@@ -167,10 +166,9 @@ export function JobsExportAllButton({
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
notification.success({
key: "jobsuccessexport",
message: t("jobs.successes.exported")
title: t("jobs.successes.exported")
});
jobUpdateResponse.data.update_jobs.returning.forEach((job) => {
insertAuditTrail({
@@ -181,18 +179,17 @@ export function JobsExportAllButton({
});
updateJobCache(jobUpdateResponse.data.update_jobs.returning.map((job) => job.id));
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
notification.error({
title: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error)
})
});
}
}
if (bodyshop.accountingconfig?.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
notification.success({
key: "jobsuccessexport",
message: t("jobs.successes.exported")
title: t("jobs.successes.exported")
});
const successfulTransactionsSet = [
...new Set(successfulTransactions.map((st) => st[bodyshop.accountingconfig?.qbo ? "jobid" : "id"]))

View File

@@ -70,7 +70,7 @@ export function JobMarkSelectedExported({
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
notification.success({ title: t("jobs.successes.save") });
result.data.update_jobs.returning.forEach((job) => {
console.log("results job", job.id, "audit: ", AuditTrailMapping.admin_jobmarkexported());
insertAuditTrail({
@@ -80,8 +80,8 @@ export function JobMarkSelectedExported({
});
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -40,8 +40,8 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
}
}).then(() => {
refetch();
notification["success"]({
message: t("notes.successes.deleted")
notification.success({
title: t("notes.successes.deleted")
});
insertAuditTrail({
jobid: jobId,

View File

@@ -50,14 +50,14 @@ export function LaborAllocationsAdjustmentEdit({
});
if (result.errors) {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
message: JSON.stringify(result.errors)
})
});
} else {
notification["success"]({
message: t("jobs.successes.save")
notification.success({
title: t("jobs.successes.save")
});
insertAuditTrail({
jobid: jobId,

View File

@@ -215,14 +215,12 @@ export function PayrollLaborAllocationsTable({
if (response.status === 200) {
if (response.data.success !== false) {
notification.open({
type: "success",
message: t("timetickets.successes.payall")
notification.success({
title: t("timetickets.successes.payall")
});
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
notification.error({
title: t("timetickets.errors.payall", {
error: response.data.error
})
});
@@ -230,9 +228,8 @@ export function PayrollLaborAllocationsTable({
if (refetch) refetch();
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
notification.error({
title: t("timetickets.errors.payall", {
error: JSON.stringify("")
})
});

View File

@@ -68,8 +68,8 @@ export function NoteUpsertModalContainer({ currentUser, noteUpsertModal, toggleM
},
refetchQueries: ["GET_JOB_BY_PK", "QUERY_JOB_CARD_DETAILS", "QUERY_PARTS_QUEUE_CARD_DETAILS"]
}).then(() => {
notification["success"]({
message: t("notes.successes.updated")
notification.success({
title: t("notes.successes.updated")
});
insertAuditTrail({
jobid: context.jobId,
@@ -136,8 +136,8 @@ export function NoteUpsertModalContainer({ currentUser, noteUpsertModal, toggleM
if (refetch) refetch();
form.resetFields();
toggleModalVisible();
notification["success"]({
message: t("notes.successes.create")
notification.success({
title: t("notes.successes.create")
});
insertAuditTrail({
jobid: context.jobId,

View File

@@ -77,14 +77,14 @@ const NotificationSettingsForm = ({ currentUser, bodyshop }) => {
if (!result?.errors) {
setAutoAddEnabled(checked);
setInitialAutoAdd(checked);
notification.success({ message: t("notifications.labels.auto-add-success") });
notification.success({ title: t("notifications.labels.auto-add-success") });
setIsDirty(false); // Reset dirty state if only auto-add was changed
} else {
throw new Error("Failed to update auto-add setting");
}
} catch {
setAutoAddEnabled(!checked); // Revert on error
notification.error({ message: t("notifications.labels.auto-add-failure") });
notification.error({ title: t("notifications.labels.auto-add-failure") });
}
}
};
@@ -96,14 +96,14 @@ const NotificationSettingsForm = ({ currentUser, bodyshop }) => {
try {
const result = await updateNotificationSettings({ variables: { id: userId, ns: values } });
if (!result?.errors) {
notification.success({ message: t("notifications.labels.notification-settings-success") });
notification.success({ title: t("notifications.labels.notification-settings-success") });
setInitialValues(values);
setIsDirty(false);
} else {
throw new Error("Failed to update notification settings");
}
} catch {
notification.error({ message: t("notifications.labels.notification-settings-failure") });
notification.error({ title: t("notifications.labels.notification-settings-failure") });
}
}
};

View File

@@ -63,19 +63,19 @@ function OwnerDetailFormContainer({ owner, refetch, bodyshop }) {
});
if (result.errors) {
notification.error({
message: t("owners.errors.deleting", {
title: t("owners.errors.deleting", {
error: JSON.stringify(result.errors)
})
});
} else {
notification.success({
message: t("owners.successes.delete")
title: t("owners.successes.delete")
});
navigate(`/manage/owners`);
}
} catch (error) {
notification.error({
message: t("owners.errors.deleting", {
title: t("owners.errors.deleting", {
error: error.message
})
});
@@ -93,20 +93,20 @@ function OwnerDetailFormContainer({ owner, refetch, bodyshop }) {
});
if (result.errors) {
notification.error({
message: t("owners.errors.saving", {
title: t("owners.errors.saving", {
error: JSON.stringify(result.errors)
})
});
} else {
notification.success({
message: t("owners.successes.save")
title: t("owners.successes.save")
});
if (refetch) await refetch();
form.resetFields();
}
} catch (error) {
notification.error({
message: t("owners.errors.saving", {
title: t("owners.errors.saving", {
error: error.message
})
});

View File

@@ -36,11 +36,11 @@ export default function OwnerDetailUpdateJobsComponent({ owner, selectedJobs, di
}
})
.then(() => {
notification["success"]({ message: t("jobs.successes.updated") });
notification.success({ title: t("jobs.successes.updated") });
})
.catch((error) => {
notification["error"]({
message: t("jobs.errors.updating", { error: JSON.stringify(error) })
notification.error({
title: t("jobs.errors.updating", { error: JSON.stringify(error) })
});
});
};

View File

@@ -71,10 +71,13 @@ export default function OwnersListComponent({ loading, owners, total, refetch })
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 (
<Card
@@ -102,12 +105,13 @@ export default function OwnersListComponent({ loading, owners, total, refetch })
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
const updatedSearch = { ...search };
if (value?.length >= 3) {
search.search = value;
updatedSearch.search = value;
} else {
delete search.search;
delete updatedSearch.search;
}
history({ search: queryString.stringify(search) });
history({ search: queryString.stringify(updatedSearch) });
}}
enterButton
/>

View File

@@ -48,14 +48,14 @@ export async function checkPartnerStatus(bodyshop) {
// bodyshop.accountingconfig.qbo)
// )
// ) {
// notification["error"]({
// notification.error({
// title: "",
// message: i18n.t("general.messages.noacctfilepath"),
// });
// }
} catch (error) {
console.log("Partner is not running.", error);
// notification["error"]({
// notification.error({
// title: "",
// message: i18n.t("general.messages.partnernotrunning"),
// });

View File

@@ -23,9 +23,8 @@ export default function PartsDispatchExpander({ dispatch }) {
}
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.accepting", {
notification.error({
title: t("parts_dispatch.errors.accepting", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -38,8 +38,8 @@ export function PartsOrderBackorderEta({ backordered_eta, partsOrderStatus, part
});
if (result.errors) {
notification["error"]({
message: t("parts_orders.errors.backordering", {
notification.error({
title: t("parts_orders.errors.backordering", {
message: JSON.stringify(result.errors)
})
});

View File

@@ -42,12 +42,12 @@ export default function PartsOrderCmReceived({ checked, orderLineId, partsOrderI
});
if (!result.errors) {
notification["success"]({
message: t("parts_orders.successes.line_updated")
notification.success({
title: t("parts_orders.successes.line_updated")
});
} else {
notification["error"]({
message: t("parts_orders.errors.saving", {
notification.error({
title: t("parts_orders.errors.saving", {
error: JSON.stringify(result.errors)
})
});

View File

@@ -48,8 +48,8 @@ export function PartsOrderLineBackorderButton({ partsOrderStatus, partsLineId, j
});
if (result.errors) {
notification["error"]({
message: t("parts_orders.errors.backordering", {
notification.error({
title: t("parts_orders.errors.backordering", {
message: JSON.stringify(result.errors)
})
});

View File

@@ -387,7 +387,7 @@ export function PartsOrderListTableDrawerComponent({
onClose={() => handleOnRowClick(null)}
open={selectedpartsorder}
closable
width={drawerPercentage}
size={drawerPercentage}
>
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
</Drawer>

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