'use client'; import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Button, Input, Label, Card, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@horux/shared-ui'; import { useContribuyentes, useCreateContribuyente, useUpdateContribuyente, useDeactivateContribuyente } from '@/lib/hooks/use-contribuyentes'; import type { CreateContribuyenteData } from '@/lib/api/contribuyentes'; import { useAuthStore } from '@/stores/auth-store'; import { apiClient } from '@/lib/api/client'; import { Plus, Pencil, Trash2, Building2, Sparkles } from 'lucide-react'; import { AddonsDialog } from './addons-dialog'; import { DESPACHO_PLANS } from '@horux/shared'; const TRIAL_LIMIT_TOOLTIP = 'Límite de contribuyentes para la prueba gratuita, para continuar agregando contribuyentes, selecciona un plan.'; export default function ContribuyentesPage() { const { user } = useAuthStore(); const { data: contribuyentes, isLoading } = useContribuyentes(); const createMut = useCreateContribuyente(); const updateMut = useUpdateContribuyente(); const deactivateMut = useDeactivateContribuyente(); const [showDialog, setShowDialog] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ rfc: '', razonSocial: '' }); const [assignSelf, setAssignSelf] = useState(true); const [addonsTarget, setAddonsTarget] = useState<{ id: string; nombre: string } | null>(null); // Trial gate: durante el periodo de prueba el despacho no puede agregar más // de 5 contribuyentes activos. El backend valida también; aquí solo se // deshabilita el botón con tooltip explicativo. const { data: planInfo } = useQuery({ queryKey: ['my-plan-info'], queryFn: () => apiClient.get<{ plan: string; isTrialActive: boolean }>('/despachos/me/plan').then(r => r.data), }); const activeCount = (contribuyentes ?? []).filter((c: any) => c.active !== false).length; const trialAtLimit = (planInfo?.isTrialActive ?? false) && activeCount >= 5; // Contador de RFCs disponibles en el plan const planKey = planInfo?.plan as keyof typeof DESPACHO_PLANS | undefined; const planMaxRfcs = planKey ? DESPACHO_PLANS[planKey]?.maxRfcs ?? undefined : undefined; const rfcCounterText = (() => { if (planInfo?.isTrialActive) return `${activeCount} de 5 RFCs`; if (planMaxRfcs != null && planMaxRfcs < 0) return `${activeCount} RFCs`; if (planMaxRfcs !== undefined) return `${activeCount} de ${planMaxRfcs} RFCs`; return `${activeCount} RFCs`; })(); const resetForm = () => { setForm({ rfc: '', razonSocial: '' }); setAssignSelf(true); setShowDialog(false); setEditingId(null); }; const handleSave = async () => { try { if (editingId) { await updateMut.mutateAsync({ id: editingId, data: form }); } else { const created = await createMut.mutateAsync({ ...form, supervisorUserId: assignSelf ? user?.id : undefined, }); // Overage Business Cloud: si el 4º+ RFC disparó un nuevo addon, abre // MercadoPago para autorizar el cobro recurrente mensual de $45/RFC. if (created.overage?.action === 'created' && created.overage.paymentUrl) { alert(`Se agregó un cobro mensual de $45 por contribuyente adicional (${created.overage.overageCount} extra${created.overage.overageCount === 1 ? '' : 's'}). Autoriza el pago en MercadoPago.`); window.open(created.overage.paymentUrl, '_blank'); } else if (created.overage?.action === 'updated') { alert(`Se actualizó el cobro mensual de contribuyentes adicionales a ${created.overage.overageCount} extra${created.overage.overageCount === 1 ? '' : 's'} ($${created.overage.overageCount * 45}/mes).`); } } resetForm(); } catch (err: any) { alert(err.response?.data?.message || 'Error'); } }; const handleDeactivate = async (id: string, rfc: string) => { if (!confirm(`¿Desactivar contribuyente ${rfc}?`)) return; try { const result = await deactivateMut.mutateAsync(id); if (result.overage?.action === 'cancelled') { alert('Se canceló el cobro mensual de contribuyente adicional (volviste a los 3 incluidos).'); } else if (result.overage?.action === 'updated') { alert(`El cobro de contribuyentes adicionales se actualizó a ${result.overage.overageCount} extra${result.overage.overageCount === 1 ? '' : 's'} ($${result.overage.overageCount * 45}/mes).`); } } catch (err: any) { alert(err.response?.data?.message || 'Error'); } }; const openEdit = (c: any) => { setForm({ rfc: c.rfc, razonSocial: c.nombre }); setEditingId(c.id); setShowDialog(true); }; const canCreate = user?.role === 'owner' || user?.role === 'cfo' || user?.role === 'supervisor'; return (

Contribuyentes

RFCs que gestiona tu despacho · {rfcCounterText}

{canCreate && ( )}
{isLoading ?

Cargando...

: !contribuyentes || contribuyentes.length === 0 ? (

Sin contribuyentes

Agrega el primer RFC para empezar.

{canCreate && ( )}
) : (
{contribuyentes.map((c) => (

{c.nombre}

{c.rfc}

{c.regimenFiscal &&

Régimen: {c.regimenFiscal}

} {c.supervisorNombre &&

Supervisor: {c.supervisorNombre}

}
{(user?.role === 'owner' || user?.role === 'cfo') && ( )} {user?.role === 'owner' && ( )}
))}
)} setAddonsTarget(null)} /> resetForm()}> {editingId ? 'Editar contribuyente' : 'Agregar contribuyente'}
setForm((p) => ({ ...p, rfc: e.target.value }))} placeholder="ABC010203XY1" maxLength={13} disabled={!!editingId} />
setForm((p) => ({ ...p, razonSocial: e.target.value }))} placeholder="Empresa SA de CV" />
{!editingId && (
setAssignSelf(e.target.checked)} className="h-4 w-4" />
)}
); }