feature/IO-3725-RPS-Changes - Deprecations / Bug fixes

This commit is contained in:
Dave
2026-05-28 14:24:31 -04:00
parent 3017619565
commit ebf81778c1
23 changed files with 323 additions and 180 deletions

View File

@@ -1,5 +1,5 @@
import { ApolloProvider } from "@apollo/client";
import { ConfigProvider, theme } from "antd";
import { App as AntdApp, ConfigProvider, theme } from "antd";
import enLocale from "antd/es/locale/en_US";
import React, { useEffect } from "react";
import { connect } from "react-redux";
@@ -13,6 +13,7 @@ import "../ipc/ipc-renderer-handler";
import { checkUserSession } from "../redux/user/user.actions";
import { selectCurrentUser, selectDarkMode } from "../redux/user/user.selectors";
import { loadErrorMessages, loadDevMessages } from "@apollo/client/dev";
import { configureAntdFeedback } from "../util/antdFeedback";
import "./App.styles.scss";
const { ipcRenderer } = window;
@@ -30,6 +31,16 @@ const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession())
});
function AntdFeedbackBridge() {
const { message, notification } = AntdApp.useApp();
useEffect(() => {
configureAntdFeedback({ message, notification });
}, [message, notification]);
return null;
}
export function App({ currentUser, checkUserSession, darkMode }) {
useEffect(() => {
checkUserSession();
@@ -62,7 +73,10 @@ export function App({ currentUser, checkUserSession, darkMode }) {
input={{ autoComplete: "new-password" }}
locale={enLocale}
>
<div>{currentUser.authorized ? <RoutesPage /> : <SignInPage />}</div>
<AntdApp>
<AntdFeedbackBridge />
<div>{currentUser.authorized ? <RoutesPage /> : <SignInPage />}</div>
</AntdApp>
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -1,6 +1,7 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, message, Popconfirm } from "antd";
import { Button, Popconfirm } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,5 +1,6 @@
import { useMutation } from "@apollo/client";
import { message, Switch } from "antd";
import { Switch } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import React, { useState } from "react";
import { UPDATE_JOB_LINE } from "../../../graphql/joblines.queries";
import ipcTypes from "../../../ipc.types";

View File

@@ -1,6 +1,7 @@
import { WarningOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { DatePicker, message, notification, Space, Spin } from "antd";
import { DatePicker, Space, Spin } from "antd";
import { antdMessage as message, antdNotification as notification } from "../../../util/antdFeedback";
import React, { useState } from "react";
import { UPDATE_JOB } from "../../../graphql/jobs.queries";
import ipcTypes from "../../../ipc.types";

View File

@@ -1,5 +1,6 @@
import { useApolloClient } from "@apollo/client";
import { Button, message, Tooltip } from "antd";
import { Button, Tooltip } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -18,7 +18,6 @@ const mapDispatchToProps = (dispatch) => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(EstimateScrubberResults);
const { Panel } = Collapse;
const { Title, Text, Link } = Typography;
export function EstimateScrubberResults({ bodyshop, jobid, job, esResults }) {
@@ -26,8 +25,15 @@ export function EstimateScrubberResults({ bodyshop, jobid, job, esResults }) {
const buttonDisabled = job?.g_bett_amt == null;
// Filter items based on search text
const filteredItems = esResults?.items
? esResults.items.filter((item) => {
const itemsWithKeys = esResults?.items
? esResults.items.map((item, itemIndex) => ({
...item,
scrubberRowKey: [item.SubCategory, item.L, item.R, item.Anchor, itemIndex].filter(Boolean).join("|")
}))
: [];
const filteredItems = itemsWithKeys.length
? itemsWithKeys.filter((item) => {
if (!searchText.trim()) return true;
const searchLower = searchText.toLowerCase();
return (
@@ -89,6 +95,38 @@ export function EstimateScrubberResults({ bodyshop, jobid, job, esResults }) {
}
];
const collapseItems = sortedCategories.map((category) => {
const items = groupedItems[category];
const config = categoryConfig[category] || { color: "default", icon: "📄" };
return {
key: category,
label: (
<Space>
{/* <span style={{ fontSize: "18px" }}>{config.icon}</span> */}
<Text strong>{category}</Text>
<Badge
count={items.length}
style={{
backgroundColor: config.color === "blue" ? "#1890ff" : config.color === "orange" ? "#fa8c16" : "#52c41a"
}}
/>
</Space>
),
children: (
<Table
dataSource={items}
columns={columns}
pagination={false}
size="middle"
rowKey="scrubberRowKey"
style={{ marginTop: "12px" }}
scroll={{}}
/>
)
};
});
if (!esResults?.items?.length || job?.id !== esResults?.jobid) {
return (
<Result
@@ -189,41 +227,7 @@ export function EstimateScrubberResults({ bodyshop, jobid, job, esResults }) {
})}
</Space>
<Collapse defaultActiveKey={sortedCategories} expandIconPosition="right" size="large">
{sortedCategories.map((category) => {
const items = groupedItems[category];
const config = categoryConfig[category] || { color: "default", icon: "📄" };
return (
<Panel
header={
<Space>
{/* <span style={{ fontSize: "18px" }}>{config.icon}</span> */}
<Text strong>{category}</Text>
<Badge
count={items.length}
style={{
backgroundColor:
config.color === "blue" ? "#1890ff" : config.color === "orange" ? "#fa8c16" : "#52c41a"
}}
/>
</Space>
}
key={category}
>
<Table
dataSource={items}
columns={columns}
pagination={false}
size="middle"
rowKey={(record, index) => `${category}-${index}`}
style={{ marginTop: "12px" }}
scroll={{}}
/>
</Panel>
);
})}
</Collapse>
<Collapse defaultActiveKey={sortedCategories} expandIconPosition="end" size="large" items={collapseItems} />
</Space>
)}
</div>

View File

@@ -1,6 +1,7 @@
import { CheckOutlined, CloseOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { message, Switch } from "antd";
import { Switch } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,12 +1,13 @@
import { AlertFilled, DownOutlined, LoadingOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, Menu, message, Space, Tooltip } from "antd";
import { Dropdown, Space, Tooltip } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB } from "../../../graphql/jobs.queries";
import ipcTypes from "../../../ipc.types";
import { selectBodyshop } from "../../../redux/user/user.selectors";
import { antdMessage as message } from "../../../util/antdFeedback";
import GroupVerifySwitch from "../group-verify-switch/group-verify-switch.component";
import JobsGroupModalMolecule from "../jobs-group-modal/jobs-group-modal.molecule";
import { WhichMPIRulesetToApply } from "../../../util/constants";
@@ -48,41 +49,38 @@ export function JobGroupMolecule({ bodyshop, jobId, group, job }) {
const MPIRulesetToApply = WhichMPIRulesetToApply(job.close_date);
const MPImenu =
MPIRulesetToApply === "V3" ? (
<Menu
onClick={handleMenuClick}
//disabled
items={[
{ label: "Group 1", key: "Group 1", value: "Group 1" },
{ label: "Group 2", key: "Group 2", value: "Group 2" },
{ label: "Group 3", key: "Group 3", value: "Group 3" },
{ label: "Group 4", key: "Group 4", value: "Group 4" },
{ label: "Group 5", key: "Group 5", value: "Group 5" },
{ label: "Group 6", key: "Group 6", value: "Group 6" },
{ label: "Group 7", key: "Group 7", value: "Group 7" },
{ label: "Group 8", key: "Group 8", value: "Group 8" },
{ label: "Group 9", key: "Group 9", value: "Group 9" },
{ label: "Group 10", key: "Group 10", value: "Group 10" },
{ label: "Group 11", key: "Group 11", value: "Group 11" },
{ label: "Group 12", key: "Group 12", value: "Group 12" },
{ label: "Default", key: "Default", value: "Default" }
]}
></Menu>
) : (
<Menu onClick={handleMenuClick} items={bodyshop.groups.map((g, idx) => ({ key: g, title: g, label: g }))}></Menu>
);
const SGImenu = (
<Menu
onClick={handleMenuClick}
//disabled
items={[{ label: "Default", key: "Default", value: "Default" }]}
></Menu>
);
MPIRulesetToApply === "V3"
? {
onClick: handleMenuClick,
items: [
{ label: "Group 1", key: "Group 1", value: "Group 1" },
{ label: "Group 2", key: "Group 2", value: "Group 2" },
{ label: "Group 3", key: "Group 3", value: "Group 3" },
{ label: "Group 4", key: "Group 4", value: "Group 4" },
{ label: "Group 5", key: "Group 5", value: "Group 5" },
{ label: "Group 6", key: "Group 6", value: "Group 6" },
{ label: "Group 7", key: "Group 7", value: "Group 7" },
{ label: "Group 8", key: "Group 8", value: "Group 8" },
{ label: "Group 9", key: "Group 9", value: "Group 9" },
{ label: "Group 10", key: "Group 10", value: "Group 10" },
{ label: "Group 11", key: "Group 11", value: "Group 11" },
{ label: "Group 12", key: "Group 12", value: "Group 12" },
{ label: "Default", key: "Default", value: "Default" }
]
}
: {
onClick: handleMenuClick,
items: bodyshop.groups.map((g) => ({ key: g, title: g, label: g }))
};
const SGImenu = {
onClick: handleMenuClick,
items: [{ label: "Default", key: "Default", value: "Default" }]
};
const menuToUse = bodyshop.ins_rule_set === "MPI" ? MPImenu : SGImenu;
return (
<Space align="center" wrap>
<Dropdown overlay={menuToUse} trigger={["click"]}>
<Dropdown menu={menuToUse} trigger={["click"]}>
<a href=" #" onClick={(e) => e.preventDefault()}>
{group}
<DownOutlined style={{ marginLeft: ".2rem" }} />

View File

@@ -5,7 +5,6 @@ import { createStructuredSelector } from "reselect";
import { queryReportingData } from "../../../redux/reporting/reporting.actions";
import dayjs from "../../../util/day.js";
import ipcTypes from "../../../ipc.types.js";
import { range } from "lodash";
const { ipcRenderer } = window;
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -15,8 +14,49 @@ const mapDispatchToProps = (dispatch) => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(ReportingDatesMolecule);
const getReportingDatePresets = () => [
{
label: "Last Month",
value: [
dayjs().startOf("month").subtract(1, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
]
},
{ label: "This Month", value: [dayjs().startOf("month"), dayjs().endOf("month")] },
{
label: "Next Month",
value: [
dayjs().startOf("month").add(1, "month"),
dayjs().startOf("month").add(1, "month").endOf("month")
]
},
{
label: "Last Quarter",
value: [
dayjs().startOf("quarter").subtract(1, "quarter"),
dayjs().startOf("quarter").subtract(1, "day")
]
},
{
label: "This Quarter",
value: [dayjs().startOf("quarter"), dayjs().startOf("quarter").add(1, "quarter").subtract(1, "day")]
},
{
label: "Last 3 Months (Exlcusive)",
value: [
dayjs().startOf("month").subtract(3, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
]
},
{
label: "Last 3 Months (Inclusive)",
value: [dayjs().startOf("month").subtract(2, "month"), dayjs().endOf("month")]
}
];
export function ReportingDatesMolecule({ queryReportingData }) {
const [form] = Form.useForm();
const reportingDatePresets = getReportingDatePresets();
const handleFinish = (values) => {
queryReportingData({
@@ -49,34 +89,7 @@ export function ReportingDatesMolecule({ queryReportingData }) {
}
]}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
ranges={{
"Last Month": [
dayjs().startOf("month").subtract(1, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
],
"This Month": [dayjs().startOf("month"), dayjs().endOf("month")],
"Next Month": [
dayjs().startOf("month").add(1, "month"),
dayjs().startOf("month").add(1, "month").endOf("month")
],
"Last Quarter": [
dayjs().startOf("quarter").subtract(1, "quarter"),
dayjs().startOf("quarter").subtract(1, "day")
],
"This Quarter": [
dayjs().startOf("quarter"),
dayjs().startOf("quarter").add(1, "quarter").subtract(1, "day")
],
"Last 3 Months (Exlcusive)": [
dayjs().startOf("month").subtract(3, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
],
"Last 3 Months (Inclusive)": [dayjs().startOf("month").subtract(2, "month"), dayjs().endOf("month")]
}}
/>
<DatePicker.RangePicker format="MM/DD/YYYY" presets={reportingDatePresets} />
</Form.Item>
<Button type="primary" htmlType="submit">
Run Search

View File

@@ -1,4 +1,5 @@
import { Button, Input, message, Table } from "antd";
import { Button, Input, Table } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,6 +1,7 @@
import { CheckOutlined, CloseOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { message, Switch } from "antd";
import { Switch } from "antd";
import { antdMessage as message } from "../../../util/antdFeedback";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";

View File

@@ -1,6 +1,6 @@
import { useQuery } from "@apollo/client";
import { Badge, Button, Card, Result } from "antd";
import { useEffect, useRef } from "react";
import { Badge, Button, Card, Result, Skeleton } from "antd";
import { useEffect, useRef, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries";
@@ -16,6 +16,8 @@ import JobsTargetsStatsMolecule from "../../molecules/jobs-targets-stats/jobs-ta
import ipcTypes from "../../../ipc.types";
import "./jobs-detail.organism.styles.scss";
const JOB_SELECTION_DEBOUNCE_MS = 120;
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
selectedJobId: selectSelectedJobId,
@@ -26,23 +28,58 @@ const mapDispatchToProps = (dispatch) => ({
setSelectedJobTargetPc: (job) => dispatch(setSelectedJobTargetPc(job))
});
export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTargetPc }) {
const { loading, error, data } = useQuery(QUERY_JOB_BY_PK, {
variables: { jobId: selectedJobId },
skip: !selectedJobId
});
const jobDetailRef = useRef();
function useDebouncedSelectedJobId(selectedJobId) {
const [debouncedSelectedJobId, setDebouncedSelectedJobId] = useState(selectedJobId);
useEffect(() => {
if (data && data.jobs_by_pk)
if (!selectedJobId) {
setDebouncedSelectedJobId(selectedJobId);
return undefined;
}
const timeoutId = setTimeout(() => {
setDebouncedSelectedJobId(selectedJobId);
}, JOB_SELECTION_DEBOUNCE_MS);
return () => clearTimeout(timeoutId);
}, [selectedJobId]);
return debouncedSelectedJobId;
}
function JobDetailSkeleton() {
return (
<div className="jobs-detail-container">
<Card>
<Skeleton active paragraph={{ rows: 4 }} />
</Card>
</div>
);
}
export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTargetPc }) {
const debouncedSelectedJobId = useDebouncedSelectedJobId(selectedJobId);
const { loading, error, data } = useQuery(QUERY_JOB_BY_PK, {
variables: { jobId: debouncedSelectedJobId },
skip: !debouncedSelectedJobId
});
const jobDetailRef = useRef();
const job = data?.jobs_by_pk;
const isSelectionPending = selectedJobId !== debouncedSelectedJobId;
const hasSelectedJobData = job?.id === debouncedSelectedJobId;
const isMissingSelectedJob = !isSelectionPending && !loading && debouncedSelectedJobId && !job;
const isJobDetailLoading = isSelectionPending || loading || (job && !hasSelectedJobData);
useEffect(() => {
if (hasSelectedJobData)
setSelectedJobTargetPc({
group: data.jobs_by_pk && data.jobs_by_pk.group,
v_age: data.jobs_by_pk && data.jobs_by_pk.v_age,
close_date: data.jobs_by_pk && data.jobs_by_pk.close_date,
v_mileage: data.jobs_by_pk && data.jobs_by_pk.v_mileage,
job: data.jobs_by_pk
group: job.group,
v_age: job.v_age,
close_date: job.close_date,
v_mileage: job.v_mileage,
job
});
}, [data, setSelectedJobTargetPc]);
}, [hasSelectedJobData, job, setSelectedJobTargetPc]);
const isSgi = bodyshop?.ins_rule_set === "SGI";
if (!selectedJobId)
return (
@@ -57,7 +94,10 @@ export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTarg
<Result title="No job selected." />
</div>
);
if (isJobDetailLoading) return <JobDetailSkeleton />;
if (error) return <ErrorResultAtom title="Error fetching Job details.." errorMessage={JSON.stringify(error)} />;
if (isMissingSelectedJob)
return <ErrorResultAtom title="Error displaying job details." errorMessage="It looks like this job doesn't exist." />;
return (
<div ref={jobDetailRef} className="jobs-detail-container">
@@ -70,13 +110,13 @@ export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTarg
</style>
<Card>
<JobsDetailDescriptionMolecule
loading={loading}
job={data ? data.jobs_by_pk : null}
loading={false}
job={job}
jobDetailRef={jobDetailRef}
/>
</Card>
<Card title="Job Targets">
<JobsTargetsStatsMolecule loading={loading} job={data ? data.jobs_by_pk : null} />
<JobsTargetsStatsMolecule loading={false} job={job} />
</Card>
{/* <JobsClaimClerk loading={loading} job={data ? data.jobs_by_pk : null} />
@@ -84,12 +124,12 @@ export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTarg
*/}
<Card title="Estimate Lines">
<JobsLinesTableMolecule loading={loading} job={data ? data.jobs_by_pk : {}} />
<JobsLinesTableMolecule loading={false} job={job} />
</Card>
{bodyshop.es_api_key ? (
<Badge.Ribbon text="BETA" color="red">
<Card id="es-results-card" title="Estimate Scrubber Results" extra={[]}>
<EstimateScrubberResultsMolecule loading={loading} job={data ? data.jobs_by_pk : {}} />
<EstimateScrubberResultsMolecule loading={false} job={job} />
</Card>
</Badge.Ribbon>
) : (
@@ -127,8 +167,8 @@ export function JobsDetailOrganism({ bodyshop, selectedJobId, setSelectedJobTarg
width: "100%"
}}
>
<JobsPartsGraphAtom job={data ? data.jobs_by_pk : null} loading={loading} price="db_price" />
<JobsPartsGraphAtom job={data ? data.jobs_by_pk : null} loading={loading} />
<JobsPartsGraphAtom job={job} loading={false} price="db_price" />
<JobsPartsGraphAtom job={job} loading={false} />
</div>
</Card>
</div>

View File

@@ -27,19 +27,23 @@ export default function JobsTableOrganism() {
},
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) {
const previousJobs = prev?.jobs || [];
const incomingJobs = fetchMoreResult?.jobs || [];
if (!incomingJobs.length) {
console.log("No more results. Fetch More was empty.");
setState({ ...state, hasMore: false });
setState((current) => ({ ...current, hasMore: false }));
return prev;
}
const newCache = Object.assign({}, prev, {
jobs: [...prev.jobs, ...fetchMoreResult.jobs]
jobs: [...previousJobs, ...incomingJobs],
jobs_aggregate: fetchMoreResult.jobs_aggregate || prev?.jobs_aggregate
});
if (newCache.jobs.length >= (data && data.jobs_aggregate.aggregate.count)) {
console.log("No more results.");
setState({ ...state, hasMore: false });
setState((current) => ({ ...current, hasMore: false }));
}
return newCache;
@@ -56,7 +60,14 @@ export default function JobsTableOrganism() {
return (
<div className="jobs-list-container">
<Button onClick={() => refetch()} style={{ marginBottom: ".5rem" }} size="large">
<Button
onClick={() => {
setState({ hasMore: true });
refetch();
}}
style={{ marginBottom: ".5rem" }}
size="large"
>
Refresh
</Button>
<div className="jobs-list-infinite-container">

View File

@@ -9,7 +9,7 @@ import JobsSearchFieldsMolecule from "../../molecules/jobs-search-fields/jobs-se
const limit = 50;
export default function JobsTableOrganism() {
const [state, setState] = useState({ hasMore: true });
const [state, setState] = useState({ hasMore: false });
const [callSearch, { loading, error, data, fetchMore }] = useLazyQuery(
SEARCH_JOBS_PAGINATED,
@@ -21,8 +21,13 @@ export default function JobsTableOrganism() {
}
);
const handleSearch = (options) => {
setState({ hasMore: true });
callSearch(options);
};
const handleInfiniteOnLoad = async (page) => {
if (fetchMore) {
if (fetchMore && data?.search_jobs) {
// ipcRenderer.send(ipcTypes.app.toMain.track, {
// event: "FETCH_MORE_JOBS",
// });
@@ -31,14 +36,18 @@ export default function JobsTableOrganism() {
offset: limit * page,
},
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult) {
const previousJobs = prev?.search_jobs || [];
const incomingJobs = fetchMoreResult?.search_jobs || [];
if (!incomingJobs.length) {
console.log("No more results. Fetch More was empty.");
setState({ ...state, hasMore: false });
setState((current) => ({ ...current, hasMore: false }));
return prev;
}
const newCache = Object.assign({}, prev, {
search_jobs: [...prev.search_jobs, ...fetchMoreResult.search_jobs],
search_jobs: [...previousJobs, ...incomingJobs],
search_jobs_aggregate: fetchMoreResult.search_jobs_aggregate || prev?.search_jobs_aggregate
});
if (
@@ -46,7 +55,7 @@ export default function JobsTableOrganism() {
(data && data.search_jobs_aggregate.aggregate.count)
) {
console.log("No more results.");
setState({ ...state, hasMore: false });
setState((current) => ({ ...current, hasMore: false }));
}
return newCache;
@@ -65,7 +74,7 @@ export default function JobsTableOrganism() {
return (
<div className="jobs-list-container">
<JobsSearchFieldsMolecule callSearchQuery={callSearch} />
<JobsSearchFieldsMolecule callSearchQuery={handleSearch} />
<div className="jobs-list-infinite-container">
<InfiniteScroll
pageStart={0}

View File

@@ -1,5 +1,5 @@
import { useMutation, useQuery } from "@apollo/client";
import { Form, notification, Skeleton } from "antd";
import { Form, Skeleton } from "antd";
import React, { useEffect, useState, useCallback } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -7,6 +7,7 @@ import { debounce } from "lodash";
import { QUERY_BODYSHOP, UPDATE_SHOP } from "../../../graphql/bodyshop.queries";
import ipcTypes from "../../../ipc.types";
import { setBodyshop } from "../../../redux/user/user.actions";
import { antdNotification as notification } from "../../../util/antdFeedback";
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
import ShopSettingsFormMolecule from "../../molecules/shop-settings-form/shop-settings-form.molecule";
const { ipcRenderer } = window;

View File

@@ -17,6 +17,38 @@ import { setSelectedJobId } from "../../../redux/application/application.actions
const { ipcRenderer } = window;
const getAuditDatePresets = () => [
{
label: "2 Months ago",
value: [
dayjs().startOf("month").subtract(2, "month"),
dayjs().startOf("month").subtract(2, "month").endOf("month")
]
},
{
label: "Last Month",
value: [
dayjs().startOf("month").subtract(1, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
]
},
{ label: "This Month", value: [dayjs().startOf("month"), dayjs().endOf("month")] },
{
label: "Last Quarter",
value: [
dayjs().startOf("quarter").subtract(1, "quarter"),
dayjs().startOf("quarter").subtract(1, "day")
]
},
{
label: "Last 3 Months",
value: [
dayjs().startOf("month").subtract(3, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
]
}
];
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -31,6 +63,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(AuditPage);
export function AuditPage({ auditError, queryReportingData, bodyshop, reportingError, setSelectedJobId }) {
const [form] = Form.useForm();
const auditDatePresets = getAuditDatePresets();
const [sheets, setSheets] = useState([]);
useEffect(() => {
ipcRenderer.on(ipcTypes.audit.toRenderer.auditFilePath, async (event, { filePath, sheets }) => {
@@ -104,28 +137,7 @@ export function AuditPage({ auditError, queryReportingData, bodyshop, reportingE
}
]}
>
<DatePicker.RangePicker
format="MM/DD/YYYY"
ranges={{
"2 Months ago": [
dayjs().startOf("month").subtract(2, "month"),
dayjs().startOf("month").subtract(2, "month").endOf("month")
],
"Last Month": [
dayjs().startOf("month").subtract(1, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
],
"This Month": [dayjs().startOf("month"), dayjs().endOf("month")],
"Last Quarter": [
dayjs().startOf("quarter").subtract(1, "quarter"),
dayjs().startOf("quarter").subtract(1, "day")
],
"Last 3 Months": [
dayjs().startOf("month").subtract(3, "month"),
dayjs().startOf("month").subtract(1, "month").endOf("month")
]
}}
/>
<DatePicker.RangePicker format="MM/DD/YYYY" presets={auditDatePresets} />
</Form.Item>
<Space align="middle" wrap>

View File

@@ -1,4 +1,4 @@
import { Alert, Layout, notification } from "antd";
import { Alert, Layout } from "antd";
import React from "react";
import { connect } from "react-redux";
import { Routes } from "react-router-dom";

View File

@@ -26,6 +26,21 @@ class ErrorBoundary extends React.Component {
render() {
if (this.state.hasErrored === true) {
const errorItems = [
{
key: "error-details",
label: "Error Details",
children: (
<>
<div>
<strong>{this.state.error.message}</strong>
</div>
<div>{this.state.error.stack}</div>
</>
)
}
];
return (
<div>
<Result
@@ -47,14 +62,7 @@ class ErrorBoundary extends React.Component {
/>
<Row>
<Col offset={6} span={12}>
<Collapse bordered={false}>
<Collapse.Panel header="Error Details">
<div>
<strong>{this.state.error.message}</strong>
</div>
<div>{this.state.error.stack}</div>
</Collapse.Panel>
</Collapse>
<Collapse bordered={false} items={errorItems} />
</Col>
</Row>
</div>

View File

@@ -104,7 +104,33 @@ if (import.meta.env.DEV) {
middlewares.push(sentryLink.concat(retryLink.concat(errorLink.concat(authLink.concat(httpLink)))));
const cache = new InMemoryCache({});
const mergeByOffset = (existing = [], incoming = [], { args }) => {
const merged = existing ? existing.slice(0) : [];
const offset = args?.offset || 0;
incoming.forEach((item, index) => {
merged[offset + index] = item;
});
return merged;
};
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
jobs: {
keyArgs: ["order"],
merge: mergeByOffset
},
search_jobs: {
keyArgs: ["args", "where"],
merge: mergeByOffset
}
}
}
}
});
export default new ApolloClient({
link: ApolloLink.from(middlewares),

View File

@@ -1,4 +1,4 @@
import { message } from "antd";
import { antdMessage as message } from "../util/antdFeedback";
import gql from "graphql-tag";
import _ from "lodash";
import client from "../graphql/GraphQLClient";
@@ -8500,4 +8500,4 @@ export const SgiGroupsV12026April = [
"ageDesc": "Over 7 years",
"name": "SGIV1"
}
]
]

View File

@@ -1,4 +1,4 @@
import { notification } from "antd";
import { antdNotification as notification } from "../util/antdFeedback";
import ipcTypes from "../ipc.types";
import {
setReleaseNotes,

View File

@@ -1,4 +1,4 @@
import { createStore, applyMiddleware, compose } from "redux";
import { legacy_createStore as createStore, applyMiddleware, compose } from "redux";
import { persistStore } from "redux-persist";
import { createLogger } from "redux-logger";
import createSagaMiddleware from "redux-saga";

View File

@@ -1,4 +1,4 @@
import { message } from "antd";
import { antdMessage as message } from "../../util/antdFeedback";
import dayjs from "../../util/day.js";
//import LogRocket from "logrocket";
import * as Sentry from "@sentry/electron/renderer";