diff --git a/electron/decoder/decoder.js b/electron/decoder/decoder.js index c4f053a..209645e 100644 --- a/electron/decoder/decoder.js +++ b/electron/decoder/decoder.js @@ -31,6 +31,10 @@ async function DecodeEstimate(filePath) { returnValue = { ERROR: `Insurance Company Name is not valid for RPS. (${job.INS_CO_NM})`, }; + } else if (!job.clm_no) { + returnValue = { + ERROR: `An unique claim number must be set for all jobs sent to RPS.`, + }; } else { returnValue = _.transform(job, function (result, val, key) { result[key.toLowerCase()] = val; diff --git a/src/components/atoms/part-type-converter/part-type-converter.atom.jsx b/src/components/atoms/part-type-converter/part-type-converter.atom.jsx index 95fe49b..624027a 100644 --- a/src/components/atoms/part-type-converter/part-type-converter.atom.jsx +++ b/src/components/atoms/part-type-converter/part-type-converter.atom.jsx @@ -1,9 +1,9 @@ export default (part_type) => { switch (part_type) { case "PAA": - return "Aftermarket"; + return "A/M"; case "PAE": - return "Existing"; + return "Exist."; case "PAN": return "OEM"; case "PAL": diff --git a/src/components/atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom.jsx b/src/components/atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom.jsx index 7d9655b..d5c7628 100644 --- a/src/components/atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom.jsx +++ b/src/components/atoms/price-diff-pc-formatter/price-diff-pc-formatter.atom.jsx @@ -1,40 +1,22 @@ -import React, { useMemo } from "react"; +import { AlertFilled } from "@ant-design/icons"; +import React from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../../redux/user/user.selectors"; +import { selectSelectedJobTargetPc } from "../../../redux/application/application.selectors"; import "./price-diff-pc-formatter.styles.scss"; -import { AlertFilled } from "@ant-design/icons"; + const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, -}); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) + selectedJobTargetPc: selectSelectedJobTargetPc, }); export function PriceDiffPcFormatterAtom({ - bodyshop, price_diff_pc, - group, - v_age, + selectedJobTargetPc, }) { - const metTarget = useMemo(() => { - const targetsForGroup = bodyshop.targets.filter((t) => t.group === group); - if (!targetsForGroup) return 0; - const targetPc = targetsForGroup.filter( - (t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true) - ); - if (targetPc.length === 0) return false; - else if (targetPc.length === 1) return price_diff_pc >= targetPc[0].target; - else { - alert("Multiple targets match."); - return false; - } - }, [bodyshop, group, price_diff_pc, v_age]); - return (
selectedJobTargetPc ? "green" : "red", display: "flex", alignItems: "center", }} @@ -46,7 +28,4 @@ export function PriceDiffPcFormatterAtom({
); } -export default connect( - mapStateToProps, - mapDispatchToProps -)(PriceDiffPcFormatterAtom); +export default connect(mapStateToProps, null)(PriceDiffPcFormatterAtom); diff --git a/src/components/atoms/target-price-diff/target-price-diff-pc.atom.jsx b/src/components/atoms/target-price-diff/target-price-diff-pc.atom.jsx deleted file mode 100644 index 1bf467e..0000000 --- a/src/components/atoms/target-price-diff/target-price-diff-pc.atom.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Statistic } from "antd"; -import React, { useMemo } from "react"; -import { connect } from "react-redux"; -import { createStructuredSelector } from "reselect"; -import { selectBodyshop } from "../../../redux/user/user.selectors"; - -const mapStateToProps = createStructuredSelector({ - bodyshop: selectBodyshop, -}); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); - -export function PriceDiffPcFormatterAtom({ bodyshop, group, v_age }) { - const metTarget = useMemo(() => { - const targetsForGroup = bodyshop.targets.filter((t) => t.group === group); - if (!targetsForGroup) return 0; - const targetPc = targetsForGroup.filter( - (t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true) - ); - if (targetPc.length === 0) return 0; - else if (targetPc.length === 1) return targetPc[0].target; - else { - return 0; - } - }, [bodyshop, group, v_age]); - - return ( - - ); -} -export default connect( - mapStateToProps, - mapDispatchToProps -)(PriceDiffPcFormatterAtom); diff --git a/src/components/molecules/jobs-targets-stats/jobs-targets-stats.molecule.jsx b/src/components/molecules/jobs-targets-stats/jobs-targets-stats.molecule.jsx index cd92bd9..b5dc058 100644 --- a/src/components/molecules/jobs-targets-stats/jobs-targets-stats.molecule.jsx +++ b/src/components/molecules/jobs-targets-stats/jobs-targets-stats.molecule.jsx @@ -1,10 +1,27 @@ import { Skeleton, Statistic } from "antd"; import Dinero from "dinero.js"; import React, { useMemo } from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectSelectedJobTargetPc } from "../../../redux/application/application.selectors"; import ErrorResultAtom from "../../atoms/error-result/error-result.atom"; -import TargetPriceDiffPcAtom from "../../atoms/target-price-diff/target-price-diff-pc.atom"; -export default function JobsTargetsStatsMolecule({ loading, job }) { +const mapStateToProps = createStructuredSelector({ + selectedJobTargetPc: selectSelectedJobTargetPc, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect( + mapStateToProps, + mapDispatchToProps +)(JobsTargetsStatsMolecule); + +export function JobsTargetsStatsMolecule({ + loading, + job, + selectedJobTargetPc, +}) { const currentRpsDollars = useMemo(() => { if (!job) { return 0; @@ -44,8 +61,20 @@ export default function JobsTargetsStatsMolecule({ loading, job }) { marginBottom: "1rem", }} > - - + + currentRpsPc ? "tomato" : "seagreen", + }} + value={currentRpsPc} + suffix="%" + /> ); diff --git a/src/components/organisms/jobs-detail/jobs-detail.organism.jsx b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx index dbf01f7..e05f725 100644 --- a/src/components/organisms/jobs-detail/jobs-detail.organism.jsx +++ b/src/components/organisms/jobs-detail/jobs-detail.organism.jsx @@ -1,9 +1,10 @@ import { useQuery } from "@apollo/client"; import { Result } from "antd"; -import React from "react"; +import React, { useEffect } from "react"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries"; +import { setSelectedJobTargetPc } from "../../../redux/application/application.actions"; 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"; @@ -18,14 +19,23 @@ const mapStateToProps = createStructuredSelector({ }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) + setSelectedJobTargetPc: (job) => dispatch(setSelectedJobTargetPc(job)), }); -export function JobsDetailOrganism({ selectedJobId }) { +export function JobsDetailOrganism({ selectedJobId, setSelectedJobTargetPc }) { const { loading, error, data } = useQuery(QUERY_JOB_BY_PK, { variables: { jobId: selectedJobId }, skip: !selectedJobId, }); + useEffect(() => { + if (data) + setSelectedJobTargetPc({ + group: data.jobs_by_pk.group, + v_age: data.jobs_by_pk.v_age, + }); + }, [data, setSelectedJobTargetPc]); + if (!selectedJobId) return ; if (error) return ( diff --git a/src/redux/application/application.actions.js b/src/redux/application/application.actions.js index a5f9bfa..6eb245d 100644 --- a/src/redux/application/application.actions.js +++ b/src/redux/application/application.actions.js @@ -29,3 +29,12 @@ export const setSelectedJobId = (jobId) => ({ type: ApplicationActionTypes.SET_SELECTED_JOB_ID, payload: jobId, }); +export const setSelectedJobTargetPc = ({ group, v_age }) => ({ + type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC, + payload: { group, v_age }, +}); + +export const setSelectedJobTargetPcSuccess = (pct) => ({ + type: ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC_SUCCESS, + payload: pct, +}); diff --git a/src/redux/application/application.reducer.js b/src/redux/application/application.reducer.js index c29a546..2e25874 100644 --- a/src/redux/application/application.reducer.js +++ b/src/redux/application/application.reducer.js @@ -4,6 +4,7 @@ const INITIAL_STATE = { watchedPaths: [], watcherError: null, selectedJobId: null, + selectedJobTargetPc: 100, }; const applicationReducer = (state = INITIAL_STATE, action) => { @@ -33,6 +34,11 @@ const applicationReducer = (state = INITIAL_STATE, action) => { ...state, watcherError: action.payload, }; + case ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC_SUCCESS: + return { + ...state, + selectedJobTargetPc: action.payload, + }; case ApplicationActionTypes.SET_SELECTED_JOB_ID: return { ...state, selectedJobId: action.payload }; default: diff --git a/src/redux/application/application.sagas.js b/src/redux/application/application.sagas.js index 4d19439..38fbe52 100644 --- a/src/redux/application/application.sagas.js +++ b/src/redux/application/application.sagas.js @@ -1,13 +1,30 @@ -//import { all, call, takeLatest } from "redux-saga/effects"; -//import ApplicationActionTypes from "./application.types"; +import { all, call, takeLatest, select, put } from "redux-saga/effects"; +import { setSelectedJobTargetPcSuccess } from "./application.actions"; +import ApplicationActionTypes from "./application.types"; -// export function* onJoinRoom() { -// yield takeLatest(ApplicationActionTypes.JOIN_ROOM, joinRoom); -// } -// export function* joinRoom({ payload: roomId }) { -// // console.log("function*joinRoom -> roomId", roomId); -// } +export function* onSetTargetPc() { + yield takeLatest( + ApplicationActionTypes.SET_SELECTED_JOB_TARGET_PC, + CalculateTarget + ); +} +export function* CalculateTarget({ payload }) { + const { group, v_age } = payload; + const targets = yield select((state) => state.user.bodyshop.targets); + + const targetsForGroup = targets.filter((t) => t.group === group); + if (!targetsForGroup) return 0; + const targetPc = targetsForGroup.filter( + (t) => t.ageGte <= v_age && (t.ageLt ? t.ageLt > v_age : true) + ); + if (targetPc.length === 0) yield put(setSelectedJobTargetPcSuccess(100)); + else if (targetPc.length === 1) + yield put(setSelectedJobTargetPcSuccess(targetPc[0].target)); + else { + yield put(setSelectedJobTargetPcSuccess(100)); + } +} export function* applicationSagas() { - //yield all([call(onJoinRoom)]); + yield all([call(onSetTargetPc)]); } diff --git a/src/redux/application/application.selectors.js b/src/redux/application/application.selectors.js index 8fcd74e..db993f7 100644 --- a/src/redux/application/application.selectors.js +++ b/src/redux/application/application.selectors.js @@ -16,7 +16,13 @@ export const selectWatcherError = createSelector( [selectApplication], (application) => application.watcherError ); + export const selectSelectedJobId = createSelector( [selectApplication], (application) => application.selectedJobId ); + +export const selectSelectedJobTargetPc = createSelector( + [selectApplication], + (application) => application.selectedJobTargetPc +); diff --git a/src/redux/application/application.types.js b/src/redux/application/application.types.js index 72b30fc..bac9945 100644 --- a/src/redux/application/application.types.js +++ b/src/redux/application/application.types.js @@ -5,5 +5,7 @@ const ApplicationActionTypes = { SET_WATCHER_STATUS: "SET_WATCHER_STATUS", SET_WATCHER_ERROR: "SET_WATCHER_ERROR", SET_SELECTED_JOB_ID: "SET_SELECTED_JOB_ID", + SET_SELECTED_JOB_TARGET_PC: "SET_SELECTED_JOB_TARGET_PC", + SET_SELECTED_JOB_TARGET_PC_SUCCESS: "SET_SELECTED_JOB_TARGET_PC_SUCCESS", }; export default ApplicationActionTypes;