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

@@ -59,8 +59,8 @@ export function ContractCreatePageContainer({ bodyshop, setBreadcrumbs, setSelec
if (!result.errors) {
//Update the courtesy car to have the damage.
logImEXEvent("courtesy_car_contract_created", {});
notification["success"]({
message: t("contracts.successes.saved")
notification.success({
title: t("contracts.successes.saved")
});
//Intake the job if required
@@ -76,8 +76,8 @@ export function ContractCreatePageContainer({ bodyshop, setBreadcrumbs, setSelec
}
});
if (result2.errors) {
notification["error"]({
message: t("jobs.errors.saving", {
notification.error({
title: t("jobs.errors.saving", {
error: JSON.stringify(!result2.errors)
})
});
@@ -89,15 +89,15 @@ export function ContractCreatePageContainer({ bodyshop, setBreadcrumbs, setSelec
form.resetFields();
history(`/manage/courtesycars/contracts/${result.data.insert_cccontracts.returning[0].id}`);
} else {
notification["error"]({
message: t("contracts.errors.saving", {
notification.error({
title: t("contracts.errors.saving", {
error: JSON.stringify(!result.errors)
})
});
}
} else {
notification["error"]({
message: t("contracts.errors.selectjobandcar")
notification.error({
title: t("contracts.errors.selectjobandcar")
});
}
setLoading(false);

View File

@@ -86,14 +86,14 @@ export function ContractDetailPageContainer({ setBreadcrumbs, addRecentItem, set
variables: { cccontract: { ...values }, contractId: contractId }
});
if (result.errors) {
notification["error"]({
message: t("contracts.errors.saving", {
notification.error({
title: t("contracts.errors.saving", {
message: JSON.stringify(result.errors)
})
});
return;
}
notification["success"]({ message: t("contracts.successes.saved") });
notification.success({ title: t("contracts.successes.saved") });
if (refetch) await refetch();
setsaveLoading(false);

View File

@@ -38,8 +38,8 @@ export function CourtesyCarCreateContainer({ bodyshop, setBreadcrumbs, setSelect
});
if (result.errors) {
notification["error"]({
message: t("courtesycars.errors.saving", {
notification.error({
title: t("courtesycars.errors.saving", {
message: JSON.stringify(result.errors)
})
});
@@ -48,7 +48,7 @@ export function CourtesyCarCreateContainer({ bodyshop, setBreadcrumbs, setSelect
setLoading(false);
form.resetFields();
form.resetFields();
notification["success"]({ message: t("courtesycars.successes.saved") });
notification.success({ title: t("courtesycars.successes.saved") });
history(`/manage/courtesycars/${result.data.insert_courtesycars.returning[0].id}`);
}
};

View File

@@ -104,13 +104,13 @@ export function CourtesyCarDetailPageContainer({ setBreadcrumbs, addRecentItem,
});
if (result.errors) {
notification["error"]({
message: t("courtesycars.errors.saving", { error: error })
notification.error({
title: t("courtesycars.errors.saving", { error: error })
});
}
notification["success"]({
message: t("courtesycars.successes.saved")
notification.success({
title: t("courtesycars.successes.saved")
});
setSaveLoading(false);

View File

@@ -83,9 +83,8 @@ export function DmsContainer({ setBreadcrumbs, setSelectedHeader }) {
});
socket.on("ap-export-complete", () => {
notification.open({
type: "success",
message: t("jobs.labels.dms.apexported")
notification.success({
title: t("jobs.labels.dms.apexported")
});
});

View File

@@ -235,8 +235,8 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
const sev = severity || (isRrOpenRoLimit ? "warning" : "error");
if (!isRrOpenRoLimit) {
const notifyKind = sev === "warning" && typeof notification.warning === "function" ? "warning" : "error";
notification[notifyKind]({ message: vendorTitle, description: msg, duration: 10 });
const type = sev === "warning" && typeof notification.warning === "function" ? "warning" : "error";
notification.open({ type, tile: vendorTitle, description: msg, duration: 10 });
} else {
setRrOpenRoLimit(true);
}
@@ -307,7 +307,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
const onConnectError = (err) => {
// Legacy and WSS both emit this
console.log(`connect_error due to ${err}`, err);
notification.error({ message: err.message });
notification.error({ title: err.message });
};
activeSocket.on("disconnect", onDisconnect);
@@ -333,7 +333,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
// Success / Failed
const onExportSuccess = (payload) => {
const jobIdResolved = payload?.jobId ?? payload;
notification.success({ message: t("jobs.successes.exported") });
notification.success({ title: t("jobs.successes.exported") });
// Clear RR Validation flag if any
setrrValidationPending(false);
@@ -414,7 +414,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
if (!isWssMode(mode)) return; // RR is WSS-only
activeSocket.emit("rr-finalize-repair-order", { jobId }, (ack) => {
if (ack?.ok) return;
if (ack?.error) notification.error({ message: ack.error });
if (ack?.error) notification.error({ title: ack.error });
});
};

View File

@@ -114,8 +114,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
// notification["success"]({ message: t("jobs.successes.save") });
// form.resetFields();
} else {
notification["error"]({
message: t("job.errors.saving", {
notification.error({
title: t("job.errors.saving", {
error: JSON.stringify(result.errors)
})
});
@@ -125,8 +125,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
if (!closeResult.errors) {
setLoading(false);
notification["success"]({
message: t("jobs.successes.closed")
notification.success({
title: t("jobs.successes.closed")
});
insertAuditTrail({
jobid: job.id,
@@ -143,8 +143,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
// history(`/manage/jobs/${job.id}`);
} else {
setLoading(false);
notification["error"]({
message: t("job.errors.closing", {
notification.error({
title: t("job.errors.closing", {
error: JSON.stringify(closeResult.errors)
})
});

View File

@@ -85,8 +85,8 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, curr
logImEXEvent("manual_job_create_completed", {});
})
.catch((error) => {
notification["error"]({
message: t("jobs.errors.creating", { error: error })
notification.error({
title: t("jobs.errors.creating", { error: error })
});
setState({ ...state, error: error });
});

View File

@@ -192,12 +192,12 @@ export function JobsDetailPage({
});
if (newTotals.status !== 200 || result.errors) {
notification["error"]({
message: t("jobs.errors.totalscalc")
notification.error({
title: t("jobs.errors.totalscalc")
});
} else {
notification["success"]({
message: t("jobs.successes.savetitle")
notification.success({
title: t("jobs.successes.savetitle")
});
const changedAuditFields = form.getFieldsValue(
[
@@ -261,8 +261,8 @@ export function JobsDetailPage({
form.resetFields();
}
} catch {
notification["error"]({
message: t("jobs.errors.totalscalc")
notification.error({
title: t("jobs.errors.totalscalc")
});
} finally {
setLoading(false);

View File

@@ -145,10 +145,16 @@ export function Manage({ conflict, bodyshop, partsManagementOnly, isDarkMode, cu
}, [t]);
// This is a required safety check to prevent parts management only users from accessing /manage based routes
useEffect(() => {
if (partsManagementOnly && !import.meta.env.DEV) {
// This NEEDS to be done this way, DO NOT use the react router, it will cause a ton of side effects
window.location = "/parts";
}
}, [partsManagementOnly]);
// Prevent rendering if redirecting
if (partsManagementOnly && !import.meta.env.DEV) {
// This NEEDS to be done this way, DO NOT use the react router, it will cause a ton of side effects
window.location = "/parts";
return null; // Prevent further rendering
return null;
}
const AppRouteTable = (

View File

@@ -23,6 +23,7 @@ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
isPartsEntry: selectIsPartsEntry
});
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
dispatch(
@@ -36,9 +37,13 @@ const mapDispatchToProps = (dispatch) => ({
export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO, job, refetch, isPartsEntry }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const history = useNavigate();
const navigate = useNavigate();
const location = useLocation();
const [loading] = useState(false);
const search = queryString.parse(useLocation().search);
// Parse once per render; do not mutate this object.
const search = queryString.parse(location.search);
const billsQuery = useQuery(QUERY_PARTS_BILLS_BY_JOBID, {
variables: { jobid: job.id },
@@ -47,44 +52,43 @@ export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO
});
useEffect(() => {
//form.setFieldsValue(transormJobToForm(job));
form.resetFields();
}, [form, job]);
const navigateWithSearch = (nextSearchObj) => {
navigate({ search: queryString.stringify(nextSearchObj) });
};
const omitKey = (obj, key) => {
if (!obj) return {};
// immutable omit
// eslint-disable-next-line no-unused-vars
const { [key]: _omit, ...rest } = obj;
return rest;
};
const handleBillOnRowClick = (record) => {
if (record) {
if (record.id) {
search.billid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.billid;
history({ search: queryString.stringify(search) });
if (record?.id) {
navigateWithSearch({ ...search, billid: record.id });
return;
}
navigateWithSearch(omitKey(search, "billid"));
};
const handlePartsOrderOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsorderid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.partsorderid;
history({ search: queryString.stringify(search) });
if (record?.id) {
navigateWithSearch({ ...search, partsorderid: record.id });
return;
}
navigateWithSearch(omitKey(search, "partsorderid"));
};
const handlePartsDispatchOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsdispatchid = record.id;
history.push({ search: queryString.stringify(search) });
}
} else {
delete search.partsdispatchid;
history.push({ search: queryString.stringify(search) });
if (record?.id) {
navigateWithSearch({ ...search, partsdispatchid: record.id });
return;
}
navigateWithSearch(omitKey(search, "partsdispatchid"));
};
const menuExtra = (
@@ -98,7 +102,9 @@ export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO
<SyncOutlined />
{t("general.labels.refresh")}
</Button>
<JobsChangeStatus job={job} />
<Button
onClick={() => {
setPrintCenterContext({
@@ -127,6 +133,8 @@ export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO
</Space>
);
const activeTabKey = (search?.tab && String(search.tab)) || "repairdata";
return (
<div>
<JobLineUpsertModalContainer />
@@ -135,9 +143,10 @@ export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO
<JobsDetailHeader job={job} />
<Divider orientation="horizontal" />
<FormFieldsChanged form={form} />
<Tabs
defaultActiveKey={search.tab}
onChange={(key) => history({ search: `?tab=${key}` })}
defaultActiveKey={activeTabKey}
onChange={(key) => navigateWithSearch({ ...search, tab: key })}
tabBarStyle={{ fontWeight: "bold", borderBottom: "10px" }}
items={[
{
@@ -159,7 +168,6 @@ export function SimplifiedPartsJobDetailComponent({ setPrintCenterContext, jobRO
/>
)
},
{
key: "partssublet",
id: "job-details-partssublet",

View File

@@ -31,6 +31,14 @@ const VehiclesContainer = lazy(() => import("../vehicles/vehicles.page.container
const VehiclesDetailContainer = lazy(() => import("../vehicles-detail/vehicles-detail.page.container.jsx"));
const { Content } = Layout;
// Redirector to strip '/parts/jobs' from path for non-detail routes
function JobsStripRedirect() {
const location = useLocation();
const { pathname, search, hash } = location;
const target = pathname.replace("/parts/jobs", "/parts") + (search || "") + (hash || "");
return <Navigate to={target} replace />;
}
const mapStateToProps = createStructuredSelector({
conflict: selectInstanceConflict,
bodyshop: selectBodyshop
@@ -39,15 +47,6 @@ const mapStateToProps = createStructuredSelector({
export function SimplifiedPartsPage({ conflict, bodyshop }) {
const { t } = useTranslation();
// Redirector to strip '/parts/jobs' from path for non-detail routes
function JobsStripRedirect() {
// lazy import to avoid top-level import churn
const location = useLocation();
const { pathname, search, hash } = location;
const target = pathname.replace("/parts/jobs", "/parts") + (search || "") + (hash || "");
return <Navigate to={target} replace />;
}
// Centralized alerts handling (fetch + dedupe + notifications)
useAlertsNotifications();