405 lines
22 KiB
TypeScript
405 lines
22 KiB
TypeScript
'use client';
|
||
|
||
import { useEffect, useState } from 'react';
|
||
import { Card, CardContent, CardHeader, CardTitle, Button } from '@horux/shared-ui';
|
||
import { CheckCircle2, Server, Cloud, Clock, ExternalLink } from 'lucide-react';
|
||
import { apiClient } from '@/lib/api/client';
|
||
import { subscribeMe, changeMyPlan, cancelMySubscription } from '@/lib/api/subscription';
|
||
|
||
type Despachoplan = 'trial' | 'business_control' | 'business_cloud' | 'mi_empresa' | 'mi_empresa_plus' | 'custom';
|
||
type PaidPlan = 'business_control' | 'business_cloud' | 'mi_empresa' | 'mi_empresa_plus';
|
||
|
||
interface SubscriptionInfo {
|
||
status: string;
|
||
plan: string;
|
||
amount: number;
|
||
currentPeriodStart: string | null;
|
||
currentPeriodEnd: string | null;
|
||
}
|
||
|
||
interface PlanInfo {
|
||
plan: Despachoplan;
|
||
dbMode: string;
|
||
trialEndsAt: string | null;
|
||
isTrialActive: boolean;
|
||
subscription: SubscriptionInfo | null;
|
||
}
|
||
|
||
function daysUntil(isoDate: string): number {
|
||
const diff = new Date(isoDate).getTime() - Date.now();
|
||
return Math.max(0, Math.ceil(diff / (1000 * 60 * 60 * 24)));
|
||
}
|
||
|
||
type Frequency = 'monthly' | 'annual';
|
||
|
||
export default function PlanesDespachoPage() {
|
||
const [planInfo, setPlanInfo] = useState<PlanInfo | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [busy, setBusy] = useState<null | PaidPlan | 'cancel'>(null);
|
||
const [message, setMessage] = useState<{ kind: 'ok' | 'err'; text: string } | null>(null);
|
||
// Toggle mensual/anual solo aplica a Mi Empresa y Mi Empresa+. Business
|
||
// Control y Enterprise siempre se cobran anual. Default monthly para
|
||
// bajar friction inicial; el descuento del 17% al pagar anual se
|
||
// muestra como CTA secundario.
|
||
const [meFreq, setMeFreq] = useState<Frequency>('monthly');
|
||
const [mePlusFreq, setMePlusFreq] = useState<Frequency>('monthly');
|
||
|
||
const fetchPlan = () => {
|
||
apiClient.get<PlanInfo>('/despachos/me/plan')
|
||
.then(res => setPlanInfo(res.data))
|
||
.catch(() => setPlanInfo(null));
|
||
};
|
||
|
||
useEffect(() => {
|
||
setLoading(true);
|
||
apiClient.get<PlanInfo>('/despachos/me/plan')
|
||
.then(res => setPlanInfo(res.data))
|
||
.catch(() => setPlanInfo(null))
|
||
.finally(() => setLoading(false));
|
||
}, []);
|
||
|
||
const currentPlan = planInfo?.plan ?? null;
|
||
const trialDaysLeft = planInfo?.trialEndsAt ? daysUntil(planInfo.trialEndsAt) : 0;
|
||
const hasPaidPlan = currentPlan === 'business_control' || currentPlan === 'business_cloud' || currentPlan === 'mi_empresa' || currentPlan === 'mi_empresa_plus';
|
||
// Plan Custom: asignado por administrador, sin cobro, sin fecha fin.
|
||
// Cuando es activo, ocultamos las cards de planes pagables (no hay opción
|
||
// de auto-cambio — el contador debe contactar soporte si quiere cambiar).
|
||
const isCustomPlan = currentPlan === 'custom';
|
||
|
||
/** Resuelve la frecuencia para un plan. Mi Empresa y Mi Empresa+ leen su
|
||
* propio toggle; el resto (business_*) siempre annual. */
|
||
function frequencyFor(plan: PaidPlan): Frequency {
|
||
if (plan === 'mi_empresa') return meFreq;
|
||
if (plan === 'mi_empresa_plus') return mePlusFreq;
|
||
return 'annual';
|
||
}
|
||
|
||
async function handleContratar(plan: PaidPlan) {
|
||
const frequency = frequencyFor(plan);
|
||
setBusy(plan);
|
||
setMessage(null);
|
||
try {
|
||
const result = await subscribeMe({ plan, frequency });
|
||
window.open(result.paymentUrl, '_blank');
|
||
setMessage({ kind: 'ok', text: 'Abrimos el pago de MercadoPago en otra pestana. Al completar regresa aqui.' });
|
||
} catch (err: any) {
|
||
const msg: string = err?.response?.data?.message || err?.message || '';
|
||
if (/Ya existe una suscripci/i.test(msg)) {
|
||
// Ya hay sub activa/pendiente en otro plan — tratar como cambio
|
||
try {
|
||
await changeMyPlan({ plan, frequency });
|
||
setMessage({ kind: 'ok', text: 'Cambio de plan programado para el final del periodo actual.' });
|
||
fetchPlan();
|
||
} catch (changeErr: any) {
|
||
setMessage({ kind: 'err', text: changeErr?.response?.data?.message || changeErr?.message || 'Error al cambiar el plan' });
|
||
}
|
||
} else {
|
||
setMessage({ kind: 'err', text: msg || 'Error al contratar el plan' });
|
||
}
|
||
} finally {
|
||
setBusy(null);
|
||
}
|
||
}
|
||
|
||
async function handleCancelar() {
|
||
if (!confirm('Seguro que quieres cancelar la suscripcion? Conservaras acceso hasta el final del periodo pagado.')) return;
|
||
setBusy('cancel');
|
||
setMessage(null);
|
||
try {
|
||
await cancelMySubscription();
|
||
setMessage({ kind: 'ok', text: 'Suscripcion cancelada. Acceso activo hasta el final del periodo actual.' });
|
||
fetchPlan();
|
||
} catch (err: any) {
|
||
setMessage({ kind: 'err', text: err?.response?.data?.message || err?.message || 'Error al cancelar' });
|
||
} finally {
|
||
setBusy(null);
|
||
}
|
||
}
|
||
|
||
function ActiveBadge() {
|
||
return (
|
||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-green-600 text-white text-xs px-3 py-1 rounded-full font-medium whitespace-nowrap">
|
||
Plan actual
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Toggle binario Mensual/Anual. La opción anual va resaltada con un
|
||
* pequeño badge "−17%" para enfocar el descuento.
|
||
*/
|
||
function FrequencyToggle({ value, onChange }: { value: Frequency; onChange: (v: Frequency) => void }) {
|
||
return (
|
||
<div className="flex bg-muted rounded-lg p-1 text-xs font-medium">
|
||
<button
|
||
type="button"
|
||
onClick={() => onChange('monthly')}
|
||
className={`flex-1 py-1.5 rounded-md transition-colors ${value === 'monthly' ? 'bg-background shadow-sm text-foreground' : 'text-muted-foreground hover:text-foreground'}`}
|
||
>
|
||
Mensual
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => onChange('annual')}
|
||
className={`flex-1 py-1.5 rounded-md transition-colors flex items-center justify-center gap-1 ${value === 'annual' ? 'bg-background shadow-sm text-foreground' : 'text-muted-foreground hover:text-foreground'}`}
|
||
>
|
||
Anual <span className="text-emerald-600 dark:text-emerald-400 text-[10px] font-bold">−17%</span>
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function PlanActionButton({ plan }: { plan: PaidPlan }) {
|
||
const isCurrent = currentPlan === plan;
|
||
if (isCurrent) {
|
||
return <Button disabled className="w-full">Plan actual</Button>;
|
||
}
|
||
const label = hasPaidPlan ? 'Cambiar a este plan' : 'Contratar';
|
||
return (
|
||
<Button
|
||
className="w-full"
|
||
onClick={() => handleContratar(plan)}
|
||
disabled={busy === plan}
|
||
>
|
||
{busy === plan ? 'Procesando...' : (
|
||
<>
|
||
<ExternalLink className="h-4 w-4 mr-2" />
|
||
{label}
|
||
</>
|
||
)}
|
||
</Button>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="p-6 max-w-5xl mx-auto space-y-8">
|
||
<div className="text-center space-y-2">
|
||
<h1 className="text-2xl font-bold">Planes Horux Despachos</h1>
|
||
<p className="text-muted-foreground">Tres planes: Mi Empresa para usuarios individuales, Business Control y Enterprise para despachos.</p>
|
||
</div>
|
||
|
||
{/* Banner Custom — plan asignado por admin, sin cobro */}
|
||
{!loading && isCustomPlan && (
|
||
<div className="flex items-start gap-3 bg-pink-50 dark:bg-pink-950 border border-pink-200 dark:border-pink-800 rounded-lg px-4 py-3 max-w-3xl mx-auto">
|
||
<CheckCircle2 className="h-5 w-5 text-pink-600 dark:text-pink-400 flex-shrink-0 mt-0.5" />
|
||
<div className="text-sm space-y-0.5">
|
||
<div className="font-semibold text-pink-800 dark:text-pink-300">
|
||
Plan Custom — sin cobro, vigencia indefinida
|
||
</div>
|
||
<div className="text-pink-700 dark:text-pink-400">
|
||
Tu cuenta está bajo un plan especial asignado por tu administrador.
|
||
Contacta a soporte si necesitas cambiar de plan.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Trial banner */}
|
||
{!loading && planInfo?.isTrialActive && (
|
||
<div className="flex items-center gap-3 bg-amber-50 dark:bg-amber-950 border border-amber-200 dark:border-amber-800 rounded-lg px-4 py-3 max-w-3xl mx-auto">
|
||
<Clock className="h-5 w-5 text-amber-600 dark:text-amber-400 flex-shrink-0" />
|
||
<div className="text-sm">
|
||
<span className="font-semibold text-amber-800 dark:text-amber-300">Periodo de prueba activo</span>
|
||
<span className="text-amber-700 dark:text-amber-400"> — {trialDaysLeft} {trialDaysLeft === 1 ? 'dia restante' : 'dias restantes'}</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Banner de suscripción activa */}
|
||
{!loading && planInfo?.subscription && hasPaidPlan && (() => {
|
||
const sub = planInfo.subscription;
|
||
const periodEndDate = sub.currentPeriodEnd ? new Date(sub.currentPeriodEnd) : null;
|
||
const fechaFormato = periodEndDate
|
||
? periodEndDate.toLocaleDateString('es-MX', { year: 'numeric', month: 'long', day: 'numeric' })
|
||
: null;
|
||
const montoFmt = sub.amount.toLocaleString('es-MX');
|
||
return (
|
||
<div className="flex items-start gap-3 bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800 rounded-lg px-4 py-3 max-w-3xl mx-auto">
|
||
<CheckCircle2 className="h-5 w-5 text-green-600 dark:text-green-400 flex-shrink-0 mt-0.5" />
|
||
<div className="text-sm space-y-0.5">
|
||
<div className="font-semibold text-green-800 dark:text-green-300">
|
||
Suscripcion activa — {
|
||
sub.plan === 'business_control' ? 'Business Control'
|
||
: sub.plan === 'business_cloud' ? 'Enterprise'
|
||
: sub.plan === 'mi_empresa_plus' ? 'Mi Empresa +'
|
||
: 'Mi Empresa'
|
||
}
|
||
</div>
|
||
<div className="text-green-700 dark:text-green-400">
|
||
Proxima renovacion{fechaFormato ? ` el ${fechaFormato}` : ''}: <strong>${montoFmt}/año</strong>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})()}
|
||
|
||
{/* Toast de resultado */}
|
||
{message && (
|
||
<div
|
||
className={`max-w-3xl mx-auto rounded-lg px-4 py-3 text-sm ${
|
||
message.kind === 'ok'
|
||
? 'bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800 text-green-800 dark:text-green-300'
|
||
: 'bg-red-50 dark:bg-red-950 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-300'
|
||
}`}
|
||
>
|
||
{message.text}
|
||
</div>
|
||
)}
|
||
|
||
{!isCustomPlan && (
|
||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 max-w-7xl mx-auto">
|
||
{/* Mi Empresa */}
|
||
<Card className={`relative flex flex-col${currentPlan === 'mi_empresa' ? ' ring-2 ring-green-500' : ''}`}>
|
||
{currentPlan === 'mi_empresa' && <ActiveBadge />}
|
||
<CardHeader className="text-center pb-2">
|
||
<div className="mx-auto bg-emerald-100 dark:bg-emerald-900 rounded-full p-3 w-fit mb-2">
|
||
<Cloud className="h-6 w-6 text-emerald-600 dark:text-emerald-400" />
|
||
</div>
|
||
<CardTitle className="text-xl">Mi Empresa</CardTitle>
|
||
<p className="text-sm text-muted-foreground">Para una sola empresa</p>
|
||
</CardHeader>
|
||
<CardContent className="flex flex-col flex-1 gap-4">
|
||
<FrequencyToggle value={meFreq} onChange={setMeFreq} />
|
||
<div className="text-center">
|
||
<div className="text-3xl font-bold">${meFreq === 'monthly' ? '580' : '5,800'}</div>
|
||
<p className="text-sm text-muted-foreground">{meFreq === 'monthly' ? 'por mes (IVA incluido)' : 'por año (IVA incluido)'}</p>
|
||
{meFreq === 'monthly' ? (
|
||
<p className="text-xs text-muted-foreground mt-1">o $5,800/año (ahorras 17%)</p>
|
||
) : (
|
||
<p className="text-xs text-emerald-600 dark:text-emerald-400 mt-1 font-medium">Pagas 10 meses en lugar de 12</p>
|
||
)}
|
||
</div>
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>1 RFC</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>3 usuarios</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 1,000,000 CFDIs</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Base de datos en la nube</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Dashboard, CFDI, IVA/ISR, alertas, calendario</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Reportes, conciliación, documentos, facturación</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>50 timbres/mes incluidos</span></div>
|
||
</div>
|
||
<div className="mt-auto"><PlanActionButton plan="mi_empresa" /></div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Mi Empresa + */}
|
||
<Card className={`relative flex flex-col${currentPlan === 'mi_empresa_plus' ? ' ring-2 ring-green-500' : ''}`}>
|
||
{currentPlan === 'mi_empresa_plus' && <ActiveBadge />}
|
||
<CardHeader className="text-center pb-2">
|
||
<div className="mx-auto bg-teal-100 dark:bg-teal-900 rounded-full p-3 w-fit mb-2">
|
||
<Cloud className="h-6 w-6 text-teal-600 dark:text-teal-400" />
|
||
</div>
|
||
<CardTitle className="text-xl">Mi Empresa +</CardTitle>
|
||
<p className="text-sm text-muted-foreground">Mi Empresa con API y Lolita IA</p>
|
||
</CardHeader>
|
||
<CardContent className="flex flex-col flex-1 gap-4">
|
||
<FrequencyToggle value={mePlusFreq} onChange={setMePlusFreq} />
|
||
<div className="text-center">
|
||
<div className="text-3xl font-bold">${mePlusFreq === 'monthly' ? '900' : '9,000'}</div>
|
||
<p className="text-sm text-muted-foreground">{mePlusFreq === 'monthly' ? 'por mes (IVA incluido)' : 'por año (IVA incluido)'}</p>
|
||
{mePlusFreq === 'monthly' ? (
|
||
<p className="text-xs text-muted-foreground mt-1">o $9,000/año (ahorras 17%)</p>
|
||
) : (
|
||
<p className="text-xs text-emerald-600 dark:text-emerald-400 mt-1 font-medium">Pagas 10 meses en lugar de 12</p>
|
||
)}
|
||
</div>
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>1 RFC</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>3 usuarios</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 1,000,000 CFDIs</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Base de datos en la nube</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Dashboard, CFDI, IVA/ISR, alertas, calendario</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Reportes, conciliación, documentos, facturación</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>50 timbres/mes incluidos</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span><strong>API REST</strong> incluida</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span><strong>Lolita IA</strong> agente fiscal</span></div>
|
||
</div>
|
||
<div className="mt-auto"><PlanActionButton plan="mi_empresa_plus" /></div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Business Control */}
|
||
<Card className={`relative flex flex-col${currentPlan === 'business_control' ? ' ring-2 ring-green-500' : ' border-primary ring-2 ring-primary/20'}`}>
|
||
{currentPlan === 'business_control'
|
||
? <ActiveBadge />
|
||
: (
|
||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-primary text-primary-foreground text-xs px-3 py-1 rounded-full">
|
||
Más popular
|
||
</div>
|
||
)
|
||
}
|
||
<CardHeader className="text-center pb-2">
|
||
<div className="mx-auto bg-blue-100 dark:bg-blue-900 rounded-full p-3 w-fit mb-2">
|
||
<Server className="h-6 w-6 text-blue-600 dark:text-blue-400" />
|
||
</div>
|
||
<CardTitle className="text-xl">Business Control</CardTitle>
|
||
<p className="text-sm text-muted-foreground">Tu servidor, tus datos</p>
|
||
</CardHeader>
|
||
<CardContent className="flex flex-col flex-1 gap-4">
|
||
<div className="text-center">
|
||
<div className="text-3xl font-bold">$25,850</div>
|
||
<p className="text-sm text-muted-foreground">por año (IVA incluido)</p>
|
||
<p className="text-xs text-muted-foreground mt-1">+ $45/mes por cada RFC adicional sobre 100</p>
|
||
</div>
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 100 RFCs</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Usuarios ilimitados</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 1,000,000 CFDIs por contribuyente</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Servidor local con backup</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Control total de tus datos</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Dashboard, CFDI, IVA/ISR, alertas, calendario</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Reportes, conciliación, documentos, facturación, API</span></div>
|
||
</div>
|
||
<div className="mt-auto"><PlanActionButton plan="business_control" /></div>
|
||
</CardContent>
|
||
</Card>
|
||
|
||
{/* Enterprise (key interna: business_cloud) */}
|
||
<Card className={`relative flex flex-col${currentPlan === 'business_cloud' ? ' ring-2 ring-green-500' : ''}`}>
|
||
{currentPlan === 'business_cloud' && <ActiveBadge />}
|
||
<CardHeader className="text-center pb-2">
|
||
<div className="mx-auto bg-purple-100 dark:bg-purple-900 rounded-full p-3 w-fit mb-2">
|
||
<Cloud className="h-6 w-6 text-purple-600 dark:text-purple-400" />
|
||
</div>
|
||
<CardTitle className="text-xl">Enterprise</CardTitle>
|
||
<p className="text-sm text-muted-foreground">Despachos grandes con alto volumen</p>
|
||
</CardHeader>
|
||
<CardContent className="flex flex-col flex-1 gap-4">
|
||
<div className="text-center">
|
||
<div className="text-3xl font-bold">$43,000</div>
|
||
<p className="text-sm text-muted-foreground">por año (IVA incluido)</p>
|
||
<p className="text-xs text-muted-foreground mt-1">+ $45/mes por cada RFC adicional sobre 100</p>
|
||
</div>
|
||
<div className="space-y-2 text-sm">
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 100 RFCs</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Usuarios ilimitados</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Hasta 3,000,000 CFDIs por contribuyente</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Servidor local con backup</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Backups automáticos en la nube</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Dashboard, CFDI, IVA/ISR, alertas, calendario</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Reportes, conciliación, documentos, facturación, API</span></div>
|
||
<div className="flex items-center gap-2"><CheckCircle2 className="h-4 w-4 text-green-500 flex-shrink-0" /><span>Soporte prioritario</span></div>
|
||
</div>
|
||
<div className="mt-auto"><PlanActionButton plan="business_cloud" /></div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
)}
|
||
|
||
{/* Cancelar — visible solo si tiene plan pagable activo */}
|
||
{hasPaidPlan && (
|
||
<div className="text-center pt-4">
|
||
<button
|
||
type="button"
|
||
onClick={handleCancelar}
|
||
disabled={busy === 'cancel'}
|
||
className="text-sm text-muted-foreground hover:text-destructive underline underline-offset-4 disabled:opacity-50"
|
||
>
|
||
{busy === 'cancel' ? 'Cancelando...' : 'Cancelar suscripcion'}
|
||
</button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|