From ebf81778c1c18099cb11f4ef8fe4990c5a70f0e2 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 28 May 2026 14:24:31 -0400 Subject: [PATCH] feature/IO-3725-RPS-Changes - Deprecations / Bug fixes --- src/App/App.jsx | 18 +++- .../atoms/delete-job/delete-job.atom.jsx | 3 +- .../ignore-job-line/ignore-job-line.atom.jsx | 3 +- .../close-date-display.molecule.jsx | 3 +- .../estimate-scrubber-button.molecule.jsx | 3 +- .../estimate-scrubber-results.molecule.jsx | 80 +++++++++--------- .../group-verify-switch.component.jsx | 3 +- .../job-group/job-group.molecule.jsx | 62 +++++++------- .../reporting-dates.molecule.jsx | 71 +++++++++------- .../scan-estimate-list.molecule.jsx | 3 +- .../sgi-rcc-switch.component.jsx | 3 +- .../jobs-detail/jobs-detail.organism.jsx | 84 ++++++++++++++----- .../jobs-list-latest.organism.jsx | 21 +++-- .../jobs-list-search.organism.jsx | 23 +++-- .../shop-settings/shop-settings.organism.jsx | 3 +- src/components/pages/audit/audit.page.jsx | 56 ++++++++----- src/components/pages/routes/routes.page.jsx | 2 +- .../templates/error-boundary.template.jsx | 24 ++++-- src/graphql/GraphQLClient.js | 28 ++++++- src/ipc/ipc-estimate-utils.js | 4 +- src/ipc/ipc-renderer-handler.js | 2 +- src/redux/store.js | 2 +- src/redux/user/user.sagas.js | 2 +- 23 files changed, 323 insertions(+), 180 deletions(-) diff --git a/src/App/App.jsx b/src/App/App.jsx index 6a4988e..53e7531 100644 --- a/src/App/App.jsx +++ b/src/App/App.jsx @@ -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} > -
{currentUser.authorized ? : }
+ + +
{currentUser.authorized ? : }
+
); diff --git a/src/components/atoms/delete-job/delete-job.atom.jsx b/src/components/atoms/delete-job/delete-job.atom.jsx index a1ffc70..39b8a85 100644 --- a/src/components/atoms/delete-job/delete-job.atom.jsx +++ b/src/components/atoms/delete-job/delete-job.atom.jsx @@ -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"; diff --git a/src/components/atoms/ignore-job-line/ignore-job-line.atom.jsx b/src/components/atoms/ignore-job-line/ignore-job-line.atom.jsx index 69fe674..0057943 100644 --- a/src/components/atoms/ignore-job-line/ignore-job-line.atom.jsx +++ b/src/components/atoms/ignore-job-line/ignore-job-line.atom.jsx @@ -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"; diff --git a/src/components/molecules/close-date-display/close-date-display.molecule.jsx b/src/components/molecules/close-date-display/close-date-display.molecule.jsx index bad239c..32816a4 100644 --- a/src/components/molecules/close-date-display/close-date-display.molecule.jsx +++ b/src/components/molecules/close-date-display/close-date-display.molecule.jsx @@ -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"; diff --git a/src/components/molecules/estimate-scrubber-button/estimate-scrubber-button.molecule.jsx b/src/components/molecules/estimate-scrubber-button/estimate-scrubber-button.molecule.jsx index d2a585c..12187b6 100644 --- a/src/components/molecules/estimate-scrubber-button/estimate-scrubber-button.molecule.jsx +++ b/src/components/molecules/estimate-scrubber-button/estimate-scrubber-button.molecule.jsx @@ -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"; diff --git a/src/components/molecules/estimate-scruber-results/estimate-scrubber-results.molecule.jsx b/src/components/molecules/estimate-scruber-results/estimate-scrubber-results.molecule.jsx index 68bd884..bceff62 100644 --- a/src/components/molecules/estimate-scruber-results/estimate-scrubber-results.molecule.jsx +++ b/src/components/molecules/estimate-scruber-results/estimate-scrubber-results.molecule.jsx @@ -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: ( + + {/* {config.icon} */} + {category} + + + ), + children: ( + + ) + }; + }); + if (!esResults?.items?.length || job?.id !== esResults?.jobid) { return ( - - {sortedCategories.map((category) => { - const items = groupedItems[category]; - const config = categoryConfig[category] || { color: "default", icon: "📄" }; - - return ( - - {/* {config.icon} */} - {category} - - - } - key={category} - > -
`${category}-${index}`} - style={{ marginTop: "12px" }} - scroll={{}} - /> - - ); - })} - + )} diff --git a/src/components/molecules/group-verify-switch/group-verify-switch.component.jsx b/src/components/molecules/group-verify-switch/group-verify-switch.component.jsx index 130a251..008447b 100644 --- a/src/components/molecules/group-verify-switch/group-verify-switch.component.jsx +++ b/src/components/molecules/group-verify-switch/group-verify-switch.component.jsx @@ -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"; diff --git a/src/components/molecules/job-group/job-group.molecule.jsx b/src/components/molecules/job-group/job-group.molecule.jsx index 7ca78b1..7aa8b30 100644 --- a/src/components/molecules/job-group/job-group.molecule.jsx +++ b/src/components/molecules/job-group/job-group.molecule.jsx @@ -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" ? ( - - ) : ( - ({ key: g, title: g, label: g }))}> - ); - const SGImenu = ( - - ); + 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 ( - + e.preventDefault()}> {group} diff --git a/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx b/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx index 647dcb8..d1bc183 100644 --- a/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx +++ b/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx @@ -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 }) { } ]} > - +
diff --git a/src/components/organisms/jobs-list-search/jobs-list-search.organism.jsx b/src/components/organisms/jobs-list-search/jobs-list-search.organism.jsx index cea8ddf..f3ccb24 100644 --- a/src/components/organisms/jobs-list-search/jobs-list-search.organism.jsx +++ b/src/components/organisms/jobs-list-search/jobs-list-search.organism.jsx @@ -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 (
- +
[ + { + 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 } ]} > - + diff --git a/src/components/pages/routes/routes.page.jsx b/src/components/pages/routes/routes.page.jsx index db8ec22..31ac033 100644 --- a/src/components/pages/routes/routes.page.jsx +++ b/src/components/pages/routes/routes.page.jsx @@ -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"; diff --git a/src/components/templates/error-boundary.template.jsx b/src/components/templates/error-boundary.template.jsx index 58f5224..a29f38b 100644 --- a/src/components/templates/error-boundary.template.jsx +++ b/src/components/templates/error-boundary.template.jsx @@ -26,6 +26,21 @@ class ErrorBoundary extends React.Component { render() { if (this.state.hasErrored === true) { + const errorItems = [ + { + key: "error-details", + label: "Error Details", + children: ( + <> +
+ {this.state.error.message} +
+
{this.state.error.stack}
+ + ) + } + ]; + return (
- - -
- {this.state.error.message} -
-
{this.state.error.stack}
-
-
+ diff --git a/src/graphql/GraphQLClient.js b/src/graphql/GraphQLClient.js index b454075..990e998 100644 --- a/src/graphql/GraphQLClient.js +++ b/src/graphql/GraphQLClient.js @@ -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), diff --git a/src/ipc/ipc-estimate-utils.js b/src/ipc/ipc-estimate-utils.js index 0edcd22..22408f4 100644 --- a/src/ipc/ipc-estimate-utils.js +++ b/src/ipc/ipc-estimate-utils.js @@ -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" } -] \ No newline at end of file +] diff --git a/src/ipc/ipc-renderer-handler.js b/src/ipc/ipc-renderer-handler.js index 2343954..d70ada6 100644 --- a/src/ipc/ipc-renderer-handler.js +++ b/src/ipc/ipc-renderer-handler.js @@ -1,4 +1,4 @@ -import { notification } from "antd"; +import { antdNotification as notification } from "../util/antdFeedback"; import ipcTypes from "../ipc.types"; import { setReleaseNotes, diff --git a/src/redux/store.js b/src/redux/store.js index aece08e..4801ead 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -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"; diff --git a/src/redux/user/user.sagas.js b/src/redux/user/user.sagas.js index 66e57de..b5da5ab 100644 --- a/src/redux/user/user.sagas.js +++ b/src/redux/user/user.sagas.js @@ -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";