Revert "Release/2026 02 27 (pull request #3070)"

This commit is contained in:
Patrick Fic
2026-03-04 16:18:44 +00:00
parent 522f2b9e26
commit c9e41ba72a
204 changed files with 5497 additions and 7715 deletions

View File

@@ -1,5 +1,5 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Checkbox, Input, Space, Typography } from "antd";
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
import { useQuery } from "@apollo/client/react";
import axios from "axios";
import queryString from "query-string";
@@ -18,7 +18,6 @@ import { pageLimit } from "../../utils/config";
import { alphaSort, dateSort } from "../../utils/sorters";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { logImEXEvent } from "../../firebase/firebase.utils";
import ResponsiveTable from "../../components/responsive-table/responsive-table.component";
const mapDispatchToProps = (dispatch) => ({
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" }))
@@ -230,7 +229,7 @@ export function BillsListPage({ loading, data, refetch, total, setBillEnterConte
>
<PartsOrderModalContainer />
<ResponsiveTable
<Table
loading={loading || searchLoading}
// scroll={{
// x: "50%", // y: "40rem"
@@ -250,7 +249,6 @@ export function BillsListPage({ loading, data, refetch, total, setBillEnterConte
}
}
columns={columns}
mobileColumnKeys={["vendorname", "invoice_number", "ro_number", "total", "actions"]}
rowKey="id"
dataSource={search?.search ? openSearchResults : data}
onChange={handleTableChange}

View File

@@ -119,13 +119,12 @@ export function DmsContainer({ setBreadcrumbs, setSelectedHeader }) {
setLogLevel(value);
socket.emit("set-log-level", value);
}}
options={[
{ key: "DEBUG", value: "DEBUG", label: "DEBUG" },
{ key: "INFO", value: "INFO", label: "INFO" },
{ key: "WARN", value: "WARN", label: "WARN" },
{ key: "ERROR", value: "ERROR", label: "ERROR" }
]}
/>
>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {

View File

@@ -11,7 +11,7 @@ import { useSocket } from "../../contexts/SocketIO/useSocket.js";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { insertAuditTrail, setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
@@ -29,8 +29,7 @@ import DmsAllocationsSummary from "../../components/dms-allocations-summary/dms-
import RrAllocationsSummary from "../../components/dms-allocations-summary/rr-dms-allocations-summary.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
@@ -66,41 +65,7 @@ const DMS_SOCKET_EVENTS = {
}
};
const stripRrXmlFromPayload = (input) => {
if (input == null || typeof input !== "object") return input;
let target = null;
try {
target = JSON.parse(JSON.stringify(input));
} catch {
// Fallback to in-place scrub if cloning fails.
target = input;
}
const scrub = (node) => {
if (node == null || typeof node !== "object") return;
if (Array.isArray(node)) {
node.forEach(scrub);
return;
}
delete node.requestXml;
delete node.responseXml;
if (node.xml && typeof node.xml === "object") {
delete node.xml.request;
delete node.xml.response;
if (Object.keys(node.xml).length === 0) delete node.xml;
}
Object.values(node).forEach(scrub);
};
scrub(target);
return target;
};
export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) {
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) {
const {
treatments: { Fortellis }
} = useTreatmentsWithConfig({
@@ -112,16 +77,6 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
const { t } = useTranslation();
const [resetAfterReconnect, setResetAfterReconnect] = useState(false);
const [allocationsSummary, setAllocationsSummary] = useState(null);
const [reconnectNonce, setReconnectNonce] = useState(0);
const isDevEnv = import.meta.env.DEV;
const isProdEnv = import.meta.env.PROD;
const userEmail = (currentUser?.email || "").toLowerCase();
const devEmails = ["imex.dev", "rome.dev"];
const prodEmails = ["imex.prod", "rome.prod", "imex.test", "rome.test"];
const hasValidEmail = (emails) => emails.some((email) => userEmail.endsWith(email));
const canViewSensitiveRrXml = (isDevEnv && hasValidEmail(devEmails)) || (isProdEnv && hasValidEmail(prodEmails));
// Compute a single normalized mode and pick the proper socket
const mode = getDmsMode(bodyshop, Fortellis.treatment); // "rr" | "fortellis" | "cdk" | "pbs" | "none"
@@ -159,7 +114,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
const notification = useNotification();
const { socket: wsssocket, reconnectSocket } = useSocket();
const { socket: wsssocket } = useSocket();
const activeSocket = useMemo(() => (isWssMode(mode) ? wsssocket : legacySocket), [mode, wsssocket]);
const [isConnected, setIsConnected] = useState(!!activeSocket?.connected);
@@ -208,44 +163,21 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
const providerLabel = useMemo(
() =>
({
[DMS_MAP.reynolds]: t("dms.labels.provider_reynolds"),
[DMS_MAP.fortellis]: t("dms.labels.provider_fortellis"),
[DMS_MAP.cdk]: t("dms.labels.provider_cdk"),
[DMS_MAP.pbs]: t("dms.labels.provider_pbs")
})[mode] || t("dms.labels.provider_dms"),
[mode, t]
[DMS_MAP.reynolds]: "Reynolds",
[DMS_MAP.fortellis]: "Fortellis",
[DMS_MAP.cdk]: "CDK",
[DMS_MAP.pbs]: "PBS"
})[mode] || "DMS",
[mode]
);
const transportLabel = isWssMode(mode) ? t("dms.labels.transport_wss") : t("dms.labels.transport_ws");
const transportLabel = isWssMode(mode) ? "(WSS)" : "(WS)";
const bannerMessage = t("dms.labels.banner_message", {
provider: providerLabel,
transport: transportLabel,
status: isConnected ? t("dms.labels.banner_status_connected") : t("dms.labels.banner_status_disconnected")
});
const bannerMessage = `Posting to ${providerLabel} | ${transportLabel} | ${
isConnected ? "Connected" : "Disconnected"
}`;
const resetKey = useMemo(() => `${mode || "none"}-${jobId || "none"}`, [mode, jobId]);
const customerSelectorKey = useMemo(() => `${resetKey}-${reconnectNonce}`, [resetKey, reconnectNonce]);
const handleReconnectClick = async () => {
setResetAfterReconnect(true);
setReconnectNonce((n) => n + 1);
if (!activeSocket) return;
if (isWssMode(mode)) {
setActiveLogLevel(logLevel);
const didReconnect = await reconnectSocket?.({ forceRefreshToken: true });
if (!didReconnect) {
activeSocket.disconnect();
setTimeout(() => activeSocket.connect(), 100);
}
return;
}
activeSocket.disconnect();
setTimeout(() => activeSocket.connect(), 100);
};
// 🔄 Hard reset of local + server-side DMS context when the page/job loads
useEffect(() => {
@@ -285,7 +217,6 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
}, [jobId, mode, activeSocket]);
const handleExportFailed = (payload = {}) => {
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
const { title, friendlyMessage, error: errText, severity, errorCode, vendorStatusCode } = payload;
const msg =
@@ -293,7 +224,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
errText ||
t("dms.errors.exportfailedgeneric", "We couldn't complete the export. Please try again.");
const vendorTitle = title || (isRrMode ? t("dms.labels.provider_reynolds") : t("dms.labels.provider_dms"));
const vendorTitle = title || (isRrMode ? "Reynolds" : "DMS");
const isRrOpenRoLimit =
isRrMode &&
@@ -316,7 +247,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
timestamp: new Date(),
level: (sev || "error").toUpperCase(),
message: `${vendorTitle}: ${msg}`,
meta: { errorCode, vendorStatusCode, raw: safePayload, blockedByOpenRoLimit: !!isRrOpenRoLimit }
meta: { errorCode, vendorStatusCode, raw: payload, blockedByOpenRoLimit: !!isRrOpenRoLimit }
}
]);
};
@@ -368,9 +299,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
{
timestamp: new Date(),
level: "warn",
message: t("dms.labels.reconnected_export_service", {
provider: isRrMode ? t("dms.labels.provider_reynolds") : providerLabel
})
message: `Reconnected to ${isRrMode ? "RR" : mode === DMS_MAP.fortellis ? "Fortellis" : "DMS"} Export Service`
}
]);
};
@@ -389,16 +318,11 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
// Logs
const onLog = isRrMode
? (payload = {}) => {
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
const normalized = {
timestamp: safePayload.timestamp
? new Date(safePayload.timestamp)
: safePayload.ts
? new Date(safePayload.ts)
: new Date(),
level: (safePayload.level || "INFO").toUpperCase(),
message: safePayload.message || safePayload.msg || "",
meta: safePayload.meta ?? safePayload.ctx ?? safePayload.details ?? null
timestamp: payload.timestamp ? new Date(payload.timestamp) : payload.ts ? new Date(payload.ts) : new Date(),
level: (payload.level || "INFO").toUpperCase(),
message: payload.message || payload.msg || "",
meta: payload.meta ?? payload.ctx ?? payload.details ?? null
};
setLogs((prev) => [...prev, normalized]);
}
@@ -434,12 +358,14 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
{
timestamp: new Date(),
level: "INFO",
message: t("dms.labels.rr_validation_message")
message:
"Repair Order created in Reynolds. Complete validation in Reynolds, then click Finished/Close to finalize."
}
]);
notification.info({
title: t("dms.labels.rr_validation_notice_title"),
description: t("dms.labels.rr_validation_notice_description"),
title: "Reynolds RO created",
description:
"Complete validation in Reynolds, then click Finished/Close to finalize and mark this export complete.",
duration: 8
});
};
@@ -451,7 +377,8 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
{
timestamp: new Date(),
level: "INFO",
message: t("dms.labels.rr_validation_message"),
message:
"Repair Order created in Reynolds. Complete validation in Reynolds, then click Finished/Close to finalize.",
meta: { payload }
}
]);
@@ -479,19 +406,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
activeSocket.disconnect();
}
};
}, [
mode,
activeSocket,
channels,
logLevel,
notification,
t,
insertAuditTrail,
history,
isRrMode,
providerLabel,
canViewSensitiveRrXml
]);
}, [mode, activeSocket, channels, logLevel, notification, t, insertAuditTrail, history]);
// RR finalize callback (unchanged public behavior)
const handleRrValidationFinished = () => {
@@ -513,7 +428,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
// Check if Reynolds mode requires early RO
const hasEarlyRO = !!(data.jobs_by_pk?.dms_id && data.jobs_by_pk?.dms_customer_id && data.jobs_by_pk?.dms_advisor_id);
if (isRrMode && !hasEarlyRO) {
return (
<Result
@@ -534,7 +449,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
<AlertComponent style={{ marginBottom: 10 }} title={bannerMessage} type="warning" showIcon closable />
<Row gutter={[16, 16]}>
<Col xs={24} xxl={10} className="dms-equal-height-col dms-top-panel-col">
<Col md={24} lg={10} className="dms-equal-height-col">
{!isRrMode ? (
<DmsAllocationsSummary
key={resetKey}
@@ -574,7 +489,7 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
)}
</Col>
<Col xs={24} xxl={14} className="dms-equal-height-col dms-top-panel-col">
<Col md={24} lg={14} className="dms-equal-height-col">
<DmsPostForm
key={resetKey}
socket={activeSocket}
@@ -588,7 +503,6 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
</Col>
<DmsCustomerSelector
key={customerSelectorKey}
jobid={jobId}
job={data?.jobs_by_pk}
bodyshop={bodyshop}
@@ -613,32 +527,43 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
<Switch
checked={colorizeJson}
onChange={setColorizeJson}
checkedChildren={t("dms.labels.color_json")}
unCheckedChildren={t("dms.labels.plain_json")}
checkedChildren="Color JSON"
unCheckedChildren="Plain JSON"
/>
<Button onClick={toggleDetailsAll}>
{detailsOpen ? t("dms.labels.collapse_all") : t("dms.labels.expand_all")}
</Button>
<Button onClick={toggleDetailsAll}>{detailsOpen ? "Collapse All" : "Expand All"}</Button>
</>
)}
<Select
placeholder={t("dms.labels.log_level")}
placeholder="Log Level"
value={logLevel}
onChange={(value) => {
setLogLevel(value);
setActiveLogLevel(value);
}}
options={[
{ key: "SILLY", value: "SILLY", label: "SILLY" },
{ key: "DEBUG", value: "DEBUG", label: "DEBUG" },
{ key: "INFO", value: "INFO", label: "INFO" },
{ key: "WARN", value: "WARN", label: "WARN" },
{ key: "ERROR", value: "ERROR", label: "ERROR" }
]}
/>
<Button onClick={() => setLogs([])}>{t("dms.labels.clear_logs")}</Button>
<Button onClick={handleReconnectClick}> {t("dms.labels.reconnect")}</Button>
>
<Select.Option key="SILLY">SILLY</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARN">WARN</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {
setLogs([]);
setResetAfterReconnect(true);
if (isWssMode(mode)) {
setActiveLogLevel(logLevel);
}
if (activeSocket) {
activeSocket.disconnect();
setTimeout(() => activeSocket.connect(), 100);
}
}}
>
Reconnect
</Button>
</Space>
}
>
@@ -650,7 +575,6 @@ export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelecte
detailsNonce={detailsNonce}
colorizeJson={isRrMode ? colorizeJson : false}
showDetails={isRrMode}
allowXmlPayload={canViewSensitiveRrXml}
/>
</Card>
</div>

