Files
bakery-ia/frontend/src/pages/app/settings/subscription/SubscriptionPage.tsx
2025-09-01 19:21:12 +02:00

855 lines
36 KiB
TypeScript

/**
* Subscription Management Page
* Allows users to view current subscription, billing details, and upgrade plans
*/
import React, { useState, useEffect } from 'react';
import {
Card,
Button,
Badge,
Modal
} from '../../../../components/ui';
import { PageHeader } from '../../../../components/layout';
import {
CreditCard,
Users,
MapPin,
Package,
TrendingUp,
Calendar,
CheckCircle,
AlertCircle,
ArrowRight,
Crown,
Star,
Zap,
X,
RefreshCw,
Settings,
Download,
ExternalLink
} from 'lucide-react';
import { useAuth } from '../../../../hooks/api/useAuth';
import { useBakeryStore } from '../../../../stores/bakery.store';
import { useToast } from '../../../../hooks/ui/useToast';
import {
subscriptionService,
type UsageSummary,
type AvailablePlans
} from '../../../../services/api';
import { isMockMode, getMockSubscription } from '../../../../config/mock.config';
interface PlanComparisonProps {
plans: AvailablePlans['plans'];
currentPlan: string;
onUpgrade: (planKey: string) => void;
}
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 (
<div className={`w-full bg-[var(--bg-tertiary)] border border-[var(--border-secondary)] rounded-full h-3 ${className}`}>
<div
className={`${getProgressColor()} h-full rounded-full transition-all duration-500 relative`}
style={{ width: `${Math.min(100, Math.max(0, value))}%` }}
>
<div className="absolute inset-0 bg-gradient-to-r from-transparent to-white/20 rounded-full"></div>
</div>
</div>
);
};
// Tabs implementation
interface TabsProps {
defaultValue: string;
className?: string;
children: React.ReactNode;
}
interface TabsListProps {
className?: string;
children: React.ReactNode;
}
interface TabsTriggerProps {
value: string;
children: React.ReactNode;
className?: string;
}
interface TabsContentProps {
value: string;
children: React.ReactNode;
className?: string;
}
const TabsContext = React.createContext<{ activeTab: string; setActiveTab: (value: string) => void } | null>(null);
const Tabs: React.FC<TabsProps> & {
List: React.FC<TabsListProps>;
Trigger: React.FC<TabsTriggerProps>;
Content: React.FC<TabsContentProps>;
} = ({
defaultValue,
className = '',
children
}) => {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<div className={className}>
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
</div>
);
};
const TabsList: React.FC<TabsListProps> = ({ className = '', children }) => {
return (
<div className={`flex border-b border-[var(--border-primary)] bg-[var(--bg-primary)] ${className}`}>
{children}
</div>
);
};
const TabsTrigger: React.FC<TabsTriggerProps> = ({ value, children, className = '' }) => {
const context = React.useContext(TabsContext);
if (!context) throw new Error('TabsTrigger must be used within Tabs');
const { activeTab, setActiveTab } = context;
const isActive = activeTab === value;
return (
<button
onClick={() => setActiveTab(value)}
className={`px-6 py-4 text-sm font-medium border-b-2 transition-all duration-200 relative flex items-center ${
isActive
? 'text-[var(--color-primary)] border-[var(--color-primary)] bg-[var(--bg-primary)]'
: 'text-[var(--text-secondary)] border-transparent hover:text-[var(--text-primary)] hover:border-[var(--color-primary)]/30 hover:bg-[var(--bg-secondary)]'
} ${className}`}
>
{children}
</button>
);
};
const TabsContent: React.FC<TabsContentProps> = ({ value, children, className = '' }) => {
const context = React.useContext(TabsContext);
if (!context) throw new Error('TabsContent must be used within Tabs');
const { activeTab } = context;
if (activeTab !== value) return null;
return (
<div className={`bg-[var(--bg-primary)] rounded-b-lg p-6 ${className}`}>
{children}
</div>
);
};
Tabs.List = TabsList;
Tabs.Trigger = TabsTrigger;
Tabs.Content = TabsContent;
const PlanComparison: React.FC<PlanComparisonProps> = ({ plans, currentPlan, onUpgrade }) => {
const planOrder = ['starter', 'professional', 'enterprise'];
const sortedPlans = Object.entries(plans).sort(([a], [b]) =>
planOrder.indexOf(a) - planOrder.indexOf(b)
);
const getPlanColor = (planKey: string) => {
switch (planKey) {
case 'starter': return 'border-blue-500/30 bg-blue-500/5';
case 'professional': return 'border-purple-500/30 bg-purple-500/5';
case 'enterprise': return 'border-amber-500/30 bg-amber-500/5';
default: return 'border-[var(--border-primary)] bg-[var(--bg-secondary)]';
}
};
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{sortedPlans.map(([planKey, plan]) => (
<Card
key={planKey}
className={`relative p-6 ${getPlanColor(planKey)} ${
currentPlan === planKey ? 'ring-2 ring-[var(--color-primary)]' : ''
}`}
>
{plan.popular && (
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
<Badge variant="primary" className="px-3 py-1">
<Star className="w-3 h-3 mr-1" />
Más Popular
</Badge>
</div>
)}
<div className="text-center mb-6">
<h3 className="text-xl font-bold text-[var(--text-primary)] mb-2">{plan.name}</h3>
<div className="text-3xl font-bold text-[var(--color-primary)] mb-1">
{subscriptionService.formatPrice(plan.monthly_price)}
<span className="text-lg text-[var(--text-secondary)]">/mes</span>
</div>
<p className="text-sm text-[var(--text-secondary)]">{plan.description}</p>
</div>
<div className="space-y-3 mb-6">
<div className="flex items-center gap-2 text-sm">
<Users className="w-4 h-4 text-[var(--color-primary)]" />
<span>{plan.max_users === -1 ? 'Usuarios ilimitados' : `${plan.max_users} usuarios`}</span>
</div>
<div className="flex items-center gap-2 text-sm">
<MapPin className="w-4 h-4 text-[var(--color-primary)]" />
<span>{plan.max_locations === -1 ? 'Ubicaciones ilimitadas' : `${plan.max_locations} ubicación${plan.max_locations > 1 ? 'es' : ''}`}</span>
</div>
<div className="flex items-center gap-2 text-sm">
<Package className="w-4 h-4 text-[var(--color-primary)]" />
<span>{plan.max_products === -1 ? 'Productos ilimitados' : `${plan.max_products} productos`}</span>
</div>
</div>
{currentPlan === planKey ? (
<Badge variant="success" className="w-full justify-center py-2">
<CheckCircle className="w-4 h-4 mr-2" />
Plan Actual
</Badge>
) : (
<Button
variant={plan.popular ? 'primary' : 'outline'}
className="w-full"
onClick={() => onUpgrade(planKey)}
>
{plan.contact_sales ? 'Contactar Ventas' : 'Cambiar Plan'}
<ArrowRight className="w-4 h-4 ml-2" />
</Button>
)}
</Card>
))}
</div>
);
};
const SubscriptionPage: React.FC = () => {
const { user, tenant_id } = useAuth();
const { currentTenant } = useBakeryStore();
const toast = useToast();
const [usageSummary, setUsageSummary] = useState<UsageSummary | null>(null);
const [availablePlans, setAvailablePlans] = useState<AvailablePlans | null>(null);
const [loading, setLoading] = useState(true);
const [upgradeDialogOpen, setUpgradeDialogOpen] = useState(false);
const [selectedPlan, setSelectedPlan] = useState<string>('');
const [upgrading, setUpgrading] = useState(false);
useEffect(() => {
if (currentTenant?.id || tenant_id || isMockMode()) {
loadSubscriptionData();
}
}, [currentTenant, tenant_id]);
const loadSubscriptionData = async () => {
let tenantId = currentTenant?.id || tenant_id;
// In mock mode, use the mock tenant ID if no real tenant is available
if (isMockMode() && !tenantId) {
tenantId = getMockSubscription().tenant_id;
console.log('🧪 Mock mode: Using mock tenant ID:', tenantId);
}
console.log('📊 Loading subscription data for tenant:', tenantId, '| Mock mode:', isMockMode());
if (!tenantId) return;
try {
setLoading(true);
const [usage, plans] = await Promise.all([
subscriptionService.getUsageSummary(tenantId),
subscriptionService.getAvailablePlans()
]);
setUsageSummary(usage);
setAvailablePlans(plans);
} catch (error) {
console.error('Error loading subscription data:', error);
toast.error("No se pudo cargar la información de suscripción");
} finally {
setLoading(false);
}
};
const handleUpgradeClick = (planKey: string) => {
setSelectedPlan(planKey);
setUpgradeDialogOpen(true);
};
const handleUpgradeConfirm = async () => {
let tenantId = currentTenant?.id || tenant_id;
// In mock mode, use the mock tenant ID if no real tenant is available
if (isMockMode() && !tenantId) {
tenantId = getMockSubscription().tenant_id;
}
if (!tenantId || !selectedPlan) return;
try {
setUpgrading(true);
const validation = await subscriptionService.validatePlanUpgrade(
tenantId,
selectedPlan
);
if (!validation.can_upgrade) {
toast.error(validation.reason);
return;
}
const result = await subscriptionService.upgradePlan(tenantId, selectedPlan);
if (result.success) {
toast.success(result.message);
await loadSubscriptionData();
setUpgradeDialogOpen(false);
setSelectedPlan('');
} else {
toast.error('Error al cambiar el plan');
}
} catch (error) {
console.error('Error upgrading plan:', error);
toast.error('Error al procesar el cambio de plan');
} finally {
setUpgrading(false);
}
};
if (loading) {
return (
<div className="space-y-6">
<PageHeader
title="Suscripción"
description="Gestiona tu plan de suscripción y facturación"
icon={CreditCard}
loading={loading}
/>
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex flex-col items-center gap-4">
<RefreshCw className="w-8 h-8 animate-spin text-[var(--color-primary)]" />
<p className="text-[var(--text-secondary)]">Cargando información de suscripción...</p>
</div>
</div>
</div>
);
}
if (!usageSummary || !availablePlans) {
return (
<div className="space-y-6">
<PageHeader
title="Suscripción"
description="Gestiona tu plan de suscripción y facturación"
icon={CreditCard}
error="No se pudo cargar la información de suscripción"
onRefresh={loadSubscriptionData}
showRefreshButton
/>
<div className="flex items-center justify-center min-h-[400px]">
<div className="flex flex-col items-center gap-4">
<AlertCircle className="w-12 h-12 text-[var(--text-tertiary)]" />
<div className="text-center">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">No se pudo cargar la información</h3>
<p className="text-[var(--text-secondary)] mb-4">Hubo un problema al cargar los datos de suscripción</p>
<Button onClick={loadSubscriptionData} variant="primary">
<RefreshCw className="w-4 h-4 mr-2" />
Reintentar
</Button>
</div>
</div>
</div>
</div>
);
}
const nextBillingDate = usageSummary.next_billing_date
? new Date(usageSummary.next_billing_date).toLocaleDateString('es-ES', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
: 'No disponible';
const planInfo = subscriptionService.getPlanDisplayInfo(usageSummary.plan);
return (
<div className="space-y-6">
<PageHeader
title="Suscripción"
subtitle={`Plan ${planInfo.name}`}
description="Gestiona tu plan de suscripción y facturación"
icon={CreditCard}
status={{
text: usageSummary.status === 'active' ? 'Activo' : usageSummary.status,
variant: usageSummary.status === 'active' ? 'success' : 'default'
}}
actions={[
{
id: 'manage-billing',
label: 'Gestionar Facturación',
icon: ExternalLink,
onClick: () => window.open('https://billing.bakery.com', '_blank'),
variant: 'outline'
},
{
id: 'download-invoice',
label: 'Descargar Factura',
icon: Download,
onClick: () => console.log('Download latest invoice'),
variant: 'outline'
}
]}
metadata={[
{
id: 'next-billing',
label: 'Próxima facturación',
value: nextBillingDate,
icon: Calendar
},
{
id: 'monthly-cost',
label: 'Coste mensual',
value: subscriptionService.formatPrice(usageSummary.monthly_price),
icon: CreditCard
}
]}
onRefresh={loadSubscriptionData}
showRefreshButton
/>
{/* Quick Stats Overview */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<div className="flex items-center gap-4">
<div className="p-3 bg-blue-500/10 rounded-xl border border-blue-500/20">
<Users className="w-6 h-6 text-blue-500" />
</div>
<div className="flex-1">
<p className="text-sm text-[var(--text-secondary)] mb-1">Usuarios</p>
<p className="text-xl font-bold text-[var(--text-primary)]">
{usageSummary.usage.users.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}</span>
</p>
</div>
</div>
</Card>
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<div className="flex items-center gap-4">
<div className="p-3 bg-green-500/10 rounded-xl border border-green-500/20">
<MapPin className="w-6 h-6 text-green-500" />
</div>
<div className="flex-1">
<p className="text-sm text-[var(--text-secondary)] mb-1">Ubicaciones</p>
<p className="text-xl font-bold text-[var(--text-primary)]">
{usageSummary.usage.locations.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}</span>
</p>
</div>
</div>
</Card>
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<div className="flex items-center gap-4">
<div className="p-3 bg-purple-500/10 rounded-xl border border-purple-500/20">
<Package className="w-6 h-6 text-purple-500" />
</div>
<div className="flex-1">
<p className="text-sm text-[var(--text-secondary)] mb-1">Productos</p>
<p className="text-xl font-bold text-[var(--text-primary)]">
{usageSummary.usage.products.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.products.unlimited ? '∞' : usageSummary.usage.products.limit}</span>
</p>
</div>
</div>
</Card>
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<div className="flex items-center gap-4">
<div className="p-3 bg-yellow-500/10 rounded-xl border border-yellow-500/20">
<TrendingUp className="w-6 h-6 text-yellow-500" />
</div>
<div className="flex-1">
<p className="text-sm text-[var(--text-secondary)] mb-1">Estado</p>
<Badge
variant={usageSummary.status === 'active' ? 'success' : 'default'}
className="text-sm font-medium"
>
{usageSummary.status === 'active' ? 'Activo' : usageSummary.status}
</Badge>
</div>
</div>
</Card>
</div>
<Card className="overflow-hidden">
<Tabs defaultValue="overview">
<Tabs.List>
<Tabs.Trigger value="overview">
<TrendingUp className="w-4 h-4 mr-2" />
Resumen
</Tabs.Trigger>
<Tabs.Trigger value="usage">
<Users className="w-4 h-4 mr-2" />
Uso
</Tabs.Trigger>
<Tabs.Trigger value="plans">
<Crown className="w-4 h-4 mr-2" />
Planes
</Tabs.Trigger>
<Tabs.Trigger value="billing">
<CreditCard className="w-4 h-4 mr-2" />
Facturación
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Current Plan Summary */}
<Card className="p-6 lg:col-span-2 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<h3 className="text-lg font-semibold mb-6 flex items-center text-[var(--text-primary)]">
<Crown className="w-5 h-5 mr-2 text-yellow-500" />
Tu Plan Actual
</h3>
<div className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Plan</span>
<div className="flex items-center gap-2">
<span className="font-semibold text-[var(--text-primary)]">{planInfo.name}</span>
{usageSummary.plan === 'professional' && (
<Badge variant="primary" size="sm">
<Star className="w-3 h-3 mr-1" />
Popular
</Badge>
)}
</div>
</div>
</div>
<div className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Precio</span>
<span className="font-semibold text-[var(--text-primary)]">{subscriptionService.formatPrice(usageSummary.monthly_price)}/mes</span>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Estado</span>
<Badge variant={usageSummary.status === 'active' ? 'success' : 'error'}>
{usageSummary.status === 'active' ? 'Activo' : 'Inactivo'}
</Badge>
</div>
</div>
<div className="p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<span className="text-sm text-[var(--text-secondary)]">Próxima facturación</span>
<span className="font-medium text-[var(--text-primary)]">{nextBillingDate}</span>
</div>
</div>
</div>
</div>
</Card>
{/* Quick Actions */}
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<h3 className="text-lg font-semibold mb-4 text-[var(--text-primary)]">
Acciones Rápidas
</h3>
<div className="space-y-3">
<Button variant="outline" className="w-full justify-start text-sm" onClick={() => window.open('https://billing.bakery.com', '_blank')}>
<ExternalLink className="w-4 h-4 mr-2" />
Portal de Facturación
</Button>
<Button variant="outline" className="w-full justify-start text-sm" onClick={() => console.log('Download invoice')}>
<Download className="w-4 h-4 mr-2" />
Descargar Facturas
</Button>
<Button variant="outline" className="w-full justify-start text-sm">
<Settings className="w-4 h-4 mr-2" />
Configurar Alertas
</Button>
</div>
</Card>
</div>
{/* Usage at a Glance */}
<Card className="p-6 border border-[var(--border-primary)] bg-[var(--bg-primary)]">
<h3 className="text-lg font-semibold mb-6 flex items-center text-[var(--text-primary)]">
<TrendingUp className="w-5 h-5 mr-2 text-orange-500" />
Uso de Recursos
</h3>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* Users */}
<div className="space-y-4 p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-blue-500/10 rounded-lg border border-blue-500/20">
<Users className="w-4 h-4 text-blue-500" />
</div>
<span className="font-medium text-[var(--text-primary)]">Usuarios</span>
</div>
<span className="text-sm font-bold text-[var(--text-primary)]">
{usageSummary.usage.users.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.users.unlimited ? '∞' : usageSummary.usage.users.limit}</span>
</span>
</div>
<ProgressBar value={usageSummary.usage.users.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)] flex items-center justify-between">
<span>{usageSummary.usage.users.usage_percentage}% utilizado</span>
<span className="font-medium">{usageSummary.usage.users.unlimited ? 'Ilimitado' : `${usageSummary.usage.users.limit - usageSummary.usage.users.current} restantes`}</span>
</p>
</div>
{/* Locations */}
<div className="space-y-4 p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-green-500/10 rounded-lg border border-green-500/20">
<MapPin className="w-4 h-4 text-green-500" />
</div>
<span className="font-medium text-[var(--text-primary)]">Ubicaciones</span>
</div>
<span className="text-sm font-bold text-[var(--text-primary)]">
{usageSummary.usage.locations.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.locations.unlimited ? '∞' : usageSummary.usage.locations.limit}</span>
</span>
</div>
<ProgressBar value={usageSummary.usage.locations.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)] flex items-center justify-between">
<span>{usageSummary.usage.locations.usage_percentage}% utilizado</span>
<span className="font-medium">{usageSummary.usage.locations.unlimited ? 'Ilimitado' : `${usageSummary.usage.locations.limit - usageSummary.usage.locations.current} restantes`}</span>
</p>
</div>
{/* Products */}
<div className="space-y-4 p-4 bg-[var(--bg-secondary)] border border-[var(--border-secondary)] rounded-lg">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="p-2 bg-purple-500/10 rounded-lg border border-purple-500/20">
<Package className="w-4 h-4 text-purple-500" />
</div>
<span className="font-medium text-[var(--text-primary)]">Productos</span>
</div>
<span className="text-sm font-bold text-[var(--text-primary)]">
{usageSummary.usage.products.current}<span className="text-[var(--text-tertiary)]">/{usageSummary.usage.products.unlimited ? '∞' : usageSummary.usage.products.limit}</span>
</span>
</div>
<ProgressBar value={usageSummary.usage.products.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)] flex items-center justify-between">
<span>{usageSummary.usage.products.usage_percentage}% utilizado</span>
<span className="font-medium">{usageSummary.usage.products.unlimited ? 'Ilimitado' : 'Ilimitado'}</span>
</p>
</div>
</div>
</Card>
</Tabs.Content>
<Tabs.Content value="usage">
<div className="space-y-6">
<Card className="p-6">
<h3 className="text-lg font-semibold mb-6 text-[var(--text-primary)]">Detalles de Uso</h3>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Users Usage */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Users className="w-5 h-5 text-blue-500" />
<h4 className="font-medium text-[var(--text-primary)]">Gestión de Usuarios</h4>
</div>
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Usuarios activos</span>
<span className="font-medium">{usageSummary.usage.users.current}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Límite del plan</span>
<span className="font-medium">{usageSummary.usage.users.unlimited ? 'Ilimitado' : usageSummary.usage.users.limit}</span>
</div>
<ProgressBar value={usageSummary.usage.users.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)]">
{usageSummary.usage.users.usage_percentage}% de capacidad utilizada
</p>
</div>
</div>
{/* Locations Usage */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<MapPin className="w-5 h-5 text-green-500" />
<h4 className="font-medium text-[var(--text-primary)]">Ubicaciones</h4>
</div>
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Ubicaciones activas</span>
<span className="font-medium">{usageSummary.usage.locations.current}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Límite del plan</span>
<span className="font-medium">{usageSummary.usage.locations.unlimited ? 'Ilimitado' : usageSummary.usage.locations.limit}</span>
</div>
<ProgressBar value={usageSummary.usage.locations.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)]">
{usageSummary.usage.locations.usage_percentage}% de capacidad utilizada
</p>
</div>
</div>
{/* Products Usage */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Package className="w-5 h-5 text-purple-500" />
<h4 className="font-medium text-[var(--text-primary)]">Productos</h4>
</div>
<div className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Productos registrados</span>
<span className="font-medium">{usageSummary.usage.products.current}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-[var(--text-secondary)]">Límite del plan</span>
<span className="font-medium">{usageSummary.usage.products.unlimited ? 'Ilimitado' : usageSummary.usage.products.limit}</span>
</div>
<ProgressBar value={usageSummary.usage.products.usage_percentage} />
<p className="text-xs text-[var(--text-secondary)]">
{usageSummary.usage.products.usage_percentage}% de capacidad utilizada
</p>
</div>
</div>
</div>
</Card>
</div>
</Tabs.Content>
<Tabs.Content value="plans">
<div className="space-y-6">
<div className="text-center">
<h3 className="text-xl font-semibold text-[var(--text-primary)] mb-2">
Planes de Suscripción
</h3>
<p className="text-[var(--text-secondary)]">
Elige el plan que mejor se adapte a las necesidades de tu panadería
</p>
</div>
<PlanComparison
plans={availablePlans.plans}
currentPlan={usageSummary.plan}
onUpgrade={handleUpgradeClick}
/>
</div>
</Tabs.Content>
<Tabs.Content value="billing">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="p-6">
<h3 className="text-lg font-semibold mb-6 text-[var(--text-primary)]">
Información de Facturación
</h3>
<div className="space-y-4">
<div className="flex justify-between p-3 bg-[var(--bg-secondary)] rounded-lg">
<span className="text-[var(--text-secondary)]">Plan actual:</span>
<span className="font-medium">{planInfo.name}</span>
</div>
<div className="flex justify-between p-3 bg-[var(--bg-secondary)] rounded-lg">
<span className="text-[var(--text-secondary)]">Precio mensual:</span>
<span className="font-medium">{subscriptionService.formatPrice(usageSummary.monthly_price)}</span>
</div>
<div className="flex justify-between p-3 bg-[var(--bg-secondary)] rounded-lg">
<span className="text-[var(--text-secondary)]">Próxima facturación:</span>
<span className="font-medium flex items-center">
<Calendar className="w-4 h-4 mr-2" />
{nextBillingDate}
</span>
</div>
</div>
</Card>
<Card className="p-6">
<h3 className="text-lg font-semibold mb-6 text-[var(--text-primary)]">
Métodos de Pago
</h3>
<div className="space-y-4">
<div className="flex items-center gap-4 p-4 border border-[var(--border-primary)] rounded-lg">
<CreditCard className="w-8 h-8 text-[var(--text-tertiary)]" />
<div className="flex-1">
<div className="font-medium"> 4242</div>
<div className="text-sm text-[var(--text-secondary)]">Visa terminada en 4242</div>
</div>
<Badge variant="success">Principal</Badge>
</div>
<Button variant="outline" className="w-full">
<Settings className="w-4 h-4 mr-2" />
Gestionar Métodos de Pago
</Button>
</div>
</Card>
</div>
</Tabs.Content>
</Tabs>
</Card>
{/* Upgrade Modal */}
{upgradeDialogOpen && selectedPlan && availablePlans && (
<Modal
isOpen={upgradeDialogOpen}
onClose={() => setUpgradeDialogOpen(false)}
title="Confirmar Cambio de Plan"
>
<div className="space-y-4">
<p className="text-[var(--text-secondary)]">
¿Estás seguro de que quieres cambiar tu plan de suscripción?
</p>
{availablePlans.plans[selectedPlan] && (
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg space-y-2">
<div className="flex justify-between">
<span>Plan actual:</span>
<span>{planInfo.name}</span>
</div>
<div className="flex justify-between">
<span>Nuevo plan:</span>
<span>{availablePlans.plans[selectedPlan].name}</span>
</div>
<div className="flex justify-between font-medium">
<span>Nuevo precio:</span>
<span>{subscriptionService.formatPrice(availablePlans.plans[selectedPlan].monthly_price)}/mes</span>
</div>
</div>
)}
<div className="flex gap-2 pt-4">
<Button
variant="outline"
onClick={() => setUpgradeDialogOpen(false)}
className="flex-1"
>
Cancelar
</Button>
<Button
variant="primary"
onClick={handleUpgradeConfirm}
disabled={upgrading}
className="flex-1"
>
{upgrading ? 'Procesando...' : 'Confirmar Cambio'}
</Button>
</div>
</div>
</Modal>
)}
</div>
);
};
export default SubscriptionPage;