import React, {useEffect, useState} from 'react'; import Chart from 'react-apexcharts'; import {ApexOptions} from 'apexcharts'; import apiFetch from '@wordpress/api-fetch'; import {getDate} from '@wordpress/date'; import { formatTimestamp } from '@givewp/admin/common'; /** * @since 4.4.0 */ type TimeSeriesChartProps = { endpoint: string; amountFormatter: Intl.NumberFormat; title?: string; }; /** * @since 4.4.0 */ type Donation = { createdAt: string; amount: { value: string; }; }; /** * @since 4.4.0 */ type DataPoint = { x: string; y: number; }; /** * @since 4.4.0 * Generates a range of dates around a target date for displaying a single donation graph. */ const getCenteredGraphRange = (targetDate: string) => { const result = []; const date = new Date(targetDate); for (let i = -3; i <= 3; i++) { const currentDate = new Date(date); currentDate.setDate(date.getDate() + i); result.push(currentDate.toISOString().split('T')[0]); } return result; }; /** * @since 4.4.0 */ const normalizeData = (donations: Donation[]): DataPoint[] => { const map = new Map(); // Group donations by date with sum amounts - fill missing dates with 0. donations.forEach((donation) => { const date = getDate(donation.createdAt); const amount = parseFloat(donation.amount.value); map.set(date.toISOString().split('T')[0], (map.get(date.toISOString().split('T')[0]) || 0) + amount); }); // Set graph range & points for single donations. if (map.size === 1) { const donationDate = donations[0]?.createdAt.split(' ')[0]; const graphRange = getCenteredGraphRange(donationDate); return graphRange.map((date) => ({ x: date, y: map.get(date) || 0, })); } // Convert to sorted array of data points. return Array.from(map.entries()) .map(([date, amount]) => ({ x: date, y: amount, })) .sort((a, b) => a.x.localeCompare(b.x)); }; /** * @since 4.13.0 updated the date format * @since 4.4.0 */ export default function TimeSeriesChart({endpoint, amountFormatter, title = ''}: TimeSeriesChartProps) { const [series, setSeries] = useState([{name: title, data: []}]); useEffect(() => { apiFetch({path: endpoint}).then((data) => { const normalized = normalizeData(data); setSeries([{name: title, data: normalized}]); }); }, [endpoint]); const options: ApexOptions = { chart: { type: 'area' as const, toolbar: {show: false}, zoom: {enabled: false}, }, xaxis: { type: 'datetime', labels: { formatter: (val) => formatTimestamp(val, false), }, }, yaxis: { min: 0, labels: { formatter: (val) => amountFormatter.format(val), }, }, dataLabels: {enabled: false}, stroke: { curve: 'smooth', width: 2, colors: ['#60a1e2'], }, fill: { type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.3, opacityTo: 0, stops: [0, 100], }, }, tooltip: { x: { format: 'MMM dd, yyyy', }, }, }; return ; }