View File

@@ -1,6 +1,6 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client/react";
import { Button, Card, Checkbox, Input, Space, Typography } from "antd";
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
import _ from "lodash";
import queryString from "query-string";
import { useTranslation } from "react-i18next";
@@ -8,7 +8,6 @@ import { connect } from "react-redux";
import { Link, useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import ResponsiveTable from "../../components/responsive-table/responsive-table.component";
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { pageLimit } from "../../utils/config";
@@ -187,7 +186,7 @@ export function ExportLogsPageComponent() {
</Space>
}
>
<ResponsiveTable
<Table
loading={loading}
pagination={{
placement: "top",
@@ -196,7 +195,6 @@ export function ExportLogsPageComponent() {
total: data && data.search_exportlog_aggregate.aggregate.count
}}
columns={columns}
mobileColumnKeys={["created_at", "ro_number", "successful", "message"]}
rowKey="id"
dataSource={data?.search_exportlog}
style={{ height: "100%" }}

View File

@@ -440,15 +440,13 @@ export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, set
}
]}
>
<Select
style={{ minWidth: "12rem" }}
disabled={jobRO}
options={bodyshop.md_ins_cos.map((s) => ({
key: s.name,
value: s.name,
label: s.name
}))}
/>
<Select style={{ minWidth: "12rem" }} disabled={jobRO}>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item

