import React, { useState, useEffect } from 'react'; import { Crown, Users, MapPin, Package, TrendingUp, RefreshCw, AlertCircle, CheckCircle, ArrowRight, Star, ExternalLink, Download, CreditCard, X, Activity, Database, Zap, HardDrive, ShoppingCart, ChefHat, Settings, Sparkles, ChevronDown, ChevronUp } from 'lucide-react'; import { Button, Card, Badge, Modal } from '../../../../components/ui'; import { DialogModal } from '../../../../components/ui/DialogModal/DialogModal'; import { PageHeader } from '../../../../components/layout'; import { useAuthUser } from '../../../../stores/auth.store'; import { useCurrentTenant } from '../../../../stores'; import { showToast } from '../../../../utils/toast'; import { subscriptionService, type UsageSummary, type AvailablePlans } from '../../../../api'; import { useSubscriptionEvents } from '../../../../contexts/SubscriptionEventsContext'; import { SubscriptionPricingCards } from '../../../../components/subscription/SubscriptionPricingCards'; import { PlanComparisonTable, ROICalculator, UsageMetricCard } from '../../../../components/subscription'; import { useSubscription } from '../../../../hooks/useSubscription'; import { trackSubscriptionPageViewed, trackUpgradeCTAClicked, trackUsageMetricViewed } from '../../../../utils/subscriptionAnalytics'; import { useTranslation } from 'react-i18next'; const SubscriptionPage: React.FC = () => { const user = useAuthUser(); const currentTenant = useCurrentTenant(); const { notifySubscriptionChanged } = useSubscriptionEvents(); const { t } = useTranslation('subscription'); 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); const [invoicesLoaded, setInvoicesLoaded] = useState(false); // New state for enhanced features const [showComparison, setShowComparison] = useState(false); const [showROI, setShowROI] = useState(false); // Use new subscription hook for usage forecast data const { subscription: subscriptionData, usage: forecastUsage, forecast } = useSubscription(); // Load subscription data on component mount React.useEffect(() => { loadSubscriptionData(); loadInvoices(); }, []); // Track page view useEffect(() => { if (usageSummary) { trackSubscriptionPageViewed(usageSummary.plan); } }, [usageSummary]); // Track high usage metrics useEffect(() => { if (forecast?.metrics) { forecast.metrics.forEach(metric => { if (metric.usage_percentage >= 80) { trackUsageMetricViewed( metric.metric, metric.current, metric.limit, metric.usage_percentage, metric.days_until_breach ); } }); } }, [forecast]); const loadSubscriptionData = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { showToast.error('No se encontró información del tenant'); return; } try { setSubscriptionLoading(true); const [usage, plans] = await Promise.all([ subscriptionService.getUsageSummary(tenantId), subscriptionService.fetchAvailablePlans() ]); // CRITICAL: No more mock data - show real errors instead if (!usage || !usage.usage) { throw new Error('No subscription found. Please contact support or create a new subscription.'); } setUsageSummary(usage); setAvailablePlans(plans); } catch (error) { console.error('Error loading subscription data:', error); showToast.error( error instanceof Error && error.message.includes('No subscription') ? error.message : "No se pudo cargar la información de suscripción. Por favor, contacte con soporte." ); } finally { setSubscriptionLoading(false); } }; const handleUpgradeClick = (planKey: string, source: string = 'pricing_cards') => { if (usageSummary) { trackUpgradeCTAClicked(usageSummary.plan, planKey, source); } setSelectedPlan(planKey); setUpgradeDialogOpen(true); }; const handleUpgradeConfirm = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId || !selectedPlan) { showToast.error('Información de tenant no disponible'); return; } try { setUpgrading(true); const validation = await subscriptionService.validatePlanUpgrade( tenantId, selectedPlan ); if (!validation.can_upgrade) { showToast.error(validation.reason || 'No se puede actualizar el plan'); return; } const result = await subscriptionService.upgradePlan(tenantId, selectedPlan); if (result.success) { showToast.success(result.message); // Invalidate cache to ensure fresh data on next fetch subscriptionService.invalidateCache(); // Broadcast subscription change event to refresh sidebar and other components notifySubscriptionChanged(); await loadSubscriptionData(); setUpgradeDialogOpen(false); setSelectedPlan(''); } else { showToast.error('Error al cambiar el plan'); } } catch (error) { console.error('Error upgrading plan:', error); showToast.error('Error al procesar el cambio de plan'); } finally { setUpgrading(false); } }; const handleCancellationClick = () => { setCancellationDialogOpen(true); }; const handleCancelSubscription = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { showToast.error('Información de tenant no disponible'); 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' }); showToast.success( `Suscripción cancelada. Acceso de solo lectura a partir del ${effectiveDate} (${daysRemaining} días restantes)` ); } await loadSubscriptionData(); setCancellationDialogOpen(false); } catch (error) { console.error('Error cancelling subscription:', error); showToast.error('Error al cancelar la suscripción'); } finally { setCancelling(false); } }; const loadInvoices = async () => { const tenantId = currentTenant?.id || user?.tenant_id; if (!tenantId) { return; } try { setInvoicesLoading(true); const fetchedInvoices = await subscriptionService.getInvoices(tenantId); setInvoices(fetchedInvoices); setInvoicesLoaded(true); } catch (error) { console.error('Error loading invoices:', error); // Don't show error toast on initial load, just log it if (invoicesLoaded) { showToast.error('Error al cargar las facturas'); } } finally { setInvoicesLoading(false); } }; const handleDownloadInvoice = (invoice: any) => { if (invoice.invoice_pdf) { window.open(invoice.invoice_pdf, '_blank'); } else if (invoice.hosted_invoice_url) { window.open(invoice.hosted_invoice_url, '_blank'); } else { showToast.warning('No hay PDF disponible para esta factura'); } }; 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.status === 'active' ? 'Activo' : usageSummary.status}
Plan {usageSummary.plan}
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', year: 'numeric' })}
Ciclo de Facturación {usageSummary.billing_cycle === 'monthly' ? 'Mensual' : 'Anual'}
{/* 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 ?? 0}

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

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

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

{/* Product & Inventory Metrics */}

