asdsadsad

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-12 15:11:17 +00:00
parent f163a2e07d
commit 9cb0e05618
6 changed files with 722 additions and 351 deletions

View File

@@ -1,40 +1,44 @@
/**
* Usage & Analytics Page
* Tabs: Limits & Usage, API Usage
* Usage & Analytics Page - Refactored
* Organized tabs: Plan Limits & Usage, Credit Activity, API Usage
*/
import { useState, useEffect } from 'react';
import { TrendingUp, Activity, DollarSign, BarChart3 } from 'lucide-react';
import { TrendingUp, Activity, BarChart3, Zap, Calendar } from 'lucide-react';
import PageMeta from '../../components/common/PageMeta';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { getUsageAnalytics, UsageAnalytics } from '../../services/billing.api';
import { getUsageAnalytics, UsageAnalytics, getCreditBalance, type CreditBalance } from '../../services/billing.api';
import { Card } from '../../components/ui/card';
import Badge from '../../components/ui/badge/Badge';
import BillingUsagePanel from '../../components/billing/BillingUsagePanel';
import BillingBalancePanel from '../../components/billing/BillingBalancePanel';
import UsageLimitsPanel from '../../components/billing/UsageLimitsPanel';
import Button from '../../components/ui/button/Button';
type TabType = 'limits' | 'api' | 'activity';
type TabType = 'limits' | 'activity' | 'api';
export default function UsageAnalyticsPage() {
const toast = useToast();
const [activeTab, setActiveTab] = useState<TabType>('limits');
const [analytics, setAnalytics] = useState<UsageAnalytics | null>(null);
const [creditBalance, setCreditBalance] = useState<CreditBalance | null>(null);
const [loading, setLoading] = useState(true);
const [period, setPeriod] = useState(30);
useEffect(() => {
loadAnalytics();
loadData();
}, [period]);
const loadAnalytics = async () => {
const loadData = async () => {
try {
setLoading(true);
const data = await getUsageAnalytics(period);
setAnalytics(data);
const [analyticsData, balanceData] = await Promise.all([
getUsageAnalytics(period),
getCreditBalance(),
]);
setAnalytics(analyticsData);
setCreditBalance(balanceData);
} catch (error: any) {
toast.error(`Failed to load usage analytics: ${error.message}`);
toast.error(`Failed to load usage data: ${error.message}`);
} finally {
setLoading(false);
}
@@ -43,44 +47,112 @@ export default function UsageAnalyticsPage() {
if (loading) {
return (
<div className="p-6">
<PageMeta title="Usage & Analytics" description="Analyze your usage patterns" />
<PageMeta title="Usage & Analytics" description="Monitor your plan limits and usage" />
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">Loading...</div>
<div className="flex flex-col items-center gap-3">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-500"></div>
<div className="text-gray-500 dark:text-gray-400">Loading usage data...</div>
</div>
</div>
</div>
);
}
const tabs = [
{ id: 'limits' as TabType, label: 'Limits & Usage', icon: <BarChart3 className="w-4 h-4" /> },
{ id: 'activity' as TabType, label: 'Activity', icon: <TrendingUp className="w-4 h-4" /> },
{ id: 'limits' as TabType, label: 'Plan Limits & Usage', icon: <BarChart3 className="w-4 h-4" /> },
{ id: 'activity' as TabType, label: 'Credit Activity', icon: <TrendingUp className="w-4 h-4" /> },
{ id: 'api' as TabType, label: 'API Usage', icon: <Activity className="w-4 h-4" /> },
];
return (
<div className="p-6">
<PageMeta title="Usage & Analytics" description="Analyze your usage patterns" />
<PageMeta title="Usage & Analytics" description="Monitor your plan limits and usage" />
{/* Page Header */}
<div className="mb-6">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Usage & Analytics</h1>
<p className="text-gray-600 dark:text-gray-400 mt-1">
Monitor plan limits, credit usage, API calls, and cost breakdown
</p>and API calls
Track plan limits, credit consumption, and API usage patterns
</p>
</div>
<div className="mb-6 flex items-center justify-between">
{/* Quick Stats Overview */}
{creditBalance && (
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<Card className="p-4 bg-gradient-to-br from-brand-50 to-brand-100 dark:from-brand-900/20 dark:to-brand-800/10 border-brand-200 dark:border-brand-700">
<div className="flex items-center gap-3">
<div className="p-2 bg-brand-500 rounded-lg">
<Zap className="w-5 h-5 text-white" />
</div>
<div>
<div className="text-xs text-brand-700 dark:text-brand-300">Current Balance</div>
<div className="text-2xl font-bold text-brand-600 dark:text-brand-400">
{creditBalance.credits.toLocaleString()}
</div>
</div>
</div>
</Card>
<Card className="p-4 bg-gradient-to-br from-purple-50 to-purple-100 dark:from-purple-900/20 dark:to-purple-800/10 border-purple-200 dark:border-purple-700">
<div className="flex items-center gap-3">
<div className="p-2 bg-purple-500 rounded-lg">
<TrendingUp className="w-5 h-5 text-white" />
</div>
<div>
<div className="text-xs text-purple-700 dark:text-purple-300">Used This Month</div>
<div className="text-2xl font-bold text-purple-600 dark:text-purple-400">
{creditBalance.credits_used_this_month.toLocaleString()}
</div>
</div>
</div>
</Card>
<Card className="p-4 bg-gradient-to-br from-success-50 to-success-100 dark:from-success-900/20 dark:to-success-800/10 border-success-200 dark:border-success-700">
<div className="flex items-center gap-3">
<div className="p-2 bg-success-500 rounded-lg">
<BarChart3 className="w-5 h-5 text-white" />
</div>
<div>
<div className="text-xs text-success-700 dark:text-success-300">Monthly Allocation</div>
<div className="text-2xl font-bold text-success-600 dark:text-success-400">
{creditBalance.plan_credits_per_month.toLocaleString()}
</div>
</div>
</div>
</Card>
<Card className="p-4 bg-gradient-to-br from-indigo-50 to-indigo-100 dark:from-indigo-900/20 dark:to-indigo-800/10 border-indigo-200 dark:border-indigo-700">
<div className="flex items-center gap-3">
<div className="p-2 bg-indigo-500 rounded-lg">
<Calendar className="w-5 h-5 text-white" />
</div>
<div>
<div className="text-xs text-indigo-700 dark:text-indigo-300">Usage %</div>
<div className="text-2xl font-bold text-indigo-600 dark:text-indigo-400">
{creditBalance.plan_credits_per_month > 0
? Math.round((creditBalance.credits_used_this_month / creditBalance.plan_credits_per_month) * 100)
: 0}%
</div>
</div>
</div>
</Card>
</div>
)}
{/* Tabs and Period Selector */}
<div className="mb-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
{/* Tabs */}
<div className="border-b border-gray-200 dark:border-gray-700">
<nav className="-mb-px flex space-x-8">
<div className="border-b border-gray-200 dark:border-gray-700 w-full sm:w-auto">
<nav className="-mb-px flex space-x-8 overflow-x-auto">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm
flex items-center gap-2 py-4 px-1 border-b-2 font-medium text-sm whitespace-nowrap
${activeTab === tab.id
? 'border-[var(--color-brand-500)] text-[var(--color-brand-500)]'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400'
? 'border-brand-500 text-brand-600 dark:text-brand-400'
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 dark:text-gray-400 dark:hover:text-gray-300'
}
`}
>
@@ -91,35 +163,37 @@ export default function UsageAnalyticsPage() {
</nav>
</div>
{/* Period Selector */}
<div className="flex gap-2">
{[7, 30, 90].map((value) => {
const isActive = period === value;
return (
<Button
key={value}
size="sm"
variant={isActive ? 'primary' : 'secondary'}
tone={isActive ? 'brand' : 'neutral'}
onClick={() => setPeriod(value)}
>
{value} Days
</Button>
);
})}
</div>
{/* Period Selector (only show on activity and api tabs) */}
{(activeTab === 'activity' || activeTab === 'api') && (
<div className="flex gap-2">
{[7, 30, 90].map((value) => {
const isActive = period === value;
return (
<Button
key={value}
size="sm"
variant={isActive ? 'primary' : 'outline'}
tone={isActive ? 'brand' : 'neutral'}
onClick={() => setPeriod(value)}
>
{value} Days
</Button>
);
})}
</div>
)}
</div>
{/* Tab Content */}
<div className="mt-6">
{/* Limits & Usage Tab */}
{/* Plan Limits & Usage Tab */}
{activeTab === 'limits' && (
<div className="space-y-6">
<UsageLimitsPanel />
</div>
)}
{/* Activity Tab */}
{/* Credit Activity Tab */}
{activeTab === 'activity' && (
<div className="space-y-6">
<BillingUsagePanel showOnlyActivity={true} />
@@ -129,52 +203,82 @@ export default function UsageAnalyticsPage() {
{/* API Usage Tab */}
{activeTab === 'api' && (
<div className="space-y-6">
{/* API Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Total API Calls</div>
<div className="text-3xl font-bold text-[var(--color-brand-500)]">
<div className="flex items-center gap-3 mb-3">
<div className="p-2 bg-brand-100 dark:bg-brand-900/30 rounded-lg">
<Activity className="w-5 h-5 text-brand-600 dark:text-brand-400" />
</div>
<div className="text-sm font-medium text-gray-600 dark:text-gray-400">Total API Calls</div>
</div>
<div className="text-3xl font-bold text-brand-600 dark:text-brand-400">
{analytics?.usage_by_type.reduce((sum, item) => sum + item.count, 0).toLocaleString() || 0}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400 mt-1">in last {period} days</div>
</Card>
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Avg Calls/Day</div>
<div className="flex items-center gap-3 mb-3">
<div className="p-2 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
<BarChart3 className="w-5 h-5 text-purple-600 dark:text-purple-400" />
</div>
<div className="text-sm font-medium text-gray-600 dark:text-gray-400">Avg Calls/Day</div>
</div>
<div className="text-3xl font-bold text-purple-600 dark:text-purple-400">
{Math.round((analytics?.usage_by_type.reduce((sum, item) => sum + item.count, 0) || 0) / period)}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400 mt-1">daily average</div>
</Card>
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Success Rate</div>
<div className="text-3xl font-bold text-green-600 dark:text-green-400">
<div className="flex items-center gap-3 mb-3">
<div className="p-2 bg-success-100 dark:bg-success-900/30 rounded-lg">
<TrendingUp className="w-5 h-5 text-success-600 dark:text-success-400" />
</div>
<div className="text-sm font-medium text-gray-600 dark:text-gray-400">Success Rate</div>
</div>
<div className="text-3xl font-bold text-success-600 dark:text-success-400">
98.5%
</div>
<div className="text-sm text-gray-500 dark:text-gray-400 mt-1">successful requests</div>
</Card>
</div>
{/* API Calls by Endpoint */}
<Card className="p-6">
<h2 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
API Calls by Endpoint
</h2>
<div className="space-y-3">
<div className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors">
<div className="flex-1">
<div className="font-medium">/api/v1/content/generate</div>
<div className="font-medium text-gray-900 dark:text-white">/api/v1/content/generate</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">Content generation</div>
</div>
<div className="text-right">
<div className="text-lg font-bold">1,234</div>
<div className="text-xs text-gray-500">calls</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">1,234</div>
<div className="text-xs text-gray-500 dark:text-gray-400">calls</div>
</div>
</div>
<div className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors">
<div className="flex-1">
<div className="font-medium">/api/v1/keywords/cluster</div>
<div className="font-medium text-gray-900 dark:text-white">/api/v1/keywords/cluster</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">Keyword clustering</div>
</div>
<div className="text-right">
<div className="text-lg font-bold">567</div>
<div className="text-xs text-gray-500">calls</div>
<div className="text-xl font-bold text-gray-900 dark:text-white">567</div>
<div className="text-xs text-gray-500 dark:text-gray-400">calls</div>
</div>
</div>
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-750 transition-colors">
<div className="flex-1">
<div className="font-medium text-gray-900 dark:text-white">/api/v1/images/generate</div>
<div className="text-xs text-gray-500 dark:text-gray-400 mt-1">Image generation</div>
</div>
<div className="text-right">
<div className="text-xl font-bold text-gray-900 dark:text-white">342</div>
<div className="text-xs text-gray-500 dark:text-gray-400">calls</div>
</div>
</div>
</div>
@@ -182,30 +286,6 @@ export default function UsageAnalyticsPage() {
</div>
)}
</div>
{/* Summary Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6 hidden">
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Total Credits Used</div>
<div className="text-3xl font-bold text-red-600 dark:text-red-400">
{analytics?.total_usage.toLocaleString() || 0}
</div>
</Card>
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Total Purchases</div>
<div className="text-3xl font-bold text-green-600 dark:text-green-400">
{analytics?.total_purchases.toLocaleString() || 0}
</div>
</Card>
<Card className="p-6">
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">Current Balance</div>
<div className="text-3xl font-bold text-gray-900 dark:text-white">
{analytics?.current_balance.toLocaleString() || 0}
</div>
</Card>
</div>
</div>
);
}