/** * Credit Insights Charts Component * Displays credit usage analytics with visual charts * - Donut chart: Credits by operation type * - Line chart: Daily credit usage timeline * - Bar chart: Top credit-consuming operations */ import Chart from 'react-apexcharts'; import { ApexOptions } from 'apexcharts'; import { Card } from '../ui/card'; import { ActivityIcon, TrendingUpIcon, PieChartIcon, BarChart3Icon } from '../../icons'; import type { UsageAnalytics } from '../../services/billing.api'; interface CreditInsightsChartsProps { analytics: UsageAnalytics | null; loading?: boolean; period: number; } // Friendly names for operation types const OPERATION_LABELS: Record = { content_generation: 'Content Generation', image_generation: 'Image Generation', keyword_clustering: 'Keyword Clustering', content_analysis: 'Content Analysis', subscription: 'Subscription', purchase: 'Credit Purchase', refund: 'Refund', adjustment: 'Adjustment', grant: 'Credit Grant', deduction: 'Deduction', }; const CHART_COLORS = [ 'var(--color-brand-500)', 'var(--color-info-500)', 'var(--color-success-500)', 'var(--color-warning-500)', 'var(--color-error-500)', 'var(--color-info-500)', '#6366f1', // indigo '#ec4899', // pink '#14b8a6', // teal '#f97316', // orange ]; export default function CreditInsightsCharts({ analytics, loading, period }: CreditInsightsChartsProps) { if (loading) { return (
{[1, 2, 3, 4].map((i) => (
))}
); } if (!analytics) { return (

No analytics data available

); } // Prepare data for donut chart (credits by operation type) const usageByType = analytics.usage_by_type.filter(item => Math.abs(item.total) > 0); const donutLabels = usageByType.map(item => OPERATION_LABELS[item.transaction_type] || item.transaction_type.replace(/_/g, ' ')); const donutSeries = usageByType.map(item => Math.abs(item.total)); const donutOptions: ApexOptions = { chart: { type: 'donut', fontFamily: 'Outfit, sans-serif', }, labels: donutLabels, colors: CHART_COLORS.slice(0, donutLabels.length), legend: { position: 'bottom', fontFamily: 'Outfit', labels: { colors: 'var(--color-gray-600)', }, }, plotOptions: { pie: { donut: { size: '65%', labels: { show: true, name: { show: true, fontSize: '14px', fontFamily: 'Outfit', color: 'var(--color-gray-600)', }, value: { show: true, fontSize: '24px', fontFamily: 'Outfit', fontWeight: 600, color: 'var(--color-gray-900)', formatter: (val: string) => parseInt(val).toLocaleString(), }, total: { show: true, label: 'Total Credits', fontSize: '14px', fontFamily: 'Outfit', color: 'var(--color-gray-600)', formatter: () => analytics.total_usage.toLocaleString(), }, }, }, }, }, dataLabels: { enabled: false, }, tooltip: { y: { formatter: (val: number) => `${val.toLocaleString()} credits`, }, }, responsive: [ { breakpoint: 480, options: { chart: { width: 300, }, legend: { position: 'bottom', }, }, }, ], }; // Prepare data for timeline chart (daily usage) const dailyData = analytics.daily_usage || []; const timelineCategories = dailyData.map(d => { const date = new Date(d.date); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); }); const usageSeries = dailyData.map(d => Math.abs(d.usage)); const purchasesSeries = dailyData.map(d => d.purchases); const timelineOptions: ApexOptions = { chart: { type: 'area', fontFamily: 'Outfit, sans-serif', height: 300, toolbar: { show: false, }, zoom: { enabled: false, }, }, colors: ['var(--color-brand-500)', 'var(--color-success-500)'], dataLabels: { enabled: false, }, stroke: { curve: 'smooth', width: 2, }, fill: { type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.4, opacityTo: 0.1, stops: [0, 90, 100], }, }, xaxis: { categories: timelineCategories, axisBorder: { show: false, }, axisTicks: { show: false, }, labels: { style: { colors: 'var(--color-gray-500)', fontFamily: 'Outfit', }, rotate: -45, rotateAlways: dailyData.length > 14, }, }, yaxis: { labels: { style: { colors: 'var(--color-gray-500)', fontFamily: 'Outfit', }, formatter: (val: number) => val.toLocaleString(), }, }, grid: { borderColor: 'var(--color-gray-200)', strokeDashArray: 4, }, legend: { position: 'top', horizontalAlign: 'right', fontFamily: 'Outfit', labels: { colors: 'var(--color-gray-600)', }, }, tooltip: { y: { formatter: (val: number) => `${val.toLocaleString()} credits`, }, }, }; const timelineSeries = [ { name: 'Credits Used', data: usageSeries }, { name: 'Credits Added', data: purchasesSeries }, ]; // Prepare data for bar chart (top operations by count) const operationsByCount = [...analytics.usage_by_type] .filter(item => item.count > 0) .sort((a, b) => b.count - a.count) .slice(0, 8); const barCategories = operationsByCount.map(item => OPERATION_LABELS[item.transaction_type] || item.transaction_type.replace(/_/g, ' ') ); const barSeries = operationsByCount.map(item => item.count); const barOptions: ApexOptions = { chart: { type: 'bar', fontFamily: 'Outfit, sans-serif', height: 300, toolbar: { show: false, }, }, colors: ['var(--color-info-500)'], plotOptions: { bar: { horizontal: true, borderRadius: 4, barHeight: '60%', }, }, dataLabels: { enabled: false, }, xaxis: { categories: barCategories, labels: { style: { colors: 'var(--color-gray-500)', fontFamily: 'Outfit', }, }, }, yaxis: { labels: { style: { colors: 'var(--color-gray-600)', fontFamily: 'Outfit', }, }, }, grid: { borderColor: 'var(--color-gray-200)', strokeDashArray: 4, }, tooltip: { y: { formatter: (val: number) => `${val.toLocaleString()} operations`, }, }, }; // Summary stats const avgDailyUsage = dailyData.length > 0 ? Math.round(dailyData.reduce((sum, d) => sum + Math.abs(d.usage), 0) / dailyData.length) : 0; const peakUsage = dailyData.length > 0 ? Math.max(...dailyData.map(d => Math.abs(d.usage))) : 0; const topOperation = usageByType.length > 0 ? usageByType.reduce((max, item) => Math.abs(item.total) > Math.abs(max.total) ? item : max, usageByType[0]) : null; return (
{/* Summary Stats */}
Avg Daily Usage
{avgDailyUsage.toLocaleString()}
credits/day
Peak Usage
{peakUsage.toLocaleString()}
credits in one day
Top Operation
{topOperation ? (OPERATION_LABELS[topOperation.transaction_type] || topOperation.transaction_type.replace(/_/g, ' ')) : 'N/A' }
{topOperation ? `${Math.abs(topOperation.total).toLocaleString()} credits` : ''}
{/* Charts Grid */}
{/* Usage by Type - Donut Chart */}

Credits by Type

{donutSeries.length > 0 ? ( ) : (

No usage data for this period

)}
{/* Operations by Count - Bar Chart */}

Operations Count

{barSeries.length > 0 ? ( ) : (

No operations in this period

)}
{/* Daily Timeline - Full Width */}

Credit Activity Timeline

Last {period} days
{dailyData.length > 0 ? ( ) : (

No daily activity data available

)}
); }