From 9cb0e05618599559b7045ab1c39bf0154f9a6c52 Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Fri, 12 Dec 2025 15:11:17 +0000 Subject: [PATCH] asdsadsad --- .../billing/CreditCostBreakdownPanel.tsx | 181 ++++-- .../components/billing/CreditCostsPanel.tsx | 20 +- .../components/billing/UsageLimitsPanel.tsx | 51 +- frontend/src/layout/AppSidebar.tsx | 10 +- .../src/pages/account/PlansAndBillingPage.tsx | 571 ++++++++++++------ .../src/pages/account/UsageAnalyticsPage.tsx | 240 +++++--- 6 files changed, 722 insertions(+), 351 deletions(-) diff --git a/frontend/src/components/billing/CreditCostBreakdownPanel.tsx b/frontend/src/components/billing/CreditCostBreakdownPanel.tsx index ba765424..9e4dc21c 100644 --- a/frontend/src/components/billing/CreditCostBreakdownPanel.tsx +++ b/frontend/src/components/billing/CreditCostBreakdownPanel.tsx @@ -7,12 +7,25 @@ import { useState, useEffect } from 'react'; import { Card } from '../ui/card'; import { DollarSign, TrendingUp, AlertCircle } from 'lucide-react'; import Badge from '../ui/badge/Badge'; -import { getUsageAnalytics, type UsageAnalytics } from '../../services/billing.api'; +import { getCreditUsageSummary } from '../../services/billing.api'; import { useToast } from '../ui/toast/ToastContainer'; +interface OperationData { + credits: number; + cost: number; + count: number; +} + +interface CreditUsageSummary { + total_credits_used: number; + total_cost_usd: string; + by_operation: Record; + by_model: Record; +} + export default function CreditCostBreakdownPanel() { const toast = useToast(); - const [analytics, setAnalytics] = useState(null); + const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [period] = useState(30); // Last 30 days @@ -25,8 +38,17 @@ export default function CreditCostBreakdownPanel() { try { setLoading(true); setError(''); - const data = await getUsageAnalytics(period); - setAnalytics(data); + + // Calculate date range for last N days + const endDate = new Date(); + const startDate = new Date(); + startDate.setDate(startDate.getDate() - period); + + const data = await getCreditUsageSummary({ + start_date: startDate.toISOString(), + end_date: endDate.toISOString(), + }); + setSummary(data); } catch (err: any) { const message = err?.message || 'Failed to load cost analytics'; setError(message); @@ -47,7 +69,7 @@ export default function CreditCostBreakdownPanel() { ); } - if (error || !analytics) { + if (error || !summary) { return ( @@ -65,63 +87,92 @@ export default function CreditCostBreakdownPanel() { ); } + // Calculate metrics + const totalCost = parseFloat(summary.total_cost_usd) || 0; + const totalCredits = summary.total_credits_used || 0; + const totalOperations = Object.values(summary.by_operation).reduce((sum, op) => sum + op.count, 0); + const avgCostPerDay = totalCost / period; + + // Convert by_operation to array and sort by cost + const operationsList = Object.entries(summary.by_operation) + .map(([type, data]) => ({ + operation_type: type, + total: data.credits, + count: data.count, + cost_usd: data.cost, + })) + .sort((a, b) => b.cost_usd - a.cost_usd); + // Color palette for different operation types const operationColors = [ - { bg: 'bg-[var(--color-brand-50)]', text: 'text-[var(--color-brand-500)]', border: 'border-[var(--color-brand-200)]' }, - { bg: 'bg-[var(--color-success-50)]', text: 'text-[var(--color-success-500)]', border: 'border-[var(--color-success-200)]' }, - { bg: 'bg-[var(--color-info-50)]', text: 'text-[var(--color-info-500)]', border: 'border-[var(--color-info-200)]' }, - { bg: 'bg-[var(--color-purple-50)]', text: 'text-[var(--color-purple-500)]', border: 'border-[var(--color-purple-200)]' }, - { bg: 'bg-[var(--color-warning-50)]', text: 'text-[var(--color-warning-500)]', border: 'border-[var(--color-warning-200)]' }, - { bg: 'bg-[var(--color-teal-50)]', text: 'text-[var(--color-teal-500)]', border: 'border-[var(--color-teal-200)]' }, + { bg: 'bg-brand-50 dark:bg-brand-900/20', text: 'text-brand-600 dark:text-brand-400', border: 'border-brand-500 dark:border-brand-400' }, + { bg: 'bg-success-50 dark:bg-success-900/20', text: 'text-success-600 dark:text-success-400', border: 'border-success-500 dark:border-success-400' }, + { bg: 'bg-info-50 dark:bg-info-900/20', text: 'text-info-600 dark:text-info-400', border: 'border-info-500 dark:border-info-400' }, + { bg: 'bg-purple-50 dark:bg-purple-900/20', text: 'text-purple-600 dark:text-purple-400', border: 'border-purple-500 dark:border-purple-400' }, + { bg: 'bg-warning-50 dark:bg-warning-900/20', text: 'text-warning-600 dark:text-warning-400', border: 'border-warning-500 dark:border-warning-400' }, + { bg: 'bg-teal-50 dark:bg-teal-900/20', text: 'text-teal-600 dark:text-teal-400', border: 'border-teal-500 dark:border-teal-400' }, ]; return (
- {/* Summary Cards */} -
- + {/* Summary Cards - 4 columns */} +
+
-
- +
+
Total Cost
- ${((analytics.total_usage || 0) * 0.01).toFixed(2)} + ${totalCost.toFixed(2)}
Last {period} days
- +
-
- +
+
Avg Cost/Day
- ${(((analytics.total_usage || 0) * 0.01) / period).toFixed(2)} + ${avgCostPerDay.toFixed(2)}
Daily average
- +
-
- +
+
-
Cost per Credit
+
Total Operations
- $0.01 + {totalOperations.toLocaleString()}
-
Standard rate
+
API calls
+ + + +
+
+ +
+
Total Credits
+
+
+ {totalCredits.toLocaleString()} +
+
Credits used
- {/* Cost by Operation */} - -
+ {/* Cost by Operation - 4 columns */} +
+

Cost by Operation Type @@ -135,53 +186,51 @@ export default function CreditCostBreakdownPanel() {

-
- {analytics.usage_by_type.map((item: { transaction_type: string; total: number; count: number }, idx: number) => { +
+ {operationsList.map((item, idx) => { const colorScheme = operationColors[idx % operationColors.length]; - const costUSD = (item.total * 0.01).toFixed(2); + const costUSD = item.cost_usd.toFixed(2); const avgPerOperation = item.count > 0 ? (item.total / item.count).toFixed(0) : '0'; return ( -
-
-
-

- {item.transaction_type} -

- - {item.count} ops - +
+

+ {item.operation_type} +

+ + {item.count} ops + +
+
+
+ Credits: + + {item.total.toLocaleString()} +
-
-
- Credits: - - {item.total.toLocaleString()} - -
-
- Avg/op: - - {avgPerOperation} credits - +
+ Avg/op: + + {avgPerOperation} + +
+
+
+ ${costUSD}
+
USD
-
-
- ${costUSD} -
-
USD
-
-
+ ); })} - {(!analytics.usage_by_type || analytics.usage_by_type.length === 0) && ( -
+ {operationsList.length === 0 && ( +

No cost data available for this period @@ -189,7 +238,7 @@ export default function CreditCostBreakdownPanel() {

)}
- +
); } diff --git a/frontend/src/components/billing/CreditCostsPanel.tsx b/frontend/src/components/billing/CreditCostsPanel.tsx index 7a9a3997..4ac5fd52 100644 --- a/frontend/src/components/billing/CreditCostsPanel.tsx +++ b/frontend/src/components/billing/CreditCostsPanel.tsx @@ -7,7 +7,7 @@ import { Card } from '../ui/card'; import Badge from '../ui/badge/Badge'; // Credit costs per operation -const CREDIT_COSTS: Record = { +const CREDIT_COSTS: Record = { clustering: { cost: 10, description: 'Per clustering request', @@ -16,42 +16,42 @@ const CREDIT_COSTS: Record
- + {typeof info.cost === 'number' ? `${info.cost} credits` : info.cost}
diff --git a/frontend/src/components/billing/UsageLimitsPanel.tsx b/frontend/src/components/billing/UsageLimitsPanel.tsx index a99dc4cb..ac50a567 100644 --- a/frontend/src/components/billing/UsageLimitsPanel.tsx +++ b/frontend/src/components/billing/UsageLimitsPanel.tsx @@ -24,24 +24,46 @@ function LimitCard({ title, icon, usage, type, daysUntilReset, accentColor = 'br const isWarning = percentage >= 80; const isDanger = percentage >= 95; - // Determine progress bar color - let barColor = `bg-[var(--color-${accentColor}-500)]`; + // Determine progress bar color - use inline styles for dynamic colors + let barColor = 'var(--color-brand-500)'; let badgeVariant: 'soft' = 'soft'; let badgeTone: 'brand' | 'warning' | 'danger' | 'success' | 'info' | 'purple' | 'indigo' | 'pink' | 'teal' | 'cyan' = accentColor; + // Color mapping for progress bars + const colorMap: Record = { + brand: '#0693e3', + success: '#0bbf87', + info: '#3b82f6', + warning: '#ff7a00', + danger: '#ef4444', + purple: '#8b5cf6', + indigo: '#6366f1', + pink: '#ec4899', + teal: '#14b8a6', + cyan: '#06b6d4', + }; + if (isDanger) { - barColor = 'bg-[var(--color-danger)]'; + barColor = colorMap.danger; badgeTone = 'danger'; } else if (isWarning) { - barColor = 'bg-[var(--color-warning)]'; + barColor = colorMap.warning; badgeTone = 'warning'; + } else { + barColor = colorMap[accentColor] || colorMap.brand; } return (
-
+
{icon}
@@ -58,10 +80,13 @@ function LimitCard({ title, icon, usage, type, daysUntilReset, accentColor = 'br {/* Progress Bar */}
-
+
@@ -228,9 +253,9 @@ export default function UsageLimitsPanel() { {/* Upgrade CTA if approaching limits */} {(Object.values(summary.hard_limits).some(u => u.percentage_used >= 80) || Object.values(summary.monthly_limits).some(u => u.percentage_used >= 80)) && ( - +
-
+
@@ -242,10 +267,10 @@ export default function UsageLimitsPanel() { and avoid interruptions.

- Purchase Credits + Upgrade Plan
diff --git a/frontend/src/layout/AppSidebar.tsx b/frontend/src/layout/AppSidebar.tsx index 17344fb5..b8a3b237 100644 --- a/frontend/src/layout/AppSidebar.tsx +++ b/frontend/src/layout/AppSidebar.tsx @@ -198,16 +198,16 @@ const AppSidebar: React.FC = () => { name: "Account Settings", path: "/account/settings", }, - { - icon: , - name: "Plans & Billing", - path: "/account/plans", - }, { icon: , name: "Team Management", path: "/account/team", }, + { + icon: , + name: "Plans & Billing", + path: "/account/plans", + }, { icon: , name: "Usage & Analytics", diff --git a/frontend/src/pages/account/PlansAndBillingPage.tsx b/frontend/src/pages/account/PlansAndBillingPage.tsx index df619649..e0ead5c5 100644 --- a/frontend/src/pages/account/PlansAndBillingPage.tsx +++ b/frontend/src/pages/account/PlansAndBillingPage.tsx @@ -1,20 +1,21 @@ /** - * Plans & Billing Page - Consolidated - * Tabs: Current Plan, Credits Overview, Billing History + * Plans & Billing Page - Refactored for Better UX + * Organized tabs: Current Plan, Plan Limits, Credits, Purchase Credits, Billing History */ import { useState, useEffect, useRef } from 'react'; import { CreditCard, Package, TrendingUp, FileText, Wallet, ArrowUpCircle, - Loader2, AlertCircle, CheckCircle, Download + Loader2, AlertCircle, CheckCircle, Download, BarChart3, Zap, Globe, Users } from 'lucide-react'; import { Card } from '../../components/ui/card'; import Badge from '../../components/ui/badge/Badge'; import Button from '../../components/ui/button/Button'; import { useToast } from '../../components/ui/toast/ToastContainer'; -import { PricingTable, PricingPlan } from '../../components/ui/pricing-table'; +import { PricingTable } from '../../components/ui/pricing-table'; import CreditCostBreakdownPanel from '../../components/billing/CreditCostBreakdownPanel'; import CreditCostsPanel from '../../components/billing/CreditCostsPanel'; +import UsageLimitsPanel from '../../components/billing/UsageLimitsPanel'; import { getCreditBalance, getCreditPackages, @@ -41,7 +42,7 @@ import { } from '../../services/billing.api'; import { useAuthStore } from '../../store/authStore'; -type TabType = 'plan' | 'credits' | 'purchase' | 'invoices'; +type TabType = 'plan' | 'limits' | 'credits' | 'upgrade' | 'invoices'; export default function PlansAndBillingPage() { const [activeTab, setActiveTab] = useState('plan'); @@ -342,9 +343,10 @@ export default function PlansAndBillingPage() { const tabs = [ { id: 'plan' as TabType, label: 'Current Plan', icon: }, - { id: 'credits' as TabType, label: 'Credits Overview', icon: }, - { id: 'purchase' as TabType, label: 'Purchase Credits', icon: }, - { id: 'invoices' as TabType, label: 'Billing History', icon: }, + { id: 'limits' as TabType, label: 'Plan Limits', icon: }, + { id: 'credits' as TabType, label: 'Credits', icon: }, + { id: 'upgrade' as TabType, label: 'Purchase/Upgrade', icon: }, + { id: 'invoices' as TabType, label: 'History', icon: }, ]; return ( @@ -403,14 +405,18 @@ export default function PlansAndBillingPage() { {/* Current Plan Tab */} {activeTab === 'plan' && (
- {/* 2/3 Current Plan + 1/3 Plan Features Layout */} + {/* Current Plan Overview */}
- {/* Current Plan Card - 2/3 width */} + {/* Main Plan Card */} -

Your Current Plan

+

Your Current Plan

{!hasActivePlan && ( -
- No active plan found. Please choose a plan to activate your account. +
+ +
+

No Active Plan

+

Choose a plan below to activate your account and unlock all features.

+
)}
@@ -419,40 +425,65 @@ export default function PlansAndBillingPage() {
{currentPlan?.name || 'No Plan Selected'}
-
+
{currentPlan?.description || 'Select a plan to unlock full access.'}
- - {hasActivePlan ? subscriptionStatus : 'plan required'} + + {hasActivePlan ? subscriptionStatus : 'Inactive'}
-
-
-
Monthly Credits
-
+ + {/* Quick Stats Grid */} +
+
+
+ + Monthly Credits +
+
{creditBalance?.plan_credits_per_month?.toLocaleString?.() || 0}
-
-
Current Balance
-
+
+
+ + Current Balance +
+
{creditBalance?.credits?.toLocaleString?.() || 0}
-
-
Period Ends
-
+
+
+ + Renewal Date +
+
{currentSubscription?.current_period_end - ? new Date(currentSubscription.current_period_end).toLocaleDateString() + ? new Date(currentSubscription.current_period_end).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '—'}
-
- + {hasActivePlan && ( )}
- {/* Plan Features Card - 1/3 width with 2-column layout */} + {/* Plan Features Card */} -

Plan Features

-
+

Included Features

+
{(currentPlan?.features && currentPlan.features.length > 0 ? currentPlan.features - : ['ai_writer', 'image_gen', 'auto_publish', 'custom_prompts', 'email_support', 'api_access']) - .map((feature: string) => ( -
- + : ['AI Content Writer', 'Image Generation', 'Auto Publishing', 'Custom Prompts', 'Email Support', 'API Access']) + .map((feature: string, index: number) => ( +
+ {feature}
))} @@ -484,100 +522,167 @@ export default function PlansAndBillingPage() {
- {/* Upgrade/Downgrade Section with Pricing Table */} -
-
- { - const discount = plan.annual_discount_percent || 15; - return { - id: plan.id, - name: plan.name, - monthlyPrice: plan.price || 0, - price: plan.price || 0, - annualDiscountPercent: discount, - period: `/${plan.interval || 'month'}`, - description: plan.description || 'Standard plan', - features: plan.features && plan.features.length > 0 - ? plan.features - : ['Monthly credits included', 'Module access', 'Email support'], - buttonText: plan.id === currentPlanId ? 'Current Plan' : 'Select Plan', - highlighted: plan.is_featured || false, - disabled: plan.id === currentPlanId || planLoadingId === plan.id, - // Plan limits - max_sites: plan.max_sites, - max_users: plan.max_users, - max_keywords: plan.max_keywords, - max_clusters: plan.max_clusters, - max_content_ideas: plan.max_content_ideas, - max_content_words: plan.max_content_words, - max_images_basic: plan.max_images_basic, - max_images_premium: plan.max_images_premium, - included_credits: plan.included_credits, - }; - })} - showToggle={true} - onPlanSelect={(plan) => plan.id && handleSelectPlan(plan.id)} - /> -
- - -

Plan Change Policy

-
    -
  • • Upgrades take effect immediately and you'll be charged a prorated amount
  • -
  • • Downgrades take effect at the end of your current billing period
  • -
  • • Unused credits from your current plan will carry over
  • -
  • • You can cancel your subscription at any time
  • -
+ {/* Plan Limits Overview */} + {hasActivePlan && ( + +
+
+

Quick Limits Overview

+

+ Key plan limits at a glance +

+
+ +
+
+
+
+ + Sites +
+
+ {currentPlan?.max_sites === 9999 ? '∞' : currentPlan?.max_sites || 0} +
+
+
+
+ + Team Members +
+
+ {currentPlan?.max_users === 9999 ? '∞' : currentPlan?.max_users || 0} +
+
+
+
+ + Content Words/mo +
+
+ {currentPlan?.max_content_words === 9999999 + ? '∞' + : currentPlan?.max_content_words + ? `${(currentPlan.max_content_words / 1000).toFixed(0)}K` + : 0} +
+
+
+
+ + Monthly Credits +
+
+ {currentPlan?.included_credits?.toLocaleString?.() || 0} +
+
+
-
+ )} +
+ )} + + {/* Plan Limits Tab */} + {activeTab === 'limits' && ( +
+
)} {/* Credits Overview Tab */} {activeTab === 'credits' && (
+ {/* Credit Balance Cards */}
- -
Current Balance
-
+ +
+
+ +
+
Current Balance
+
+
{creditBalance?.credits.toLocaleString() || 0}
-
credits available
+
credits available
- -
Used This Month
-
+ + +
+
+ +
+
Used This Month
+
+
{creditBalance?.credits_used_this_month.toLocaleString() || 0}
-
credits consumed
+
credits consumed
- -
Monthly Included
-
+ + +
+
+ +
+
Monthly Included
+
+
{creditBalance?.plan_credits_per_month.toLocaleString() || 0}
-
from your plan
+
from your plan
+ {/* Usage Summary with Progress Bar */} -

Credit Usage Summary

-
-
- Remaining Credits - {creditBalance?.credits_remaining.toLocaleString() || 0} +

Credit Usage Summary

+
+
+ Monthly Allocation + + {creditBalance?.plan_credits_per_month.toLocaleString() || 0} credits +
-
-
+
+ Used This Month + + {creditBalance?.credits_used_this_month.toLocaleString() || 0} credits + +
+
+ Remaining Balance + + {creditBalance?.credits_remaining.toLocaleString() || 0} credits + +
+ + {/* Progress Bar */} +
+
+ Usage Progress + + {creditBalance?.credits_used_this_month && creditBalance?.plan_credits_per_month + ? `${Math.round((creditBalance.credits_used_this_month / creditBalance.plan_credits_per_month) * 100)}%` + : '0%'} + +
+
+
+
@@ -585,8 +690,8 @@ export default function PlansAndBillingPage() { {/* Credit Cost Breakdown */}
-

Credit Cost Analytics

-

Cost breakdown by operation type

+

Credit Cost Analytics

+

Detailed breakdown of credit usage by operation type

@@ -598,78 +703,190 @@ export default function PlansAndBillingPage() {
)} - {/* Purchase Credits Tab */} - {activeTab === 'purchase' && ( + {/* Purchase/Upgrade Tab */} + {activeTab === 'upgrade' && (
-
-

Purchase Additional Credits

-

Top up your credit balance with our packages

-
- -
-
- {packages.map((pkg) => ( -
-
-
- - - -
-

- {pkg.name} -

-
- {pkg.credits.toLocaleString()} - credits -
-
- ${pkg.price} -
- {pkg.description && ( -

- {pkg.description} -

- )} -
-
- -
-
- ))} - {packages.length === 0 && ( -
- No credit packages available at this time -
- )} + {/* Upgrade Plans Section */} +
+
+

+ {hasActivePlan ? 'Upgrade or Change Your Plan' : 'Choose Your Plan'} +

+

+ Select the plan that best fits your needs +

+
+ { + // Only show paid plans (exclude Free Plan) + const planName = (plan.name || '').toLowerCase(); + const planPrice = plan.price || 0; + return planPrice > 0 && !planName.includes('free'); + }) + .map(plan => { + const discount = plan.annual_discount_percent || 15; + return { + id: plan.id, + name: plan.name, + monthlyPrice: plan.price || 0, + price: plan.price || 0, + annualDiscountPercent: discount, + period: `/${plan.interval || 'month'}`, + description: plan.description || 'Standard plan', + features: plan.features && plan.features.length > 0 + ? plan.features + : ['Monthly credits included', 'Module access', 'Email support'], + buttonText: plan.id === currentPlanId ? 'Current Plan' : 'Select Plan', + highlighted: plan.is_featured || false, + disabled: plan.id === currentPlanId || planLoadingId === plan.id, + max_sites: plan.max_sites, + max_users: plan.max_users, + max_keywords: plan.max_keywords, + max_clusters: plan.max_clusters, + max_content_ideas: plan.max_content_ideas, + max_content_words: plan.max_content_words, + max_images_basic: plan.max_images_basic, + max_images_premium: plan.max_images_premium, + included_credits: plan.included_credits, + }; + })} + showToggle={true} + onPlanSelect={(plan) => plan.id && handleSelectPlan(plan.id)} + /> +
+ + {/* Plan Change Policy */} + +

+ + Plan Change Policy +

+
    +
  • + + Upgrades take effect immediately with prorated billing +
  • +
  • + + Downgrades take effect at the end of your current billing period +
  • +
  • + + Unused credits carry over when changing plans +
  • +
  • + + Cancel anytime - no long-term commitments +
  • +
+
- {/* Payment Methods Info */} - {!hasPaymentMethods && paymentMethods.length === 0 && ( - -
- -
-

- Payment Method Required -

-

- Please contact support to set up a payment method before purchasing credits. -

+ {/* Purchase Additional Credits Section */} +
+
+

Purchase Additional Credits

+

Top up your credit balance with our credit packages

+
+ + {/* Current Balance Quick View */} + +
+
+
+ +
+
+
Current Credit Balance
+
+ {creditBalance?.credits.toLocaleString() || 0} +
+
+
+
+
Monthly Allocation
+
+ {creditBalance?.plan_credits_per_month.toLocaleString() || 0} +
- )} + + {/* Credit Packages Grid */} +
+ {packages.map((pkg) => ( + +
+
+ +
+
+

+ {pkg.name} +

+ {pkg.description && ( +

+ {pkg.description} +

+ )} +
+ + {pkg.credits.toLocaleString()} + + credits +
+
+ ${pkg.price} +
+ +
+ ))} + {packages.length === 0 && ( +
+
+ +
+

No Packages Available

+

Credit packages will be available soon

+
+ )} +
+ + {/* Payment Methods Info */} + {!hasPaymentMethods && paymentMethods.length === 0 && ( + +
+
+ +
+
+

+ Payment Method Required +

+

+ Please contact support to set up a payment method before purchasing credits. +

+
+
+
+ )} +
)} diff --git a/frontend/src/pages/account/UsageAnalyticsPage.tsx b/frontend/src/pages/account/UsageAnalyticsPage.tsx index 64924cbc..a68e7b62 100644 --- a/frontend/src/pages/account/UsageAnalyticsPage.tsx +++ b/frontend/src/pages/account/UsageAnalyticsPage.tsx @@ -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('limits'); const [analytics, setAnalytics] = useState(null); + const [creditBalance, setCreditBalance] = useState(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 (
- +
-
Loading...
+
+
+
Loading usage data...
+
); } const tabs = [ - { id: 'limits' as TabType, label: 'Limits & Usage', icon: }, - { id: 'activity' as TabType, label: 'Activity', icon: }, + { id: 'limits' as TabType, label: 'Plan Limits & Usage', icon: }, + { id: 'activity' as TabType, label: 'Credit Activity', icon: }, { id: 'api' as TabType, label: 'API Usage', icon: }, ]; return (
- + + {/* Page Header */}

Usage & Analytics

- Monitor plan limits, credit usage, API calls, and cost breakdown -

and API calls + Track plan limits, credit consumption, and API usage patterns +

-
+ {/* Quick Stats Overview */} + {creditBalance && ( +
+ +
+
+ +
+
+
Current Balance
+
+ {creditBalance.credits.toLocaleString()} +
+
+
+
+ + +
+
+ +
+
+
Used This Month
+
+ {creditBalance.credits_used_this_month.toLocaleString()} +
+
+
+
+ + +
+
+ +
+
+
Monthly Allocation
+
+ {creditBalance.plan_credits_per_month.toLocaleString()} +
+
+
+
+ + +
+
+ +
+
+
Usage %
+
+ {creditBalance.plan_credits_per_month > 0 + ? Math.round((creditBalance.credits_used_this_month / creditBalance.plan_credits_per_month) * 100) + : 0}% +
+
+
+
+
+ )} + + {/* Tabs and Period Selector */} +
{/* Tabs */} -
-
{/* Tab Content */}
- {/* Limits & Usage Tab */} + {/* Plan Limits & Usage Tab */} {activeTab === 'limits' && (
)} - {/* Activity Tab */} + {/* Credit Activity Tab */} {activeTab === 'activity' && (
@@ -129,52 +203,82 @@ export default function UsageAnalyticsPage() { {/* API Usage Tab */} {activeTab === 'api' && (
+ {/* API Stats Cards */}
-
Total API Calls
-
+
+
+ +
+
Total API Calls
+
+
{analytics?.usage_by_type.reduce((sum, item) => sum + item.count, 0).toLocaleString() || 0}
+
in last {period} days
-
Avg Calls/Day
+
+
+ +
+
Avg Calls/Day
+
{Math.round((analytics?.usage_by_type.reduce((sum, item) => sum + item.count, 0) || 0) / period)}
+
daily average
-
Success Rate
-
+
+
+ +
+
Success Rate
+
+
98.5%
+
successful requests
+ {/* API Calls by Endpoint */}

API Calls by Endpoint

-
+
-
/api/v1/content/generate
+
/api/v1/content/generate
Content generation
-
1,234
-
calls
+
1,234
+
calls
-
+
-
/api/v1/keywords/cluster
+
/api/v1/keywords/cluster
Keyword clustering
-
567
-
calls
+
567
+
calls
+
+
+
+
+
/api/v1/images/generate
+
Image generation
+
+
+
342
+
calls
@@ -182,30 +286,6 @@ export default function UsageAnalyticsPage() {
)}
- - {/* Summary Cards */} -
- -
Total Credits Used
-
- {analytics?.total_usage.toLocaleString() || 0} -
-
- - -
Total Purchases
-
- {analytics?.total_purchases.toLocaleString() || 0} -
-
- - -
Current Balance
-
- {analytics?.current_balance.toLocaleString() || 0} -
-
-
); }