/** * Admin Billing Management Page * Admin-only interface for managing credits, billing, and user accounts */ import React, { useState, useEffect } from 'react'; import PageMeta from '../../components/common/PageMeta'; import ComponentCard from '../../components/common/ComponentCard'; import EnhancedMetricCard from '../../components/dashboard/EnhancedMetricCard'; import { useToast } from '../../components/ui/toast/ToastContainer'; import { fetchAPI } from '../../services/api'; import Button from '../../components/ui/button/Button'; import Badge from '../../components/ui/badge/Badge'; import { BoltIcon, UserIcon, DollarLineIcon, PlugInIcon, CheckCircleIcon, TimeIcon } from '../../icons'; interface UserAccount { id: number; email: string; username?: string; account_name?: string; credits: number; subscription_plan?: string; is_active: boolean; date_joined: string; } interface CreditCostConfig { id: number; operation_type: string; display_name: string; credits_cost: number; unit?: string; description?: string; is_active: boolean; created_at: string; } interface CreditPackageItem { id: number; name: string; slug: string; credits: number; price: string; discount_percentage: number; is_featured: boolean; description?: string; is_active?: boolean; sort_order?: number; } interface SystemStats { total_users: number; active_users: number; total_credits_issued: number; total_credits_used: number; } const AdminBilling: React.FC = () => { const toast = useToast(); const [stats, setStats] = useState(null); const [users, setUsers] = useState([]); const [creditConfigs, setCreditConfigs] = useState([]); const [creditPackages, setCreditPackages] = useState([]); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState<'overview' | 'users' | 'pricing' | 'packages'>('overview'); const [searchTerm, setSearchTerm] = useState(''); const [selectedUser, setSelectedUser] = useState(null); const [creditAmount, setCreditAmount] = useState(''); const [adjustmentReason, setAdjustmentReason] = useState(''); useEffect(() => { loadData(); }, []); const loadData = async () => { try { setLoading(true); const [statsData, usersData, configsData] = await Promise.all([ // Admin billing stats (modules admin endpoints) fetchAPI('/v1/admin/billing/stats/'), // Admin users with credits fetchAPI('/v1/admin/users/'), // Admin credit costs (modules billing) fetchAPI('/v1/admin/credit-costs/'), ]); const packagesData = await fetchAPI('/v1/billing/credit-packages/'); setStats(statsData); setUsers(usersData.results || []); setCreditConfigs(configsData.results || []); setCreditPackages(packagesData.results || []); } catch (error: any) { toast?.error(error?.message || 'Failed to load admin data'); } finally { setLoading(false); } }; const handleAdjustCredits = async () => { if (!selectedUser || !creditAmount) { toast?.error('Please select a user and enter amount'); return; } try { await fetchAPI(`/v1/admin/users/${selectedUser.id}/adjust-credits/`, { method: 'POST', body: JSON.stringify({ amount: parseInt(creditAmount), reason: adjustmentReason || 'Admin adjustment', }), }); toast?.success(`Credits adjusted for ${selectedUser.username}`); setCreditAmount(''); setAdjustmentReason(''); setSelectedUser(null); loadData(); } catch (error: any) { toast?.error(error?.message || 'Failed to adjust credits'); } }; const handleUpdateCreditCost = async (configId: number, newCost: number) => { try { await fetchAPI('/v1/admin/credit-costs/', { method: 'POST', body: JSON.stringify({ updates: [{ id: configId, cost: newCost }] }), }); toast?.success('Credit cost updated successfully'); loadData(); } catch (error: any) { toast?.error(error?.message || 'Failed to update credit cost'); } }; const filteredUsers = users.filter(user => (user.email || '').toLowerCase().includes(searchTerm.toLowerCase()) || (user.username || '').toLowerCase().includes(searchTerm.toLowerCase()) || (user.account_name || '').toLowerCase().includes(searchTerm.toLowerCase()) ); const formatLabel = (value?: string) => (value || '') .split('_') .map((w) => (w ? w[0].toUpperCase() + w.slice(1) : '')) .join(' ') .trim(); const updateLocalCost = (id: number, value: string) => { setCreditConfigs((prev) => prev.map((c) => c.id === id ? { ...c, credits_cost: value === '' ? ('' as any) : Number(value) } : c ) ); }; if (loading) { return (

Loading admin data...

); } return (

Billing Management

Admin controls for credits, pricing, and user billing

{/* System Stats */}
} accentColor="blue" /> } accentColor="green" /> } accentColor="orange" /> } accentColor="purple" />
{/* Tabs */}
{/* Tab Content */} {activeTab === 'overview' && (
Activity log coming soon
)} {activeTab === 'users' && (
setSearchTerm(e.target.value)} />
{filteredUsers.map((user) => ( ))}
User Plan Credits Actions
{user.username}
{user.email}
{user.subscription_plan || 'Free'} {user.credits}
{selectedUser ? (
{selectedUser.username}
Current: {selectedUser.credits} credits
setCreditAmount(e.target.value)} />
setAdjustmentReason(e.target.value)} />
) : (
Select a user to adjust credits
)}
)} {activeTab === 'pricing' && (
{creditConfigs.map((config) => ( ))}
Operation Display Name Credits Cost Unit Description Actions
{config.operation_type} {config.display_name || formatLabel(config.operation_type)} updateLocalCost(config.id, e.target.value)} className="w-24 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-800 dark:text-white" /> {formatLabel(config.unit)} {config.description || '—'}
)} {activeTab === 'packages' && (
{creditPackages.map((pkg) => (
{pkg.credits.toLocaleString()}
credits
${pkg.price}
{pkg.discount_percentage > 0 && (
Save {pkg.discount_percentage}%
)}
{pkg.is_active ? 'Active' : 'Inactive'} {pkg.is_featured && ( Featured )}
))} {creditPackages.length === 0 && (
No credit packages found.
)}
)}
); }; export default AdminBilling;