bananas-commerce-admin
Version:
What's this, an admin for apes?
120 lines • 5.86 kB
JavaScript
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Stack } from "@mui/material";
import { styled, useTheme } from "@mui/material/styles";
import { ChartContainer, ChartsLegend } from "@mui/x-charts";
import { useDrawingArea } from "@mui/x-charts/hooks";
import { PieChart } from "@mui/x-charts/PieChart";
import WidgetCard from "../../../components/WidgetCard";
import { useI18n } from "../../../contexts/I18nContext";
import { getHumanReadablePaymentMethod, } from "../utils/getHumanReadablePaymentMethod";
/**
* Calculate the percentage of a payment method.
*/
function calculatePercentage(count, total) {
return Math.round((count / total) * 100);
}
const StyledTitle = styled("text")(({ theme }) => ({
fill: theme.palette.text.primary,
textAnchor: "middle",
dominantBaseline: "central",
fontWeight: 700,
fontSize: theme.typography.h4.fontSize,
}));
const StyledSubtitle = styled("text")(({ theme }) => ({
fill: theme.palette.text.secondary,
textAnchor: "middle",
dominantBaseline: "central",
fontSize: theme.typography.htmlFontSize,
}));
function PieCenterLabel({ title, subtitle }) {
const { width, height, left, top } = useDrawingArea();
return (React.createElement(React.Fragment, null,
React.createElement(StyledTitle, { x: left + width / 2, y: top - 10 + height / 2 }, title),
React.createElement(StyledSubtitle, { x: left + width / 2, y: top + 25 + height / 2 }, subtitle)));
}
const PaymentMethodWidget = ({ data, sx }) => {
const { t } = useI18n();
const theme = useTheme();
const total = useMemo(() => data.reduce((acc, curr) => acc + curr.count, 0), [data]);
const dataSeries = useMemo(() => {
// Accumulate counts per payment method name using a Map for efficient lookups
const paymentMethodMap = data.reduce((map, item) => {
const { name, color } = getHumanReadablePaymentMethod(item.method, {
disambiguateCards: false,
theme,
});
if (map.has(name)) {
const existingItem = map.get(name);
existingItem.count += item.count;
existingItem.value += item.count;
}
else {
map.set(name, { ...item, color, name, value: item.count, label: name });
}
return map;
}, new Map());
// Convert Map values to an array and sort by count in descending order
const sortedItems = Array.from(paymentMethodMap.values()).sort((a, b) => b.count - a.count);
// Take the top 5 items and accumulate the rest into an 'Other' category
const topItems = sortedItems.slice(0, 5);
const otherItems = sortedItems.slice(5);
if (otherItems.length > 0) {
const otherTotals = otherItems.reduce((totals, item) => {
totals.count += item.count;
totals.value += item.value;
return totals;
}, { count: 0, value: 0 });
topItems.push({
method: "other",
name: "Other",
count: otherTotals.count,
value: otherTotals.value,
color: theme.palette.grey[300],
label: "Other",
});
}
// Map the items to include percentage labels
return topItems.map((item) => {
const percentage = calculatePercentage(item.count, total);
return { ...item, label: `${item.name} ${percentage}%` };
});
}, [data, total, theme]);
const [highlighted, setHighlighted] = useState(dataSeries?.[0]);
// Update highlight when data changes.
useEffect(() => {
setHighlighted(dataSeries?.[0]);
}, [data, total, highlighted]);
const handleHighlightChange = useCallback((highlight) => {
if (typeof highlight?.dataIndex === "number") {
setHighlighted(dataSeries[highlight.dataIndex]);
}
else {
setHighlighted(dataSeries?.[0]);
}
}, [data, total, highlighted]);
const highlightedPercentage = useMemo(() => {
if (highlighted == null)
return null;
const percentage = calculatePercentage(highlighted.count, total);
if (Number.isNaN(percentage) || !Number.isFinite(percentage))
return null;
return percentage;
}, [data, total, highlighted]);
return (React.createElement(WidgetCard, { gridColumn: { md: "span 6", sm: "span 1" }, gridRow: "span 2", sx: sx, title: t("Payment Methods") },
React.createElement(Stack, { alignItems: "center", direction: { xs: "column", sm: "row" }, justifyContent: "center" },
React.createElement(PieChart, { height: 275, margin: { top: 0, right: 0, bottom: 0, left: 0 }, series: [
{
data: dataSeries,
innerRadius: 75,
highlightScope: { fade: "global", highlight: "item" },
},
], slotProps: {
legend: {
hidden: true,
},
}, sx: { py: { xs: 1, sm: 3 } }, width: 275, onHighlightChange: handleHighlightChange }, highlighted != null && (React.createElement(PieCenterLabel, { subtitle: highlighted.name, title: `${highlightedPercentage}%` }))),
React.createElement(ChartContainer, { dataset: dataSeries, height: 8 + dataSeries.length * 32, margin: { top: 0, right: 0, bottom: 0, left: 0 }, series: dataSeries.map(({ label, color }) => ({ type: "bar", label, color })), width: 200 },
React.createElement(ChartsLegend, { direction: "column", position: { vertical: "middle", horizontal: "left" } })))));
};
export default PaymentMethodWidget;
//# sourceMappingURL=PaymentMethodWidget.js.map