138 lines
3.9 KiB
JavaScript
138 lines
3.9 KiB
JavaScript
import { Card } from "antd";
|
|
import Dinero from "dinero.js";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Cell, Pie, PieChart, ResponsiveContainer, Sector } from "recharts";
|
|
import DashboardRefreshRequired from "../refresh-required.component";
|
|
|
|
export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
|
|
const { t } = useTranslation();
|
|
if (!data) return null;
|
|
if (!data.monthly_sales) return <DashboardRefreshRequired {...cardProps} />;
|
|
|
|
const partData = {};
|
|
|
|
data.monthly_sales.forEach((job) => {
|
|
job.joblines.forEach((jobline) => {
|
|
if (!jobline.part_type) return;
|
|
if (!partData[jobline.part_type]) partData[jobline.part_type] = Dinero();
|
|
partData[jobline.part_type] = partData[jobline.part_type].add(
|
|
Dinero({ amount: Math.round((jobline.act_price || 0) * 100) }).multiply(jobline.part_qty || 0)
|
|
);
|
|
});
|
|
});
|
|
|
|
const chartData = Object.keys(partData).map((key) => {
|
|
return {
|
|
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
|
|
value: partData[key].getAmount() / 100,
|
|
color: pieColor(key.toUpperCase())
|
|
};
|
|
});
|
|
|
|
return (
|
|
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
|
|
<div style={{ height: "100%" }}>
|
|
<ResponsiveContainer width="100%" height="100%" minHeight={100}>
|
|
<PieChart margin={0} padding={0}>
|
|
<Pie
|
|
data={chartData}
|
|
shape={renderActiveShape}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius="60%"
|
|
// outerRadius={80}
|
|
fill="#8884d8"
|
|
dataKey="value"
|
|
>
|
|
{chartData.map((entry, index) => (
|
|
<Cell key={`cell-${index}`} fill={entry.color} />
|
|
))}
|
|
</Pie>
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
export const DashboardMonthlyRevenueGraphGql = `
|
|
|
|
`;
|
|
const pieColor = (type) => {
|
|
if (type === "PAA") return "darkgreen";
|
|
else if (type === "PAC") return "green";
|
|
else if (type === "PAE") return "gold";
|
|
else if (type === "PAG") return "seafoam";
|
|
else if (type === "PAL") return "chartreuse";
|
|
else if (type === "PAM") return "magenta";
|
|
else if (type === "PAN") return "crimson";
|
|
else if (type === "PAO") return "gold";
|
|
else if (type === "PAP") return "crimson";
|
|
else if (type === "PAR") return "indigo";
|
|
else if (type === "PAS") return "dodgerblue";
|
|
else if (type === "PASL") return "dodgerblue";
|
|
return "slategray";
|
|
};
|
|
|
|
const renderActiveShape = (props) => {
|
|
// const RADIAN = Math.PI / 180;
|
|
const {
|
|
cx,
|
|
cy,
|
|
// midAngle,
|
|
innerRadius,
|
|
outerRadius,
|
|
startAngle,
|
|
endAngle,
|
|
fill,
|
|
payload,
|
|
// percent,
|
|
value,
|
|
isActive
|
|
} = 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>
|
|
{isActive && (
|
|
<>
|
|
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
|
|
{payload.name}
|
|
</text>
|
|
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
|
|
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
|
</text>
|
|
</>
|
|
)}
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
innerRadius={innerRadius}
|
|
outerRadius={outerRadius}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
fill={fill}
|
|
/>
|
|
{isActive && (
|
|
<Sector
|
|
cx={cx}
|
|
cy={cy}
|
|
startAngle={startAngle}
|
|
endAngle={endAngle}
|
|
innerRadius={outerRadius + 6}
|
|
outerRadius={outerRadius + 10}
|
|
fill={fill}
|
|
/>
|
|
)}
|
|
</g>
|
|
);
|
|
};
|