diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js index ed9a0b2..42550fe 100644 --- a/electron/decoder/decoder.js +++ b/electron/decoder/decoder.js @@ -255,7 +255,7 @@ async function DecodeAd2File(extensionlessFilePath) { } } -async function DecodeVehFile(extensionlessFilePath) { +async function DecodePfhFile(extensionlessFilePath) { let dbf; try { dbf = await DBFFile.open(`${extensionlessFilePath}.PFH`); @@ -270,7 +270,7 @@ async function DecodeVehFile(extensionlessFilePath) { } } -async function DecodePfhFile(extensionlessFilePath) { +async function DecodeVehFile(extensionlessFilePath) { let dbf; try { dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`); @@ -282,8 +282,8 @@ async function DecodePfhFile(extensionlessFilePath) { if (!dbf) return {}; let records = await dbf.readRecords(1); return _.pick(records[0], [ - // "IMPACT_1", - // "IMPACT_2", + "IMPACT_1", + "IMPACT_2", // "DB_V_CODE", // "PLATE_NO", // "PLATE_ST", diff --git a/electron/estimate-scrubber/estimate-scrubber.js b/electron/estimate-scrubber/estimate-scrubber.js index 5c028bf..809fb94 100644 --- a/electron/estimate-scrubber/estimate-scrubber.js +++ b/electron/estimate-scrubber/estimate-scrubber.js @@ -82,7 +82,7 @@ async function ScrubEstimate({ job }) { return lowercasedTotal; }); } - console.log("*** ~ ScrubEstimate ~ job:", job); + const fileName = `RPSTest-${job.id}-${Date.now()}`; // Write job object to logs subfolder @@ -104,7 +104,6 @@ async function ScrubEstimate({ job }) { }, headers: formData.getHeaders ? formData.getHeaders() : {} }); - console.log("*** ~ ScrubEstimate ~ result:", result); const resultPDFUrl = result?.data?.report_link diff --git a/hasura/metadata/databases/default/tables/public_jobs.yaml b/hasura/metadata/databases/default/tables/public_jobs.yaml index c3e70d1..fc1d2b4 100644 --- a/hasura/metadata/databases/default/tables/public_jobs.yaml +++ b/hasura/metadata/databases/default/tables/public_jobs.yaml @@ -34,6 +34,8 @@ insert_permissions: - group_verified - id - id_pro_nam + - impact_1 + - impact_2 - ins_co_nm - loss_date - loss_desc @@ -71,6 +73,8 @@ select_permissions: - group_verified - id - id_pro_nam + - impact_1 + - impact_2 - ins_co_nm - loss_date - loss_desc @@ -115,6 +119,8 @@ update_permissions: - group_verified - id - id_pro_nam + - impact_1 + - impact_2 - ins_co_nm - loss_date - loss_desc diff --git a/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/down.sql b/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/down.sql new file mode 100644 index 0000000..967401b --- /dev/null +++ b/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."jobs" add column "impact1" text +-- null; diff --git a/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/up.sql b/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/up.sql new file mode 100644 index 0000000..e00cead --- /dev/null +++ b/hasura/migrations/default/1756242784957_alter_table_public_jobs_add_column_impact1/up.sql @@ -0,0 +1,2 @@ +alter table "public"."jobs" add column "impact1" text + null; diff --git a/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/down.sql b/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/down.sql new file mode 100644 index 0000000..e29283f --- /dev/null +++ b/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."jobs" add column "impact2" text +-- null; diff --git a/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/up.sql b/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/up.sql new file mode 100644 index 0000000..a636a18 --- /dev/null +++ b/hasura/migrations/default/1756242793309_alter_table_public_jobs_add_column_impact2/up.sql @@ -0,0 +1,2 @@ +alter table "public"."jobs" add column "impact2" text + null; diff --git a/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/down.sql b/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/down.sql new file mode 100644 index 0000000..64cb235 --- /dev/null +++ b/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/down.sql @@ -0,0 +1 @@ +alter table "public"."jobs" rename column "impact_1" to "impact1"; diff --git a/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/up.sql b/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/up.sql new file mode 100644 index 0000000..d260b17 --- /dev/null +++ b/hasura/migrations/default/1756242960223_alter_table_public_jobs_alter_column_impact1/up.sql @@ -0,0 +1 @@ +alter table "public"."jobs" rename column "impact1" to "impact_1"; diff --git a/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/down.sql b/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/down.sql new file mode 100644 index 0000000..3819663 --- /dev/null +++ b/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/down.sql @@ -0,0 +1 @@ +alter table "public"."jobs" rename column "impact_2" to "impact2"; diff --git a/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/up.sql b/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/up.sql new file mode 100644 index 0000000..a9b72f9 --- /dev/null +++ b/hasura/migrations/default/1756242971065_alter_table_public_jobs_alter_column_impact2/up.sql @@ -0,0 +1 @@ +alter table "public"."jobs" rename column "impact2" to "impact_2"; diff --git a/package.json b/package.json index a1d8e92..5c97d49 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "ImEX RPS", "author": "ImEX Systems Inc. ", "description": "ImEX RPS", - "version": "1.4.2-alpha.7", + "version": "1.4.2-alpha.8", "main": "electron/main.js", "homepage": "./", "dependencies": { 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 new file mode 100644 index 0000000..8135015 --- /dev/null +++ b/src/components/molecules/estimate-scruber-results/estimate-scrubber-results.molecule.jsx @@ -0,0 +1,197 @@ +import { InfoCircleOutlined, LinkOutlined, SearchOutlined } from "@ant-design/icons"; +import { Badge, Collapse, Input, Space, Table, Tag, Typography } from "antd"; +import _ from "lodash"; +import { useState } from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectEsResults } from "../../../redux/application/application.selectors"; +import { selectBodyshop } from "../../../redux/user/user.selectors"; + +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, + esResults: selectEsResults +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(EstimateScrubberResults); + +const { Panel } = Collapse; +const { Title, Text, Link } = Typography; + +export function EstimateScrubberResults({ bodyshop, jobid, job, esResults }) { + const [searchText, setSearchText] = useState(""); + + // Filter items based on search text + const filteredItems = esResults?.items + ? esResults.items.filter(item => { + if (!searchText.trim()) return true; + const searchLower = searchText.toLowerCase(); + return ( + (item.L && item.L.toLowerCase().includes(searchLower)) || + (item.R && item.R.toLowerCase().includes(searchLower)) + ); + }) + : []; + + // Group filtered items by category + const groupedItems = filteredItems.length + ? _.groupBy( + filteredItems.filter((item) => item.Category !== "Display Group"), + "Category" + ) + : {}; + + // Define category colors and priorities + const categoryConfig = { + "Association Items": { color: "blue", priority: 1, icon: "🔗" }, + "Guidelines Items": { color: "orange", priority: 2, icon: "📋" } + //"Display Group": { color: "green", priority: 3, icon: "📊" } + }; + + // Sort categories by priority + const sortedCategories = Object.keys(groupedItems).sort((a, b) => { + const priorityA = categoryConfig[a]?.priority || 999; + const priorityB = categoryConfig[b]?.priority || 999; + return priorityA - priorityB; + }); + + // Define table columns + const columns = [ + { + title: "Item", + dataIndex: "L", + key: "item", + width: "25%", + render: (text, record) => ( + + {text} + {record.LinkText && record.Anchor && ( + + Learn more + + )} + + ) + }, + { + title: "Description", + dataIndex: "R", + key: "description", + width: "75%", + render: (text) => {text} + } + ]; + + if (!esResults?.items?.length || job?.id !== esResults?.jobid) { + return ( +
+ + + Estimate not yet scrubbed. + + Run the estimate scrubber to see results here. +
+ ); + } + + // Show empty search results state + if (searchText && !filteredItems.length) { + return ( +
+ } + value={searchText} + onChange={(e) => setSearchText(e.target.value)} + allowClear + style={{ maxWidth: 400, marginBottom: "24px" }} + /> +
+ + + No items match your search + + Try different keywords or clear the search to see all items. +
+
+ ); + } + + return ( +
+ + {/* Search Input */} +
+ } + value={searchText} + onChange={(e) => setSearchText(e.target.value)} + allowClear + style={{ maxWidth: 400 }} + /> + {searchText && ( +
+ + Showing {filteredItems.length} of {esResults.items.length} items + +
+ )} +
+ + {/* Grouped Results */} + + {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={{}} + /> + + ); + })} + + + {/* Summary */} + + Summary + + {Object.entries(groupedItems).map(([category, items]) => { + const config = categoryConfig[category] || { color: "default" }; + return ( + + {category}: {items.length} + + ); + })} + + + + ); +} diff --git a/src/components/organisms/jobs-detail/jobs-detail.organism.jsx b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx index 623d20e..48d448e 100644 --- a/src/components/organisms/jobs-detail/jobs-detail.organism.jsx +++ b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx @@ -1,6 +1,6 @@ import { useQuery } from "@apollo/client"; import { Card, Result } from "antd"; -import React, { useEffect, useRef } from "react"; +import { useEffect, useRef } from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries"; @@ -8,12 +8,12 @@ import { setSelectedJobTargetPc } from "../../../redux/application/application.a import { selectSelectedJobId } from "../../../redux/application/application.selectors"; import ErrorResultAtom from "../../atoms/error-result/error-result.atom"; import JobsPartsGraphAtom from "../../atoms/jobs-parts-graph/jobs-parts-graph.atom"; +import EstimateScrubberButton from "../../molecules/estimate-scrubber-button/estimate-scrubber-button.molecule"; +import EstimateScrubberResultsMolecule from "../../molecules/estimate-scruber-results/estimate-scrubber-results.molecule"; import JobsDetailDescriptionMolecule from "../../molecules/jobs-detail-description/jobs-detail-description.molecule"; import JobsLinesTableMolecule from "../../molecules/jobs-lines-table/jobs-lines-table.molecule"; import JobsTargetsStatsMolecule from "../../molecules/jobs-targets-stats/jobs-targets-stats.molecule"; import "./jobs-detail.organism.styles.scss"; -import JobsClaimClerk from "../../molecules/jobs-claims-clerk/jobs-claims-clerk.molecule"; -import { EstimateScrubberButton } from "../../molecules/estimate-scrubber-button/estimate-scrubber-button.molecule"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -93,6 +93,18 @@ export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) { > + + ]} + > + +
{ store.dispatch(setAuditError(error)); }); +ipcRenderer.on(ipcTypes.app.toRenderer.scrubResults, async (event, results) => { + store.dispatch(setScrubResults(results)); +}); diff --git a/src/redux/application/application.actions.js b/src/redux/application/application.actions.js index f0ebfce..291817c 100644 --- a/src/redux/application/application.actions.js +++ b/src/redux/application/application.actions.js @@ -57,3 +57,7 @@ export const setReleaseNotes = (releaseNotes) => ({ type: ApplicationActionTypes.SET_RELEASE_NOTES, payload: releaseNotes }); +export const setScrubResults = (scrubResults) => ({ + type: ApplicationActionTypes.SET_ES_RESULTS, + payload: scrubResults +}); diff --git a/src/redux/application/application.reducer.js b/src/redux/application/application.reducer.js index 64d8faf..b96092c 100644 --- a/src/redux/application/application.reducer.js +++ b/src/redux/application/application.reducer.js @@ -10,6 +10,10 @@ const INITIAL_STATE = { updateAvailable: false, updateProgress: null, releaseNotes: null, + esResults: { + jobid: null, + identified_items: [] + } }; const { ipcRenderer } = window; @@ -69,6 +73,8 @@ const applicationReducer = (state = INITIAL_STATE, action) => { return { ...state, updateProgress: action.payload }; case ApplicationActionTypes.SET_RELEASE_NOTES: return { ...state, releaseNotes: action.payload }; + case ApplicationActionTypes.SET_ES_RESULTS: + return { ...state, esResults: action.payload }; default: return state; } diff --git a/src/redux/application/application.selectors.js b/src/redux/application/application.selectors.js index 729e391..60ffbc9 100644 --- a/src/redux/application/application.selectors.js +++ b/src/redux/application/application.selectors.js @@ -46,3 +46,7 @@ export const selectReleaseNotes = createSelector( [selectApplication], (application) => application.releaseNotes ); +export const selectEsResults = createSelector( + [selectApplication], + (application) => application.esResults +); diff --git a/src/redux/application/application.types.js b/src/redux/application/application.types.js index bdb4e27..421cba0 100644 --- a/src/redux/application/application.types.js +++ b/src/redux/application/application.types.js @@ -11,5 +11,6 @@ const ApplicationActionTypes = { SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE", SET_UPDATE_PROGRESS: "SET_UPDATE_PROGRESS", SET_RELEASE_NOTES: "SET_RELEASE_NOTES", + SET_ES_RESULTS: "SET_ES_RESULTS" }; export default ApplicationActionTypes; diff --git a/src/redux/reporting/reporting.sagas.js b/src/redux/reporting/reporting.sagas.js index 6f04f0b..99b9a62 100644 --- a/src/redux/reporting/reporting.sagas.js +++ b/src/redux/reporting/reporting.sagas.js @@ -186,6 +186,10 @@ export function* handleCalculateScoreCard({ payload: queriedJobs }) { const simpleJobAlerts = []; job.joblines.forEach((jobline) => { + + if (jobline.ignore) { + return; + } if (jobline.part_qty > 1) { simpleJobAlerts.push({ key: `line-${jobline.id}-q`, @@ -206,7 +210,7 @@ export function* handleCalculateScoreCard({ payload: queriedJobs }) { } }); - const jobAlerts = [...simpleJobAlerts, + const jobAlerts = [...simpleJobAlerts, // ...job.joblines // .map((jobline) => // jobline.alerts?.map((alert, idx) => ({