View File

@@ -237,13 +237,7 @@ export function JobsDetailPage({
"rate_mapa",
"rate_mahw",
"rate_mash",
"rate_matd",
"flat_rate_ats",
"state_tax_rate",
"tax_lbr_rt",
"tax_shop_mat_rt",
"tax_paint_mat_rt",
"tax_sub_rt"
"rate_matd"
],
(meta) => meta && meta.touched
);

View File

@@ -1,6 +1,6 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client/react";
import { Button, Card, Input, Space, Typography } from "antd";
import { Button, Card, Input, Space, Table, Typography } from "antd";
import _ from "lodash";
import queryString from "query-string";
import { useTranslation } from "react-i18next";
@@ -8,7 +8,6 @@ import { connect } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import ResponsiveTable from "../../components/responsive-table/responsive-table.component";
import { QUERY_PHONEBOOK_PAGINATED } from "../../graphql/phonebook.queries";
import { selectAuthLevel, selectBodyshop } from "../../redux/user/user.selectors";
import ChatOpenButton from "../../components/chat-open-button/chat-open-button.component";
@@ -173,7 +172,7 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
</Space>
}
>
<ResponsiveTable
<Table
loading={loading}
pagination={{
placement: "top",
@@ -182,7 +181,6 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
total: data && data.search_phonebook_aggregate.aggregate.count
}}
columns={columns}
mobileColumnKeys={["firstname", "lastname", "company", "phone1", "phone2"]}
rowKey="id"
dataSource={data?.search_phonebook}
//scroll={{ x: true }}

