Updated parts status graph and formatting. BOD-328
This commit is contained in:
@@ -9860,6 +9860,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>nostatus</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
|
||||
@@ -145,7 +145,7 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Col {...colBreakPoints}>
|
||||
<JobDetailCardsPartsComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
@@ -163,7 +163,7 @@ export function JobDetailCards({ setPrintCenterContext }) {
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Col {...colBreakPoints}>
|
||||
<JobDetailCardsDamageComponent
|
||||
loading={loading}
|
||||
data={data ? data.jobs_by_pk : null}
|
||||
|
||||
@@ -1,141 +1,17 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Pie, PieChart, Sector } from "recharts";
|
||||
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
|
||||
import CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
export default function JobDetailCardsPartsComponent({ loading, data }) {
|
||||
const { t } = useTranslation();
|
||||
const { joblines_status } = data;
|
||||
// console.log(
|
||||
// "JobDetailCardsPartsComponent -> joblines_stats",
|
||||
// joblines_status
|
||||
// );
|
||||
|
||||
const memoizedData = useMemo(() => Calculatedata(joblines_status), [
|
||||
joblines_status,
|
||||
]);
|
||||
|
||||
const [state, setState] = useState({ activeIndex: 0 });
|
||||
|
||||
const onPieEnter = (data, index) => {
|
||||
setState({
|
||||
activeIndex: index,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
|
||||
<PieChart width={400} height={400}>
|
||||
<Pie
|
||||
activeIndex={state.activeIndex}
|
||||
activeShape={renderActiveShape}
|
||||
data={memoizedData}
|
||||
cx={200}
|
||||
cy={200}
|
||||
innerRadius={60}
|
||||
outerRadius={80}
|
||||
fill="#8884d8"
|
||||
dataKey="value"
|
||||
onMouseEnter={onPieEnter}
|
||||
/>
|
||||
</PieChart>
|
||||
<PartsStatusPie joblines_status={joblines_status} />
|
||||
</CardTemplate>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Calculatedata = (data) => {
|
||||
if (data.length > 0) {
|
||||
const statusMapping = {};
|
||||
data.map((i) => {
|
||||
if (!statusMapping[i.status])
|
||||
statusMapping[i.status] = { name: i.status || "No Status*", value: 0 };
|
||||
statusMapping[i.status].value = statusMapping[i.status].value + i.count;
|
||||
return null;
|
||||
});
|
||||
return Object.keys(statusMapping).map((key) => {
|
||||
return statusMapping[key];
|
||||
});
|
||||
} else {
|
||||
return [
|
||||
{ name: "Group A", value: 400 },
|
||||
{ name: "Group B", value: 300 },
|
||||
{ name: "Group C", value: 300 },
|
||||
{ name: "Group D", value: 200 },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
const renderActiveShape = (props) => {
|
||||
const RADIAN = Math.PI / 180;
|
||||
const {
|
||||
cx,
|
||||
cy,
|
||||
midAngle,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
fill,
|
||||
payload,
|
||||
percent,
|
||||
value,
|
||||
} = props;
|
||||
const sin = Math.sin(-RADIAN * midAngle);
|
||||
const cos = Math.cos(-RADIAN * midAngle);
|
||||
const sx = cx + (outerRadius + 10) * cos;
|
||||
const sy = cy + (outerRadius + 10) * sin;
|
||||
const mx = cx + (outerRadius + 30) * cos;
|
||||
const my = cy + (outerRadius + 30) * sin;
|
||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
const ey = my;
|
||||
const textAnchor = cos >= 0 ? "start" : "end";
|
||||
|
||||
return (
|
||||
<g>
|
||||
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
|
||||
{payload.name}
|
||||
</text>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
innerRadius={innerRadius}
|
||||
outerRadius={outerRadius}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
fill={fill}
|
||||
/>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
startAngle={startAngle}
|
||||
endAngle={endAngle}
|
||||
innerRadius={outerRadius + 6}
|
||||
outerRadius={outerRadius + 10}
|
||||
fill={fill}
|
||||
/>
|
||||
<path
|
||||
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
||||
stroke={fill}
|
||||
fill="none"
|
||||
/>
|
||||
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
textAnchor={textAnchor}
|
||||
fill="#333"
|
||||
>{`Count: ${value}`}</text>
|
||||
<text
|
||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||
y={ey}
|
||||
dy={18}
|
||||
textAnchor={textAnchor}
|
||||
fill="#999"
|
||||
>
|
||||
{`(${(percent * 100).toFixed(2)}%)`}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,22 +6,15 @@ import CardTemplate from "./job-detail-cards.template.component";
|
||||
|
||||
export default function JobDetailCardsTotalsComponent({ loading, data }) {
|
||||
const { t } = useTranslation();
|
||||
let totals;
|
||||
|
||||
try {
|
||||
totals = JSON.parse(data.job_totals);
|
||||
} catch (error) {
|
||||
console.log("Error in CardsTotal component", error);
|
||||
}
|
||||
|
||||
return (
|
||||
<CardTemplate loading={loading} title={t("jobs.labels.cards.totals")}>
|
||||
{totals ? (
|
||||
{data.job_totals ? (
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
<Statistic
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.labels.total_repairs")}
|
||||
value={Dinero(totals.totals.total_repairs).toFormat()}
|
||||
value={Dinero(data.job_totals.totals.total_repairs).toFormat()}
|
||||
/>
|
||||
<Statistic
|
||||
className="imex-flex-row__margin-large"
|
||||
@@ -33,7 +26,7 @@ export default function JobDetailCardsTotalsComponent({ loading, data }) {
|
||||
<Statistic
|
||||
className="imex-flex-row__margin-large"
|
||||
title={t("jobs.labels.net_repairs")}
|
||||
value={Dinero(totals.totals.net_repairs).toFormat()}
|
||||
value={Dinero(data.job_totals.totals.net_repairs).toFormat()}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -1,25 +1,93 @@
|
||||
import React from "react";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export default function PartsStatusPie({ partsList }) {
|
||||
return <div>Parts Pie</div>;
|
||||
//const [pieData, setPieData] = useState([]);
|
||||
export function PartsStatusPie({ bodyshop, joblines_status }) {
|
||||
const { t } = useTranslation();
|
||||
const pieColor = useCallback(
|
||||
(status) => {
|
||||
if (status === bodyshop.md_order_statuses.default_ordered)
|
||||
return "lightgreen";
|
||||
if (status === bodyshop.md_order_statuses.default_bo) return "crimson";
|
||||
|
||||
// const result = partsList
|
||||
// ? partsList.reduce((names, name) => {
|
||||
// const val = name || "?";
|
||||
// const count = names[val] || 0;
|
||||
// names[val] = count + 1;
|
||||
// return names;
|
||||
// }, {})
|
||||
// : {};
|
||||
if (status === bodyshop.md_order_statuses.default_canceled)
|
||||
return "dodgerblue";
|
||||
|
||||
// const pieData = Object.keys(result).map((i) => {
|
||||
// return {
|
||||
// id: i,
|
||||
// label: i,
|
||||
// value: result[i],
|
||||
// };
|
||||
// });
|
||||
if (status === bodyshop.md_order_statuses.default_returned)
|
||||
return "powderblue";
|
||||
if (status === bodyshop.md_order_statuses.default_received)
|
||||
return "seagreen";
|
||||
|
||||
// return <div>{JSON.stringify(pieData)}</div>;
|
||||
return "slategray";
|
||||
},
|
||||
[
|
||||
bodyshop.md_order_statuses.default_ordered,
|
||||
bodyshop.md_order_statuses.default_bo,
|
||||
bodyshop.md_order_statuses.default_canceled,
|
||||
bodyshop.md_order_statuses.default_returned,
|
||||
bodyshop.md_order_statuses.default_received,
|
||||
]
|
||||
);
|
||||
|
||||
const Calculatedata = useCallback(
|
||||
(data) => {
|
||||
if (data.length > 0) {
|
||||
const statusMapping = {};
|
||||
data.map((i) => {
|
||||
if (!statusMapping[i.status])
|
||||
statusMapping[i.status] = {
|
||||
name: i.status || t("joblines.labels.nostatus"),
|
||||
value: 0,
|
||||
color: pieColor(i.status),
|
||||
};
|
||||
statusMapping[i.status].value =
|
||||
statusMapping[i.status].value + i.count;
|
||||
return null;
|
||||
});
|
||||
return Object.keys(statusMapping).map((key) => {
|
||||
return statusMapping[key];
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
[pieColor, t]
|
||||
);
|
||||
|
||||
const memoizedData = useMemo(() => Calculatedata(joblines_status), [
|
||||
joblines_status,
|
||||
Calculatedata,
|
||||
]);
|
||||
|
||||
console.log("PartsStatusPie -> memoizedData", memoizedData);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ResponsiveContainer width="100%" height={175}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={memoizedData}
|
||||
innerRadius={40}
|
||||
outerRadius={50}
|
||||
fill="#8884d8"
|
||||
paddingAngle={5}
|
||||
dataKey="value"
|
||||
label={(entry) => `${entry.name} - ${entry.value}`}
|
||||
labelLine
|
||||
>
|
||||
{memoizedData.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(PartsStatusPie);
|
||||
|
||||
@@ -635,7 +635,8 @@
|
||||
},
|
||||
"labels": {
|
||||
"edit": "Edit Line",
|
||||
"new": "New Line"
|
||||
"new": "New Line",
|
||||
"nostatus": "No Status"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Job line created successfully.",
|
||||
|
||||
@@ -635,7 +635,8 @@
|
||||
},
|
||||
"labels": {
|
||||
"edit": "Línea de edición",
|
||||
"new": "Nueva línea"
|
||||
"new": "Nueva línea",
|
||||
"nostatus": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
|
||||
@@ -635,7 +635,8 @@
|
||||
},
|
||||
"labels": {
|
||||
"edit": "Ligne d'édition",
|
||||
"new": "Nouvelle ligne"
|
||||
"new": "Nouvelle ligne",
|
||||
"nostatus": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,8 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE VIEW \"public\".\"joblines_status\" AS \n SELECT j.jobid,\n
|
||||
\ j.status,\n count(1) AS count,\n j.part_type\n FROM joblines j\n
|
||||
\ where j.part_type is not null\n GROUP BY j.jobid, j.status, j.part_type\n
|
||||
\ ;"
|
||||
type: run_sql
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,8 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE VIEW \"public\".\"joblines_status\" AS \n SELECT j.jobid,\n
|
||||
\ j.status,\n count(1) AS count,\n j.part_type\n FROM joblines j\n
|
||||
\ where j.part_type is not null\n GROUP BY j.jobid, j.status, j.part_type\n
|
||||
\ ;"
|
||||
type: run_sql
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,8 @@
|
||||
- args:
|
||||
cascade: true
|
||||
read_only: false
|
||||
sql: "CREATE OR REPLACE VIEW \"public\".\"joblines_status\" AS \n SELECT j.jobid,\n
|
||||
\ j.status,\n count(1) AS count,\n j.part_type\n FROM joblines j\n
|
||||
\ where j.part_type is not null and j.part_type <> 'PAE'\n GROUP BY j.jobid,
|
||||
j.status, j.part_type\n ;"
|
||||
type: run_sql
|
||||
Reference in New Issue
Block a user