Productos & Inventario

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

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

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

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

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

{usageSummary.usage.suppliers.usage_percentage}% utilizado {usageSummary.usage.suppliers.unlimited ? 'Ilimitado' : `${(usageSummary.usage.suppliers.limit ?? 0) - 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 ?? 0}

{usageSummary.usage.training_jobs_today.usage_percentage}% utilizado {usageSummary.usage.training_jobs_today.unlimited ? 'Ilimitado' : `${(usageSummary.usage.training_jobs_today.limit ?? 0) - 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 ?? 0}

{usageSummary.usage.forecasts_today.usage_percentage}% utilizado {usageSummary.usage.forecasts_today.unlimited ? 'Ilimitado' : `${(usageSummary.usage.forecasts_today.limit ?? 0) - 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 ?? 0}

{usageSummary.usage.api_calls_this_hour.usage_percentage}% utilizado {usageSummary.usage.api_calls_this_hour.unlimited ? 'Ilimitado' : `${(usageSummary.usage.api_calls_this_hour.limit ?? 0) - 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 ?? 0) - usageSummary.usage.file_storage_used_gb.current).toFixed(2)} GB restantes`}

{/* Enhanced Usage Metrics with Predictive Analytics */} {forecastUsage && forecast && (

Análisis Predictivo de Uso

Predicciones basadas en tendencias de crecimiento

{/* Products */} handleUpgradeClick('professional', 'usage_metric_products')} icon={} /> {/* Users */} handleUpgradeClick('professional', 'usage_metric_users')} icon={} /> {/* Locations */} handleUpgradeClick('professional', 'usage_metric_locations')} icon={} /> {/* Training Jobs */} handleUpgradeClick('professional', 'usage_metric_training')} icon={} /> {/* Forecasts */} handleUpgradeClick('professional', 'usage_metric_forecasts')} icon={} /> {/* Storage */} handleUpgradeClick('professional', 'usage_metric_storage')} icon={} />
)} {/* High Usage Warning Banner (Starter tier with >80% usage) */} {usageSummary.plan === 'starter' && forecastUsage && forecastUsage.highUsageMetrics.length > 0 && (

¡Estás superando el plan Starter!

Estás usando {forecastUsage.highUsageMetrics.length} métrica{forecastUsage.highUsageMetrics.length > 1 ? 's' : ''} con más del 80% de capacidad. Actualiza a Professional para obtener 10 veces más capacidad y funciones avanzadas.

)} {/* ROI Calculator (Starter tier only) */} {usageSummary.plan === 'starter' && (

Calcula Tus Ahorros

{showROI && ( handleUpgradeClick('professional', 'roi_calculator')} /> )}
)} {/* Plan Comparison */} {availablePlans && (

Comparar Planes

{showComparison && ( handleUpgradeClick(tier, 'comparison_table')} mode="inline" /> )}
)} {/* Available Plans */}

Planes Disponibles

handleUpgradeClick(plan, 'pricing_cards')} showPilotBanner={false} />
{/* Invoices Section */}

Historial de Facturas

{invoicesLoading && !invoicesLoaded ? (

Cargando facturas...

) : invoices.length === 0 ? (

No hay facturas disponibles

Las facturas aparecerán aquí una vez realizados los pagos

) : (
{invoices.map((invoice) => ( ))}
Fecha Descripción Monto Estado Acciones
{new Date(invoice.date).toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })} {invoice.description || 'Suscripción'} {subscriptionService.formatPrice(invoice.amount)} {invoice.status === 'paid' ? 'Pagada' : invoice.status === 'open' ? 'Pendiente' : invoice.status}
)}
{/* Subscription Management */}

Gestión de Suscripción

{/* Payment Method Card */}

Método de Pago

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

{/* Cancel Subscription Card */}

Cancelar Suscripción

Si cancelas, mantendrás acceso de solo lectura hasta el final de tu período de facturación actual.

{/* Additional Info */}

¿Necesitas ayuda?

Si tienes preguntas sobre tu suscripción o necesitas asistencia, contacta a nuestro equipo de soporte en{' '} support@bakery-ia.com

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

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

{availablePlans.plans[selectedPlan as keyof typeof availablePlans.plans] && usageSummary && (
Plan actual: {t(`plans.${usageSummary.plan}.name`, usageSummary.plan)}
Nuevo plan: {t(`plans.${selectedPlan}.name`, availablePlans.plans[selectedPlan as keyof typeof availablePlans.plans].name)}
Nuevo precio: {subscriptionService.formatPrice(availablePlans.plans[selectedPlan as keyof typeof availablePlans.plans].monthly_price)}/mes
)}
} type="confirm" onConfirm={handleUpgradeConfirm} onCancel={() => setUpgradeDialogOpen(false)} confirmLabel="Confirmar Cambio" cancelLabel="Cancelar" loading={upgrading} /> )} {/* Cancellation Dialog */} {cancellationDialogOpen && ( setCancellationDialogOpen(false)} title="Cancelar Suscripción" message={

¿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.

} type="warning" onConfirm={handleCancelSubscription} onCancel={() => setCancellationDialogOpen(false)} confirmLabel="Confirmar Cancelación" cancelLabel="Volver" loading={cancelling} /> )}
); }; export default SubscriptionPage;