import React, { useState } from 'react'; import { Crown, Users, MapPin, Package, TrendingUp, RefreshCw, AlertCircle, CheckCircle, ArrowRight, Star, ExternalLink, Download, CreditCard, X, Activity, Database, Zap, HardDrive, ShoppingCart, ChefHat } from 'lucide-react'; import { Button, Card, Badge, Modal } from '../../../../components/ui'; import { PageHeader } from '../../../../components/layout'; import { useAuthUser } from '../../../../stores/auth.store'; import { useCurrentTenant } from '../../../../stores'; import { useToast } from '../../../../hooks/ui/useToast'; import { subscriptionService, type UsageSummary, type AvailablePlans } from '../../../../api'; import { SubscriptionPricingCards } from '../../../../components/subscription/SubscriptionPricingCards'; const SubscriptionPage: React.FC = () => { const user = useAuthUser(); const currentTenant = useCurrentTenant(); const { addToast } = useToast(); const [usageSummary, setUsageSummary] = useState(null); const [availablePlans, setAvailablePlans] = useState(null); const [subscriptionLoading, setSubscriptionLoading] = useState(false); const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false); const [selectedPlan, setSelectedPlan] = useState(''); const [upgrading, setUpgrading] = useState(false); const [cancellationDialogOpen, setCancellationDialogOpen] = useState(false); const [cancelling, setCancelling] = useState(false); const [invoices, setInvoices] = useState([]); const [invoicesLoading, setInvoicesLoading] = useState(false); // Load subscription data on component mount React.useEffect(() => { loadSubscriptionData(); }, []); const loadSubscriptionData = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { addToast('No se encontró información del tenant', { type: 'error' }); return; } try { setSubscriptionLoading(true); const [usage, plans] = await Promise.all([ subscriptionService.getUsageSummary(tenantId), subscriptionService.fetchAvailablePlans() ]); // FIX: Handle demo mode or missing subscription data if (!usage || !usage.usage) { // If no usage data, likely a demo tenant - create mock data const mockUsage: UsageSummary = { plan: 'starter', status: 'active', billing_cycle: 'monthly', monthly_price: 0, next_billing_date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), usage: { users: { current: 1, limit: 5, unlimited: false, usage_percentage: 20 }, locations: { current: 1, limit: 1, unlimited: false, usage_percentage: 100 }, products: { current: 0, limit: 50, unlimited: false, usage_percentage: 0 }, recipes: { current: 0, limit: 50, unlimited: false, usage_percentage: 0 }, suppliers: { current: 0, limit: 20, unlimited: false, usage_percentage: 0 }, training_jobs_today: { current: 0, limit: 1, unlimited: false, usage_percentage: 0 }, forecasts_today: { current: 0, limit: 10, unlimited: false, usage_percentage: 0 }, api_calls_this_hour: { current: 0, limit: 100, unlimited: false, usage_percentage: 0 }, file_storage_used_gb: { current: 0, limit: 5, unlimited: false, usage_percentage: 0 } } }; setUsageSummary(mockUsage); } else { setUsageSummary(usage); } setAvailablePlans(plans); } catch (error) { console.error('Error loading subscription data:', error); addToast("No se pudo cargar la información de suscripción", { type: 'error' }); } finally { setSubscriptionLoading(false); } }; const handleUpgradeClick = (planKey: string) => { setSelectedPlan(planKey); setUpgradeDialogOpen(true); }; const handleUpgradeConfirm = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId || !selectedPlan) { addToast('Información de tenant no disponible', { type: 'error' }); return; } try { setUpgrading(true); const validation = await subscriptionService.validatePlanUpgrade( tenantId, selectedPlan ); if (!validation.can_upgrade) { addToast(validation.reason || 'No se puede actualizar el plan', { type: 'error' }); return; } const result = await subscriptionService.upgradePlan(tenantId, selectedPlan); if (result.success) { addToast(result.message, { type: 'success' }); await loadSubscriptionData(); setUpgradeDialogOpen(false); setSelectedPlan(''); } else { addToast('Error al cambiar el plan', { type: 'error' }); } } catch (error) { console.error('Error upgrading plan:', error); addToast('Error al procesar el cambio de plan', { type: 'error' }); } finally { setUpgrading(false); } }; const handleCancellationClick = () => { setCancellationDialogOpen(true); }; const handleCancelSubscription = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { addToast('Información de tenant no disponible', { type: 'error' }); return; } try { setCancelling(true); const result = await subscriptionService.cancelSubscription(tenantId, 'User requested cancellation'); if (result.success) { const daysRemaining = result.days_remaining; const effectiveDate = new Date(result.cancellation_effective_date).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' }); addToast( `Suscripción cancelada. Acceso de solo lectura a partir del ${effectiveDate} (${daysRemaining} días restantes)`, { type: 'success' } ); } await loadSubscriptionData(); setCancellationDialogOpen(false); } catch (error) { console.error('Error cancelling subscription:', error); addToast('Error al cancelar la suscripción', { type: 'error' }); } finally { setCancelling(false); } }; const loadInvoices = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { addToast('No se encontró información del tenant', { type: 'error' }); return; } try { setInvoicesLoading(true); // In a real implementation, this would call an API endpoint to get invoices // const invoices = await subscriptionService.getInvoices(tenantId); // For now, we'll simulate some invoices setInvoices([ { id: 'inv_001', date: '2023-10-01', amount: 49.00, status: 'paid', description: 'Plan Starter Mensual' }, { id: 'inv_002', date: '2023-09-01', amount: 49.00, status: 'paid', description: 'Plan Starter Mensual' }, { id: 'inv_003', date: '2023-08-01', amount: 49.00, status: 'paid', description: 'Plan Starter Mensual' }, ]); } catch (error) { console.error('Error loading invoices:', error); addToast('Error al cargar las facturas', { type: 'error' }); } finally { setInvoicesLoading(false); } }; const handleDownloadInvoice = (invoiceId: string) => { // In a real implementation, this would download the actual invoice console.log(`Downloading invoice: ${invoiceId}`); addToast(`Descargando factura ${invoiceId}`, { type: 'info' }); }; const ProgressBar: React.FC<{ value: number; className?: string }> = ({ value, className = '' }) => { const getProgressColor = () => { if (value >= 90) return 'bg-red-500'; if (value >= 80) return 'bg-yellow-500'; return 'bg-green-500'; }; return (
); }; return (
{subscriptionLoading ? (

Cargando información de suscripción...

) : !usageSummary || !availablePlans ? (

No se pudo cargar la información

Hubo un problema al cargar los datos de suscripción

) : ( <> {/* Current Plan Overview */}

Plan Actual: {usageSummary.plan}

{usageSummary.status === 'active' ? 'Activo' : usageSummary.status}
Precio Mensual {subscriptionService.formatPrice(usageSummary.monthly_price)}
Próxima Facturación {new Date(usageSummary.next_billing_date).toLocaleDateString('es-ES', { day: '2-digit', month: '2-digit' })}
Usuarios {usageSummary.usage.users.current}/{usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}
Ubicaciones {usageSummary.usage.locations.current}/{usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}
{/* Usage Details */}

Uso de Recursos

{/* Team & Organization Metrics */}

Equipo & Organización

{/* Users */}
Usuarios
{usageSummary.usage.users.current}/ {usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}

{usageSummary.usage.users.usage_percentage}% utilizado {usageSummary.usage.users.unlimited ? 'Ilimitado' : `${usageSummary.usage.users.limit - usageSummary.usage.users.current} restantes`}

{/* Locations */}
Ubicaciones
{usageSummary.usage.locations.current}/ {usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}

{usageSummary.usage.locations.usage_percentage}% utilizado {usageSummary.usage.locations.unlimited ? 'Ilimitado' : `${usageSummary.usage.locations.limit - usageSummary.usage.locations.current} restantes`}

{/* Product & Inventory Metrics */}

Productos & Inventario

{/* Products */}
Productos
{usageSummary.usage.products.current}/ {usageSummary.usage.products.unlimited ? '∞' : usageSummary.usage.products.limit}

{usageSummary.usage.products.usage_percentage}% utilizado {usageSummary.usage.products.unlimited ? 'Ilimitado' : `${usageSummary.usage.products.limit - usageSummary.usage.products.current} restantes`}

{/* Recipes */}
Recetas
{usageSummary.usage.recipes.current}/ {usageSummary.usage.recipes.unlimited ? '∞' : usageSummary.usage.recipes.limit}

{usageSummary.usage.recipes.usage_percentage}% utilizado {usageSummary.usage.recipes.unlimited ? 'Ilimitado' : `${usageSummary.usage.recipes.limit - usageSummary.usage.recipes.current} restantes`}

{/* Suppliers */}
Proveedores
{usageSummary.usage.suppliers.current}/ {usageSummary.usage.suppliers.unlimited ? '∞' : usageSummary.usage.suppliers.limit}

{usageSummary.usage.suppliers.usage_percentage}% utilizado {usageSummary.usage.suppliers.unlimited ? 'Ilimitado' : `${usageSummary.usage.suppliers.limit - usageSummary.usage.suppliers.current} restantes`}

{/* ML & Analytics Metrics (Daily) */}

IA & Analíticas (Uso Diario)

{/* Training Jobs Today */}
Entrenamientos IA Hoy
{usageSummary.usage.training_jobs_today.current}/ {usageSummary.usage.training_jobs_today.unlimited ? '∞' : usageSummary.usage.training_jobs_today.limit}

{usageSummary.usage.training_jobs_today.usage_percentage}% utilizado {usageSummary.usage.training_jobs_today.unlimited ? 'Ilimitado' : `${usageSummary.usage.training_jobs_today.limit - usageSummary.usage.training_jobs_today.current} restantes`}

{/* Forecasts Today */}
Pronósticos Hoy
{usageSummary.usage.forecasts_today.current}/ {usageSummary.usage.forecasts_today.unlimited ? '∞' : usageSummary.usage.forecasts_today.limit}

{usageSummary.usage.forecasts_today.usage_percentage}% utilizado {usageSummary.usage.forecasts_today.unlimited ? 'Ilimitado' : `${usageSummary.usage.forecasts_today.limit - usageSummary.usage.forecasts_today.current} restantes`}

{/* API & Storage Metrics */}

API & Almacenamiento

{/* API Calls This Hour */}
Llamadas API (Esta Hora)
{usageSummary.usage.api_calls_this_hour.current}/ {usageSummary.usage.api_calls_this_hour.unlimited ? '∞' : usageSummary.usage.api_calls_this_hour.limit}

{usageSummary.usage.api_calls_this_hour.usage_percentage}% utilizado {usageSummary.usage.api_calls_this_hour.unlimited ? 'Ilimitado' : `${usageSummary.usage.api_calls_this_hour.limit - usageSummary.usage.api_calls_this_hour.current} restantes`}

{/* File Storage */}
Almacenamiento
{usageSummary.usage.file_storage_used_gb.current.toFixed(2)}/ {usageSummary.usage.file_storage_used_gb.unlimited ? '∞' : `${usageSummary.usage.file_storage_used_gb.limit} GB`}

{usageSummary.usage.file_storage_used_gb.usage_percentage}% utilizado {usageSummary.usage.file_storage_used_gb.unlimited ? 'Ilimitado' : `${(usageSummary.usage.file_storage_used_gb.limit - usageSummary.usage.file_storage_used_gb.current).toFixed(2)} GB restantes`}

{/* Available Plans */}

Planes Disponibles

{/* Invoices Section */}

Historial de Facturas

{invoicesLoading ? (

Cargando facturas...

) : invoices.length === 0 ? (

No hay facturas disponibles

) : (
{invoices.map((invoice) => ( ))}
ID Fecha Descripción Monto Estado Acciones
{invoice.id} {invoice.date} {invoice.description} {subscriptionService.formatPrice(invoice.amount)} {invoice.status === 'paid' ? 'Pagada' : 'Pendiente'}
)}
{/* Subscription Management */}

Gestión de Suscripción

Cancelar Suscripción

Si cancelas tu suscripción, perderás acceso a las funcionalidades premium al final del período de facturación actual.

Método de Pago

Actualiza tu información de pago para asegurar la continuidad de tu servicio.

)} {/* Upgrade Modal */} {upgradeDialogOpen && selectedPlan && availablePlans && ( setUpgradeDialogOpen(false)} title="Confirmar Cambio de Plan" >

¿Estás seguro de que quieres cambiar tu plan de suscripción?

{availablePlans.plans[selectedPlan] && usageSummary && (
Plan actual: {usageSummary.plan}
Nuevo plan: {availablePlans.plans[selectedPlan].name}
Nuevo precio: {subscriptionService.formatPrice(availablePlans.plans[selectedPlan].monthly_price)}/mes
)}
)} {/* Cancellation Modal */} {cancellationDialogOpen && ( setCancellationDialogOpen(false)} title="Cancelar Suscripción" >

¿Estás seguro de que deseas cancelar tu suscripción? Esta acción no se puede deshacer.

Perderás acceso a las funcionalidades premium al final del período de facturación actual.

)}
); }; export default SubscriptionPage;