Merged in master (pull request #9)

Added scatter plot on reporting RPS-52
This commit is contained in:
Patrick Fic
2020-10-29 00:26:21 +00:00
9 changed files with 196 additions and 17 deletions

View File

@@ -10,7 +10,7 @@ Nucleus.init("5f91b569b95bac34eefdb63a", {
});
Nucleus.setProps({
version: app.getVersion().toString(),
version: app.getVersion(),
});
Nucleus.onError = (type, err) => {
@@ -24,8 +24,7 @@ ipcMain.on(ipcTypes.app.toMain.setUserName, (event, userName) => {
});
ipcMain.on(ipcTypes.app.toMain.track, (e, args) => {
console.log("args", args);
log.log("Received Tracking Request", args);
log.log("NUCLEUS Event", args);
const { event, ...eventDetails } = args;
try {
Nucleus.track(event, eventDetails);

View File

@@ -301,7 +301,7 @@ autoUpdater.on("download-progress", (ev) => {
});
autoUpdater.on("update-downloaded", (ev, info) => {
Nucleus.track("UPDATE_DOWNLOADED", info);
Nucleus.track("UPDATE_DOWNLOADED", ev);
// if (process.env.NODE_ENV === "production") {
dialog.showMessageBox(

View File

@@ -133,7 +133,6 @@ export default function JobLinesTableMolecule({ loading, job }) {
: joblines;
const handleChange = (pagination, filters, sorter) => {
console.log("Various parameters", pagination, filters, sorter);
setFilters(filters);
};

View File

@@ -3,6 +3,7 @@ import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { queryReportingData } from "../../../redux/reporting/reporting.actions";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
@@ -32,7 +33,24 @@ export function ReportingDatesMolecule({ queryReportingData }) {
name="dateRange"
rules={[{ type: "array", required: true }]}
>
<DatePicker.RangePicker />
<DatePicker.RangePicker
ranges={{
Today: [moment(), moment()],
"Last Month": [
moment().startOf("month").subtract(1, "month"),
moment().startOf("month").subtract(1, "month").endOf("month"),
],
"This Month": [
moment().startOf("month"),
moment().endOf("month"),
],
"Next Month": [
moment().startOf("month").add(1, "month"),
moment().startOf("month").add(1, "month").endOf("month"),
],
"Last 7 days": [moment().subtract(7, "days"), moment()],
}}
/>
</Form.Item>
<Button type="primary" htmlType="submit">
Run Search

View File

@@ -81,7 +81,7 @@ export function ReportingJobsListMolecule({
render: (text, record) => record.actPriceSum.toFormat(),
},
{
title: "Price Diff.",
title: "$ (Act./Target)",
dataIndex: "jobRpsDollars",
key: "jobRpsDollars",
render: (text, record) => (
@@ -95,7 +95,7 @@ export function ReportingJobsListMolecule({
),
},
{
title: "Price Diff. %",
title: "% (Act./Target)",
dataIndex: "price_diff_pc",
key: "price_diff_pc",
render: (text, record) => (
@@ -152,7 +152,9 @@ export function ReportingJobsListMolecule({
}}
summary={() => (
<Table.Summary.Row>
<Table.Summary.Cell index={0}></Table.Summary.Cell>
<Table.Summary.Cell index={0}>
<strong>Totals</strong>
</Table.Summary.Cell>
<Table.Summary.Cell index={1}></Table.Summary.Cell>
<Table.Summary.Cell index={2}></Table.Summary.Cell>
<Table.Summary.Cell index={3}></Table.Summary.Cell>

View File

@@ -0,0 +1,119 @@
import { Card, Skeleton, Typography, Tooltip as AntdToolTip } from "antd";
import React from "react";
import { connect } from "react-redux";
import {
CartesianGrid,
Legend,
ResponsiveContainer,
Scatter,
ScatterChart,
Tooltip,
XAxis,
YAxis,
ZAxis,
} from "recharts";
import { createStructuredSelector } from "reselect";
import {
selectReportLoading,
selectScorecard,
} from "../../../redux/reporting/reporting.selectors";
import DataLabelAtom from "../../atoms/data-label/data-label.atom";
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
const mapStateToProps = createStructuredSelector({
reportingLoading: selectReportLoading,
scoreCard: selectScorecard,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ReportingScatterChartMolecule);
export function ReportingScatterChartMolecule({ reportingLoading, scoreCard }) {
if (reportingLoading) return <Skeleton active />;
if (!scoreCard)
return <ErrorResultAtom title="Error displaying score card data." />;
return (
<div>
<div
style={{
marginTop: "1rem",
marginBottom: "1rem",
width: "100%",
height: "30rem",
}}
>
<AntdToolTip
placement="bottomLeft"
title="Calculated as Actual Job RPS % less Target Job RPS %. E.g. 10% - 8% = 2%"
>
<Typography.Title level={4}>% Variance from Target</Typography.Title>
</AntdToolTip>
<ResponsiveContainer>
<ScatterChart margin={{ top: 20, right: 20, bottom: 10, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
dataKey="deviation"
type="number"
ticks={[-100, -75, -50, -25, 0, 25, 50, 75, 100]}
domain={[-100, 100]}
name="Deviation"
unit="%"
/>
<YAxis type="number" dataKey="age" name="Age" unit="Years" />
<ZAxis dataKey="dbPriceSumAmt" name="DB Price Total" />
<Tooltip
content={({ payload }) => {
const item = payload && payload[0] && payload[0].payload;
if (!item) return null;
return (
<Card title={item.clm_no}>
<div></div>
<div></div>
<DataLabelAtom label="Owner">{item.owner}</DataLabelAtom>
<DataLabelAtom label="Vehicle">
{item.vehicle}
</DataLabelAtom>
<DataLabelAtom label="Job RPS $">
{item.jobRpsDollars.toFormat()}
</DataLabelAtom>
<DataLabelAtom label="Job RPS %">
{(item.jobRpsPc * 100).toFixed(1)}%
</DataLabelAtom>
<DataLabelAtom label="DB Price">
{item.dbPriceSum.toFormat()}
</DataLabelAtom>
</Card>
);
}}
cursor={{ strokeDasharray: "2 2", stroke: "tomato" }}
/>
<Legend />
{Object.keys(scoreCard.scatterChart).map((key, idx) => (
<Scatter
name={key}
key={idx}
data={scoreCard.scatterChart[key]}
fill={colors[idx]}
></Scatter>
))}
</ScatterChart>
</ResponsiveContainer>
</div>
</div>
);
}
const colors = [
"#0c344d",
"#6cc314",
"#f5782a",
"#f5be2a",
"#fbff90",
"dodgerblue",
];

View File

@@ -1,3 +1,4 @@
import { Card } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -5,7 +6,9 @@ import { selectDates } from "../../../redux/reporting/reporting.selectors";
import ReportingTitleAtom from "../../atoms/reporting-title/reporting-title.atom";
import ReportingDatesMolecule from "../../molecules/reporting-dates/reporting-dates.molecule";
import ReportingJobsListMolecule from "../../molecules/reporting-jobs-list/reporting-jobs-list.molecule";
import ReportingScatterChartMolecule from "../../molecules/reporting-scatterchart/reporting-scatterchart.molecule";
import ReportingTotalsStatsMolecule from "../../molecules/reporting-totals-stats/reporting-totals-stats.molecule";
import "./reporting.page.styles.scss";
const mapStateToProps = createStructuredSelector({
dates: selectDates,
@@ -17,13 +20,22 @@ export default connect(mapStateToProps, mapDispatchToProps)(ReportingPage);
export function ReportingPage({ dates }) {
return (
<div>
<ReportingDatesMolecule />
<div className="reporting-container">
<Card>
<ReportingDatesMolecule />
</Card>
{dates && dates.startDate && dates.endDate && (
<div>
<ReportingTitleAtom />
<ReportingTotalsStatsMolecule />
<ReportingJobsListMolecule />
<div className="reporting-cards">
<Card>
<ReportingTitleAtom />
<ReportingTotalsStatsMolecule />
</Card>
<Card>
<ReportingJobsListMolecule />
</Card>
<Card>
<ReportingScatterChartMolecule />
</Card>
</div>
)}
</div>

View File

@@ -0,0 +1,8 @@
.reporting-container {
height: 100%;
overflow-y: auto;
background-color: rgb(244, 244, 244);
& > .reporting-cards > * {
margin: 0.7rem;
}
}

View File

@@ -1,17 +1,18 @@
import Dinero from "dinero.js";
import _ from "lodash";
import { all, call, put, select, takeLatest } from "redux-saga/effects";
import client from "../../graphql/GraphQLClient";
import { REPORTING_GET_JOBS } from "../../graphql/reporting.queries";
import ipcTypes from "../../ipc.types";
import {
CalculateJobRpsDollars,
CalculateJobRpsPc
CalculateJobRpsPc,
} from "../../util/CalculateJobRps";
import GetJobTarget from "../../util/GetJobTarget";
import {
calculateScorecard,
setReportingData,
setScoreCard
setScoreCard,
} from "./reporting.actions";
import ReportingApplicationTypes from "./reporting.types";
@@ -59,6 +60,7 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
});
const targets = yield select((state) => state.user.bodyshop.targets);
const groups = yield select((state) => state.user.bodyshop.groups);
const scoreCard = {
shopRpsTotalDollars: Dinero(),
@@ -69,6 +71,13 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
allJobsSumActPrice: Dinero(),
currentRpsPc: 0,
targetRpsPc: 0,
scatterChart: _.sortBy(
groups,
[(group) => group.toLowerCase()],
["desc"]
).reduce((acc, val) => {
return { ...acc, [val]: [] };
}, {}),
};
//Get the RPS on a per job basis.
@@ -93,6 +102,19 @@ export function* handleCalculateScoreCard({ payload: jobs }) {
actPriceSum
);
scoreCard.scatterChart[job.group].push({
deviation: Math.round((jobRpsPc - jobTarget) * 1000) / 10,
age: job.v_age,
dbPriceSum,
dbPriceSumAmt: dbPriceSum.getAmount() / 100,
id: job.id,
owner: `${job.ownr_fn} ${job.ownr_ln}`,
vehicle: `${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type}) - ${job.group}`,
clm_no: job.clm_no,
jobRpsDollars,
jobRpsPc,
});
//sum db price * percentage expected.
return {
...job,