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 { HighlightItemData } from "@mui/x-charts/context/HighlightedProvider";
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 { ContribComponent } from "../../../types";
import { PaymentMethodItem } from "../../../types/dashboard";
import {
getHumanReadablePaymentMethod,
HumanReadablePaymentMethod,
} from "../utils/getHumanReadablePaymentMethod";
/**
* Calculate the percentage of a payment method.
*/
function calculatePercentage(count: number, total: number): number {
return Math.round((count / total) * 100);
}
interface PaymentMethod extends HumanReadablePaymentMethod, PaymentMethodItem {
label: string;
value: number;
}
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 }: { title: string; subtitle: string }) {
const { width, height, left, top } = useDrawingArea();
return (
<>
{title}
{subtitle}
>
);
}
const PaymentMethodWidget: ContribComponent = ({ 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: HighlightItemData | null) => {
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: number = calculatePercentage(highlighted.count, total);
if (Number.isNaN(percentage) || !Number.isFinite(percentage)) return null;
return percentage;
}, [data, total, highlighted]);
return (
{highlighted != null && (
)}
({ type: "bar", label, color }))}
width={200}
>
);
};
export default PaymentMethodWidget;