Refactored target percentage to use redux and only be calculated once per job.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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 (
|
||||
<div
|
||||
style={{
|
||||
color: metTarget ? "green" : "red",
|
||||
color: price_diff_pc > selectedJobTargetPc ? "green" : "red",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
}}
|
||||
@@ -46,7 +28,4 @@ export function PriceDiffPcFormatterAtom({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PriceDiffPcFormatterAtom);
|
||||
export default connect(mapStateToProps, null)(PriceDiffPcFormatterAtom);
|
||||
|
||||
@@ -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 (
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(metTarget * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PriceDiffPcFormatterAtom);
|
||||
@@ -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",
|
||||
}}
|
||||
>
|
||||
<TargetPriceDiffPcAtom v_age={job.v_age} group={job.group} />
|
||||
<Statistic title="Current RPS %" value={currentRpsPc} suffix="%" />
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(selectedJobTargetPc * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
valueStyle={{
|
||||
color:
|
||||
selectedJobTargetPc * 100 > currentRpsPc ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={currentRpsPc}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic title="Current RPS $" value={currentRpsDollars.toFormat()} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -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 <Result title="No job selected." />;
|
||||
if (error)
|
||||
return (
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)]);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user