View File

@@ -39,8 +39,10 @@ export function PhonebookContainer({ setBreadcrumbs, setSelectedHeader }) {
const navigate = useNavigate();
const screens = Grid.useBreakpoint();
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
@@ -49,15 +51,7 @@ export function PhonebookContainer({ setBreadcrumbs, setSelectedHeader }) {
xl: "50%",
xxl: "45%"
};
// Get the largest active breakpoint
let drawerPercentage = "100%";
if (screens.xxl) drawerPercentage = bpoints.xxl;
else if (screens.xl) drawerPercentage = bpoints.xl;
else if (screens.lg) drawerPercentage = bpoints.lg;
else if (screens.md) drawerPercentage = bpoints.md;
else if (screens.sm) drawerPercentage = bpoints.sm;
else if (screens.xs) drawerPercentage = bpoints.xs;
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
return (
<RbacWrapper action="phonebook:view">

View File

@@ -8,8 +8,10 @@ export default function ShopVendorPageComponent() {
const { selectedvendor } = Object.fromEntries(searchParams);
const navigate = useNavigate();
const screens = Grid.useBreakpoint();
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
@@ -18,14 +20,7 @@ export default function ShopVendorPageComponent() {
xl: "50%",
xxl: "45%"
};
let drawerPercentage = "100%";
if (screens.xxl) drawerPercentage = bpoints.xxl;
else if (screens.xl) drawerPercentage = bpoints.xl;
else if (screens.lg) drawerPercentage = bpoints.lg;
else if (screens.md) drawerPercentage = bpoints.md;
else if (screens.sm) drawerPercentage = bpoints.sm;
else if (screens.xs) drawerPercentage = bpoints.xs;
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
return (
<div>

View File

@@ -1,7 +1,6 @@
import { useQuery } from "@apollo/client/react";
import { useState } from "react";
import { Button, Card, Input, Space } from "antd";
import ResponsiveTable from "../../components/responsive-table/responsive-table.component";
import { Button, Card, Input, Space, Table } from "antd";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_JOBS_TECH_ASIGNED_TO_BY_TEAM } from "../../graphql/jobs.queries";
@@ -189,11 +188,10 @@ export function TechAssignedProdJobs({ setTimeTicketTaskContext, technician, bod
</Space>
}
>
<ResponsiveTable
<Table
loading={loading}
pagination={false}
columns={columns}
mobileColumnKeys={["ro_number", "owner", "status", "vehicle", "plate_no"]}
rowKey="id"
dataSource={jobs}
scroll={{ x: true }}

View File

@@ -1,7 +1,6 @@
import { MinusCircleTwoTone, PlusCircleTwoTone, SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client/react";
import { Button, Card, Space } from "antd";
import ResponsiveTable from "../../components/responsive-table/responsive-table.component";
import { Button, Card, Space, Table } from "antd";
import queryString from "query-string";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -109,7 +108,7 @@ export function TechDispatchedParts({ technician, bodyshop }) {
</Space>
}
>
<ResponsiveTable
<Table
loading={loading}
pagination={{
pageSize: 25,
@@ -118,7 +117,6 @@ export function TechDispatchedParts({ technician, bodyshop }) {
showSizeChanger: false
}}
columns={columns}
mobileColumnKeys={["ro_number", "status", "vehicle", "actions"]}
rowKey="id"
dataSource={parts_dispatch}
scroll={{ x: true }}