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 82b53ab..2c7008e 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,9 +1,12 @@
import { Skeleton, Statistic } from "antd";
-import Dinero from "dinero.js";
-import React, { useMemo } from "react";
+import React, { useCallback } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectSelectedJobTargetPc } from "../../../redux/application/application.selectors";
+import {
+ CalculateJobRpsDollars,
+ CalculateJobRpsPc,
+} from "../../../util/CalculateJobRps";
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
const mapStateToProps = createStructuredSelector({
@@ -22,35 +25,12 @@ export function JobsTargetsStatsMolecule({
job,
selectedJobTargetPc,
}) {
- const currentRpsDollars = useMemo(() => {
- if (!job) {
- return 0;
- }
- return job.joblines
- .filter((j) => !j.ignore)
- .reduce((acc, val) => {
- if (val.price_diff > 0) {
- return acc.add(
- Dinero({ amount: Math.round((val.price_diff || 0) * 100) })
- );
- } else {
- return acc;
- }
- }, Dinero());
- }, [job]);
+ const currentRpsDollars = useCallback(CalculateJobRpsDollars(job), [job]);
- const currentRpsPc = useMemo(() => {
- //TODO Redo this to do total of db price - act price / db price
- if (!job) {
- return 0;
- }
- const dbPriceSum = job.joblines
- .filter((j) => !j.ignore)
- .reduce((acc, val) => {
- return acc + val.db_price;
- }, 0);
- return (currentRpsDollars.getAmount() / dbPriceSum).toFixed(1);
- }, [job, currentRpsDollars]);
+ const currentRpsPc = useCallback(CalculateJobRpsPc(job, currentRpsDollars), [
+ job,
+ currentRpsDollars,
+ ]);
if (loading) return ;
if (!job) return ;
diff --git a/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx b/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx
new file mode 100644
index 0000000..b908a9a
--- /dev/null
+++ b/src/components/molecules/reporting-dates/reporting-dates.molecule.jsx
@@ -0,0 +1,44 @@
+import { Button, DatePicker, Form } from "antd";
+import React from "react";
+import { connect } from "react-redux";
+import { createStructuredSelector } from "reselect";
+import { queryReportingData } from "../../../redux/reporting/reporting.actions";
+const mapStateToProps = createStructuredSelector({
+ //currentUser: selectCurrentUser
+});
+const mapDispatchToProps = (dispatch) => ({
+ queryReportingData: (dates) => dispatch(queryReportingData(dates)),
+});
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ReportingDatesMolecule);
+
+export function ReportingDatesMolecule({ queryReportingData }) {
+ const [form] = Form.useForm();
+
+ const handleFinish = (values) => {
+ console.log("values", values);
+ queryReportingData({
+ startDate: values.dateRange[0],
+ endDate: values.dateRange[1],
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/molecules/sider-sign-out/sider-sign-out.molecule.jsx b/src/components/molecules/sider-sign-out/sider-sign-out.molecule.jsx
index daed0b4..9e50dd6 100644
--- a/src/components/molecules/sider-sign-out/sider-sign-out.molecule.jsx
+++ b/src/components/molecules/sider-sign-out/sider-sign-out.molecule.jsx
@@ -1,4 +1,4 @@
-import { UserOutlined } from "@ant-design/icons";
+import { LogoutOutlined } from "@ant-design/icons";
import { Menu } from "antd";
import React from "react";
import { connect } from "react-redux";
@@ -11,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({
export function SiderSignOut({ signOutStart, ...restProps }) {
return (
}
+ icon={}
{...restProps}
onClick={() => signOutStart()}
>
diff --git a/src/components/organisms/sider-menu/sider-menu.organism.jsx b/src/components/organisms/sider-menu/sider-menu.organism.jsx
index 7c02dd4..e82410e 100644
--- a/src/components/organisms/sider-menu/sider-menu.organism.jsx
+++ b/src/components/organisms/sider-menu/sider-menu.organism.jsx
@@ -2,6 +2,7 @@ import {
PieChartOutlined,
SettingFilled,
CloseOutlined,
+ BarChartOutlined,
} from "@ant-design/icons";
import { Menu } from "antd";
import React from "react";
@@ -19,6 +20,9 @@ export default function SiderMenuOrganism() {
}>
Jobs
+ }>
+ Reporting
+
}>
Settings
diff --git a/src/components/pages/reporting/reporting.page.jsx b/src/components/pages/reporting/reporting.page.jsx
new file mode 100644
index 0000000..73a57d9
--- /dev/null
+++ b/src/components/pages/reporting/reporting.page.jsx
@@ -0,0 +1,10 @@
+import React from "react";
+import ReportingDatesMolecule from "../../molecules/reporting-dates/reporting-dates.molecule";
+
+export default function ReportingPage() {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/pages/routes/routes.page.jsx b/src/components/pages/routes/routes.page.jsx
index cf05db8..4709e39 100644
--- a/src/components/pages/routes/routes.page.jsx
+++ b/src/components/pages/routes/routes.page.jsx
@@ -3,11 +3,13 @@ import React from "react";
import { connect } from "react-redux";
import { Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
-import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism";
-import Jobs from "../jobs/jobs.page";
-import SettingsPage from "../settings/settings.page";
import { selectBodyshop } from "../../../redux/user/user.selectors";
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
+import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism";
+import Jobs from "../jobs/jobs.page";
+import ReportingPage from "../reporting/reporting.page";
+import SettingsPage from "../settings/settings.page";
+
const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop });
const mapDispatchToProps = (dispatch) => ({});
@@ -32,6 +34,7 @@ export function RoutesPage({ bodyshop }) {
+
diff --git a/src/components/pages/settings/settings.page.jsx b/src/components/pages/settings/settings.page.jsx
index f23e018..8455ac5 100644
--- a/src/components/pages/settings/settings.page.jsx
+++ b/src/components/pages/settings/settings.page.jsx
@@ -28,11 +28,3 @@ export default function SettingsPage() {
);
}
-
-//
diff --git a/src/graphql/reporting.queries.js b/src/graphql/reporting.queries.js
new file mode 100644
index 0000000..a7bbfb5
--- /dev/null
+++ b/src/graphql/reporting.queries.js
@@ -0,0 +1,47 @@
+import gql from "graphql-tag";
+
+export const REPORTING_GET_JOBS = gql`
+ query REPORTING_GET_JOBS($startDate: date, $endDate: date) {
+ jobs(
+ where: {
+ _and: [
+ { close_date: { _gte: $startDate } }
+ { close_date: { _lte: $endDate } }
+ { close_date: { _is_null: false } }
+ ]
+ }
+ ) {
+ ownr_ln
+ ownr_fn
+ ins_co_nm
+ group
+ clm_total
+ clm_no
+ close_date
+ id
+ loss_date
+ updated_at
+ v_age
+ v_makedesc
+ v_model
+ v_model_yr
+ joblines {
+ act_price
+ db_price
+ part_qty
+ part_type
+ price_diff
+ price_diff_pc
+ updated_at
+ oem_partno
+ line_no
+ line_ind
+ line_desc
+ ignore
+ id
+ db_ref
+ unq_seq
+ }
+ }
+ }
+`;
diff --git a/src/redux/application/application.sagas.js b/src/redux/application/application.sagas.js
index 38fbe52..c69a008 100644
--- a/src/redux/application/application.sagas.js
+++ b/src/redux/application/application.sagas.js
@@ -1,4 +1,5 @@
import { all, call, takeLatest, select, put } from "redux-saga/effects";
+import GetJobTarget from "../../util/GetJobTarget";
import { setSelectedJobTargetPcSuccess } from "./application.actions";
import ApplicationActionTypes from "./application.types";
@@ -12,17 +13,18 @@ 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));
- }
+ yield put(setSelectedJobTargetPcSuccess(GetJobTarget(group, v_age, 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() {
diff --git a/src/redux/reporting/reporting.actions.js b/src/redux/reporting/reporting.actions.js
new file mode 100644
index 0000000..a7aa440
--- /dev/null
+++ b/src/redux/reporting/reporting.actions.js
@@ -0,0 +1,24 @@
+import ReportingActionTypes from "./reporting.types";
+
+export const queryReportingData = ({ startDate, endDate }) => ({
+ type: ReportingActionTypes.QUERY_REPORTING_DATA,
+ payload: { startDate, endDate },
+});
+
+export const setReportingData = (data) => ({
+ type: ReportingActionTypes.SET_REPORTING_DATA,
+ payload: data,
+});
+
+export const calculateScorecard = (data) => ({
+ type: ReportingActionTypes.CALCULATE_SCORE_CARD,
+ payload: data,
+});
+export const setScoreCard = (data) => ({
+ type: ReportingActionTypes.SET_SCORE_CARD,
+ payload: data,
+});
+export const setReportingError = (data) => ({
+ type: ReportingActionTypes.SET_REPORTING_ERROR,
+ payload: data,
+});
diff --git a/src/redux/reporting/reporting.reducer.js b/src/redux/reporting/reporting.reducer.js
new file mode 100644
index 0000000..f77390c
--- /dev/null
+++ b/src/redux/reporting/reporting.reducer.js
@@ -0,0 +1,23 @@
+import ReportingActionTypes from "./reporting.types";
+const INITIAL_STATE = {
+ dates: { startDate: null, endDate: null },
+ data: [],
+ scoreCard: null,
+ error: null,
+ loading: false,
+};
+
+const applicationReducer = (state = INITIAL_STATE, action) => {
+ switch (action.type) {
+ case ReportingActionTypes.QUERY_REPORTING_DATA:
+ return { ...state, loading: true, dates: action.payload };
+ case ReportingActionTypes.SET_REPORTING_DATA:
+ return { ...state, data: action.payload };
+ case ReportingActionTypes.SET_SCORE_CARD:
+ return { ...state, loading: false, scoreCard: action.payload };
+ default:
+ return state;
+ }
+};
+
+export default applicationReducer;
diff --git a/src/redux/reporting/reporting.sagas.js b/src/redux/reporting/reporting.sagas.js
new file mode 100644
index 0000000..5d02944
--- /dev/null
+++ b/src/redux/reporting/reporting.sagas.js
@@ -0,0 +1,57 @@
+import { all, call, takeLatest, select, put } from "redux-saga/effects";
+import { calculateScorecard, setReportingData } from "./reporting.actions";
+import ReportingApplicationTypes from "./reporting.types";
+import client from "../../graphql/GraphQLClient";
+import { REPORTING_GET_JOBS } from "../../graphql/reporting.queries";
+const { log } = window;
+
+export function* onQueryReportData() {
+ yield takeLatest(
+ ReportingApplicationTypes.QUERY_REPORTING_DATA,
+ queryReportingData
+ );
+}
+export function* queryReportingData({ payload: { startDate, endDate } }) {
+ const result = yield client.query({
+ query: REPORTING_GET_JOBS,
+ variables: { startDate, endDate },
+ });
+ if (result.errors) {
+ log.error("Error fetching report data.", result.errors);
+ yield put(setReportingData(null));
+ } else {
+ yield put(setReportingData(result.data.jobs));
+ }
+}
+
+export function* onSetReportData() {
+ yield takeLatest(
+ ReportingApplicationTypes.SET_REPORTING_DATA,
+ handleSetReportData
+ );
+}
+export function* handleSetReportData({ payload: jobs }) {
+ yield put(calculateScorecard(jobs));
+}
+
+export function* onCalculateScoreCard() {
+ yield takeLatest(
+ ReportingApplicationTypes.CALCULATE_SCORE_CARD,
+ handleCalculateScoreCard
+ );
+}
+export function* handleCalculateScoreCard({ payload: jobs }) {
+ console.log("jobs", jobs);
+ // yield put(calculateScorecard(jobs));
+
+ //Get the RPS on a per job basis.
+
+}
+
+export function* reportingSagas() {
+ yield all([
+ call(onQueryReportData),
+ call(onSetReportData),
+ call(onCalculateScoreCard),
+ ]);
+}
diff --git a/src/redux/reporting/reporting.selectors.js b/src/redux/reporting/reporting.selectors.js
new file mode 100644
index 0000000..7780c2a
--- /dev/null
+++ b/src/redux/reporting/reporting.selectors.js
@@ -0,0 +1,49 @@
+import { createSelector } from "reselect";
+
+const selectReporting = (state) => state.reporting;
+
+export const selectReportLoading = createSelector(
+ [selectReporting],
+ (reporting) => reporting.loading
+);
+export const selectDates = createSelector(
+ [selectReporting],
+ (reporting) => reporting.dates
+);
+export const selectScorecard = createSelector(
+ [selectReporting],
+ (reporting) => reporting.scoreCard
+);
+export const selectReportingError = createSelector(
+ [selectReporting],
+ (reporting) => reporting.error
+);
+export const selectReportData = createSelector(
+ [selectReporting],
+ (reporting) => reporting.data
+);
+
+// export const selectWatchedPaths = createSelector(
+// [selectReporting],
+// (application) => application.watchedPaths
+// );
+
+// export const selectWatcherError = createSelector(
+// [selectReporting],
+// (application) => application.watcherError
+// );
+
+// export const selectSelectedJobId = createSelector(
+// [selectReporting],
+// (application) => application.selectedJobId
+// );
+
+// export const selectSelectedJobTargetPc = createSelector(
+// [selectReporting],
+// (application) => application.selectedJobTargetPc
+// );
+
+// export const selectSettings = createSelector(
+// [selectReporting],
+// (application) => application.settings
+// );
diff --git a/src/redux/reporting/reporting.types.js b/src/redux/reporting/reporting.types.js
new file mode 100644
index 0000000..18c0755
--- /dev/null
+++ b/src/redux/reporting/reporting.types.js
@@ -0,0 +1,8 @@
+const ReportingActionTypes = {
+ QUERY_REPORTING_DATA: "QUERY_REPORTING_DATA",
+ CALCULATE_SCORE_CARD: "CALCULATE_SCORE_CARD",
+ SET_REPORTING_DATA: "SET_REPORTING_DATA",
+ SET_SCORE_CARD: "SET_SCORE_CARD",
+ SET_REPORTING_ERROR: "SET_REPORTING_ERROR",
+};
+export default ReportingActionTypes;
diff --git a/src/redux/root.reducer.js b/src/redux/root.reducer.js
index 902b0fd..eaeda26 100644
--- a/src/redux/root.reducer.js
+++ b/src/redux/root.reducer.js
@@ -3,16 +3,18 @@ import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import applicationReducer from "./application/application.reducer";
import userReducer from "./user/user.reducer";
+import reportingReducer from "./reporting/reporting.reducer";
const persistConfig = {
key: "root",
storage,
- blacklist: ["application", "user"],
+ blacklist: ["application", "user", "reporting"],
};
const rootReducer = combineReducers({
application: applicationReducer,
user: userReducer,
+ reporting: reportingReducer,
});
export default persistReducer(persistConfig, rootReducer);
diff --git a/src/redux/root.saga.js b/src/redux/root.saga.js
index 64c4946..d348a07 100644
--- a/src/redux/root.saga.js
+++ b/src/redux/root.saga.js
@@ -1,7 +1,7 @@
import { all, call } from "redux-saga/effects";
import { applicationSagas } from "./application/application.sagas";
import { userSagas } from "./user/user.sagas";
-
+import { reportingSagas } from "./reporting/reporting.sagas";
export default function* rootSaga() {
- yield all([call(applicationSagas), call(userSagas)]);
+ yield all([call(applicationSagas), call(userSagas), call(reportingSagas)]);
}
diff --git a/src/util/CalculateJobRps.js b/src/util/CalculateJobRps.js
new file mode 100644
index 0000000..f31aa09
--- /dev/null
+++ b/src/util/CalculateJobRps.js
@@ -0,0 +1,31 @@
+import Dinero from "dinero.js";
+
+export function CalculateJobRpsDollars(job) {
+ if (!job) {
+ return 0;
+ }
+ return job.joblines
+ .filter((j) => !j.ignore)
+ .reduce((acc, val) => {
+ if (val.price_diff > 0) {
+ return acc.add(
+ Dinero({ amount: Math.round((val.price_diff || 0) * 100) })
+ );
+ } else {
+ return acc;
+ }
+ }, Dinero());
+}
+
+export function CalculateJobRpsPc(job, currentRpsDollars) {
+ //TODO Redo this to do total of db price - act price / db price
+ if (!job) {
+ return 0;
+ }
+ const dbPriceSum = job.joblines
+ .filter((j) => !j.ignore)
+ .reduce((acc, val) => {
+ return acc + val.db_price;
+ }, 0);
+ return (currentRpsDollars.getAmount() / dbPriceSum).toFixed(1);
+}
diff --git a/src/util/GetJobTarget.js b/src/util/GetJobTarget.js
new file mode 100644
index 0000000..4919eaa
--- /dev/null
+++ b/src/util/GetJobTarget.js
@@ -0,0 +1,12 @@
+export default function GetJobTarget(group, v_age, 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) return 100;
+ else if (targetPc.length === 1) return targetPc[0].target;
+ else {
+ return 100;
+ }
+}