import { useState, useEffect } from 'react'; import { useToast } from '../../components/ui/toast/ToastContainer'; import { getCreditTransactions, CreditTransaction as BillingTransaction } from '../../services/billing.api'; import { useBillingStore } from '../../store/billingStore'; import { Card } from '../../components/ui/card'; import Badge from '../../components/ui/badge/Badge'; import { CompactPagination } from '../ui/pagination'; import { formatDateTime } from '../../utils/date'; // Credit costs per operation (copied from Billing usage page) const CREDIT_COSTS: Record = { clustering: { cost: 10, description: 'Per clustering request' }, idea_generation: { cost: 15, description: 'Per cluster → ideas request' }, content_generation: { cost: '1 per 100 words', description: 'Per 100 words generated' }, image_prompt_extraction: { cost: 2, description: 'Per content piece' }, image_generation: { cost: 5, description: 'Per image generated' }, linking: { cost: 8, description: 'Per content piece' }, optimization: { cost: '1 per 200 words', description: 'Per 200 words optimized' }, site_structure_generation: { cost: 50, description: 'Per site blueprint' }, site_page_generation: { cost: 20, description: 'Per page generated' }, }; interface BillingUsagePanelProps { showOnlyActivity?: boolean; } export default function BillingUsagePanel({ showOnlyActivity = false }: BillingUsagePanelProps) { const toast = useToast(); const [transactions, setTransactions] = useState([]); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const { balance, usageLimits, loadBalance, loadUsageLimits } = useBillingStore(); useEffect(() => { loadUsage(); loadBalance(); loadUsageLimits(); }, [loadBalance, loadUsageLimits]); const loadUsage = async () => { try { setLoading(true); const txnData = await getCreditTransactions(); setTransactions(txnData.results || []); } catch (error: any) { toast.error(`Failed to load credit usage: ${error.message}`); } finally { setLoading(false); } }; const totalPages = Math.max(1, Math.ceil(transactions.length / pageSize)); const paginated = transactions.slice((page - 1) * pageSize, page * pageSize); if (loading) { return (
Loading credit usage...
); } // If only showing activity table, render just that if (showOnlyActivity) { return (

Credit Activity

Complete history of credit transactions

{paginated.map((txn) => ( ))} {transactions.length === 0 && ( )}
Date Type Amount Description Reference
{formatDateTime(txn.created_at)} = 0 ? 'success' : 'danger'}>{txn.transaction_type} = 0 ? 'text-success-600 dark:text-success-400' : 'text-error-600 dark:text-error-400'}`}> {txn.amount >= 0 ? '+' : ''}{txn.amount} {txn.description} {txn.reference_id || '-'}
No transactions yet
{transactions.length > 0 && (
Showing {(page - 1) * pageSize + 1}-{Math.min(page * pageSize, transactions.length)} of {transactions.length}
setPage(p)} onPageSizeChange={(size) => { setPageSize(size); setPage(1); }} />
)}
); } return (
{balance && (

Current Balance

{(balance?.credits ?? 0).toLocaleString()}

Available credits

Monthly Allocation

{(balance?.plan_credits_per_month ?? 0).toLocaleString()}

{(balance as any)?.subscription_plan || 'No plan'}

Status

{(balance as any)?.subscription_status || 'No subscription'}
)} {usageLimits && (

Plan Limits

Monthly Credits
{usageLimits.plan_credits_per_month?.toLocaleString?.() || 0}
{usageLimits.credits_used_this_month?.toLocaleString?.() || 0} used
Remaining
{usageLimits.credits_remaining?.toLocaleString?.() || 0}
{usageLimits.approaching_limit && (
Approaching limit
)}
Usage %
{usageLimits.percentage_used?.toFixed?.(0) || 0}%
)}

Credit Costs per Operation

Understanding how credits are consumed for each operation type

{Object.entries(CREDIT_COSTS).map(([operation, info]) => (
{operation.replace(/_/g, ' ')}
{info.description}
{typeof info.cost === 'number' ? `${info.cost} credits` : info.cost}
))}

Credit Activity

{paginated.map((txn) => ( ))} {transactions.length === 0 && ( )}
Date Type Amount Description Reference
{formatDateTime(txn.created_at)} = 0 ? 'success' : 'error'}>{txn.transaction_type} = 0 ? 'text-success-600 dark:text-success-400' : 'text-error-600 dark:text-error-400'}`}> {txn.amount >= 0 ? '+' : ''}{txn.amount} {txn.description} {txn.reference_id || '-'}
No transactions yet
{transactions.length > 0 && (
Showing {(page - 1) * pageSize + 1}-{Math.min(page * pageSize, transactions.length)} of {transactions.length}
setPage(p)} onPageSizeChange={(size) => { setPageSize(size); setPage(1); }} />
)}
); }