'use client'; import React, { useState, useEffect } from 'react'; import { cmsApi } from '@/lib/api'; import { ShieldCheck, TrendingUp, DollarSign, Users, Activity, PieChart, BarChart3, MessageCircle, GraduationCap, Building2, ArrowUpRight, Save } from 'lucide-react'; interface DailyUsage { date: string; total_tokens: number; input_tokens: number; output_tokens: number; cost_usd: number; requests: number; } interface UsageByEndpoint { endpoint: string; request_type: string; total_tokens: number; input_tokens: number; output_tokens: number; cost_usd: number; requests: number; } interface UsageByOrganization { org_id: string; org_name: string; total_tokens: number; input_tokens: number; output_tokens: number; cost_usd: number; requests: number; active_users: number; } interface UsageByRequestType { request_type: string; total_tokens: number; cost_usd: number; requests: number; } interface TopUserUsage { user_id: string; email: string; full_name: string; role: string; org_name: string; total_tokens: number; input_tokens: number; output_tokens: number; cost_usd: number; requests: number; } interface StudentChatUsage { user_id: string; email: string; full_name: string; org_name: string; total_tokens: number; cost_usd: number; chat_requests: number; last_chat: string; } interface GlobalAiSummary { total_tokens: number; total_input: number; total_output: number; total_requests: number; total_cost_usd: number; savings_vs_openai_usd: number; savings_percentage: number; openai_equivalent_cost_usd: number; total_organizations: number; total_active_users: number; } interface StudentChatSummary { total_tokens: number; total_requests: number; total_cost_usd: number; active_students: number; } interface GlobalAiUsageResponse { summary: GlobalAiSummary; student_chat_summary: StudentChatSummary | null; daily_usage: DailyUsage[]; by_endpoint: UsageByEndpoint[]; by_organization: UsageByOrganization[]; by_request_type: UsageByRequestType[]; top_users: TopUserUsage[]; student_chat_usage: StudentChatUsage[]; } export default function GlobalAiControl() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [dateRange, setDateRange] = useState<'7d' | '30d' | '90d'>('30d'); const [authError, setAuthError] = useState(false); useEffect(() => { loadGlobalUsage(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [dateRange]); const loadGlobalUsage = async () => { try { const endDate = new Date(); const startDate = new Date(); if (dateRange === '7d') { startDate.setDate(startDate.getDate() - 7); } else if (dateRange === '30d') { startDate.setDate(startDate.getDate() - 30); } else { startDate.setDate(startDate.getDate() - 90); } const token = localStorage.getItem('studio_token'); if (!token) { console.error('No token found. Please login again.'); setAuthError(true); setLoading(false); return; } const jsonData = await cmsApi.getGlobalAiUsage( startDate.toISOString().split('T')[0], endDate.toISOString().split('T')[0] ); setData(jsonData); setAuthError(false); } catch (error) { console.error('Failed to load global AI usage:', error); const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes('401') || errorMessage.includes('Unauthorized')) { setAuthError(true); } } finally { setLoading(false); } }; const formatNumber = (num: number) => { return new Intl.NumberFormat('en-US').format(num); }; const formatCurrency = (num: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 4 }).format(num); }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString('es-ES', { day: '2-digit', month: 'short' }); }; // Calculate max value for chart scaling const maxDailyTokens = data?.daily_usage.reduce((max, d) => Math.max(max, d.total_tokens), 0) || 1; if (loading) { return (

Cargando métricas globales...

); } if (authError) { return (

Error de Autenticación

No se pudo cargar los datos. Esto puede deberse a:

  • Tu sesión ha expirado
  • No has iniciado sesión
  • El token no es válido
Iniciar Sesión

Token en localStorage: {localStorage.getItem('studio_token') ? '✓ Existe' : '✗ No existe'}

); } if (!data) { return (

Error al cargar los datos. Por favor, recarga la página.

); } return (
{/* Header */}

Control Global - Uso de IA

Monitoreo integral de tokens, costos y ahorro en todo el sistema

{/* Date Range Selector */}
{(['7d', '30d', '90d'] as const).map((range) => ( ))}
{/* Summary Cards */}
{/* Total Tokens */}

Total Tokens

{formatNumber(data.summary.total_tokens)}

Input: {formatNumber(data.summary.total_input)} | Output: {formatNumber(data.summary.total_output)}
{/* Costo Total */}

Costo Total (IA Local)

{formatCurrency(data.summary.total_cost_usd)}

Requests: {formatNumber(data.summary.total_requests)}
{/* Ahorro vs OpenAI */}

Ahorro vs OpenAI GPT-4

{formatCurrency(data.summary.savings_vs_openai_usd)}

{data.summary.savings_percentage.toFixed(1)}% más económico
{/* Usuarios Activos */}

Usuarios Activos

{data.summary.total_active_users}

Organizaciones: {data.summary.total_organizations}
{/* Student Chat Summary */} {data.student_chat_summary && (

Uso del Chat por Alumnos

Tokens Consumidos

{formatNumber(data.student_chat_summary.total_tokens)}

Requests de Chat

{formatNumber(data.student_chat_summary.total_requests)}

Costo Total

{formatCurrency(data.student_chat_summary.total_cost_usd)}

Alumnos Activos

{data.student_chat_summary.active_students}

)} {/* Charts Row */}
{/* Daily Usage Chart */}

Uso Diario de Tokens

{data.daily_usage.slice(-30).map((day, idx) => (
{formatDate(day.date)}
))}
{/* Usage by Request Type */}

Uso por Tipo de Request

{data.by_request_type.map((type, idx) => { const percentage = data.summary.total_tokens > 0 ? (type.total_tokens / data.summary.total_tokens) * 100 : 0; const colors = [ 'bg-blue-500', 'bg-green-500', 'bg-purple-500', 'bg-orange-500', 'bg-pink-500' ]; return (
{type.request_type} {formatNumber(type.total_tokens)} tokens ({percentage.toFixed(1)}%)
); })}
{/* Usage by Organization */}

Uso por Organización

{data.by_organization.map((org) => ( ))}
Organización Tokens Requests Usuarios Activos Costo USD
{org.org_name} {formatNumber(org.total_tokens)} {formatNumber(org.requests)} {org.active_users} {formatCurrency(org.cost_usd)}
{/* Top Users */}

Top 20 Usuarios por Consumo

{data.top_users.map((user, idx) => ( ))}
Usuario Organización Rol Tokens Requests Costo USD
{user.full_name}
{user.email}
{user.org_name} {user.role} {formatNumber(user.total_tokens)} {formatNumber(user.requests)} {formatCurrency(user.cost_usd)}
{/* Student Chat Usage Detail */} {data.student_chat_usage.length > 0 && (

Detalle de Uso del Chat por Alumnos (Top 50)

{data.student_chat_usage.map((student) => ( ))}
Alumno Organización Tokens Requests Costo USD Último Chat
{student.full_name}
{student.email}
{student.org_name} {formatNumber(student.total_tokens)} {formatNumber(student.chat_requests)} {formatCurrency(student.cost_usd)} {new Date(student.last_chat).toLocaleDateString('es-ES')}
)}
); }