feat(dashboard): agregar scorecards de notas de crédito emitidas y recibidas

- Extiende KpiData con ncsEmitidas, ncsEmitidasPorRegimen, ncsRecibidas y ncsRecibidasPorRegimen.
- En getKpis se reutilizan calcularNcsEmitidasPorRegimen y calcularNcsRecibidasPorRegimen en paralelo.
- En el dashboard se agregan dos KpiCard y su desglose por régimen.
This commit is contained in:
Horux Dev
2026-06-13 20:46:57 +00:00
parent 66d68c652c
commit b52ff875be
3 changed files with 90 additions and 6 deletions

View File

@@ -19,6 +19,8 @@ import {
AlertTriangle,
ShoppingCart,
CheckSquare,
FileMinus,
FilePlus,
} from 'lucide-react';
import { cn } from '@horux/shared-ui';
import { FiscalDisclaimer } from '@/components/fiscal-disclaimer';
@@ -118,6 +120,15 @@ export default function DashboardPage() {
? kpis?.ivaBalancePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: kpis?.ivaBalance || 0;
// Notas de crédito
const ncsEmitidasDisplay = regimenSeleccionado
? kpis?.ncsEmitidasPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: kpis?.ncsEmitidas || 0;
const ncsRecibidasDisplay = regimenSeleccionado
? kpis?.ncsRecibidasPorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: kpis?.ncsRecibidas || 0;
const ivaAnterior = regimenSeleccionado
? kpisAnterior?.ivaBalancePorRegimen?.find(r => r.regimenClave === regimenSeleccionado)?.monto || 0
: kpisAnterior?.ivaBalance || 0;
@@ -203,7 +214,7 @@ export default function DashboardPage() {
</div>
{/* KPIs */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
<KpiCard
title={regimenSeleccionado ? `Ingresos del Mes (${regimenSeleccionado})` : 'Ingresos del Mes'}
value={ingresosDisplay}
@@ -248,11 +259,25 @@ export default function DashboardPage() {
}
href={drillUrl('Balance IVA - CFDIs', {})}
/>
<KpiCard
title={regimenSeleccionado ? `NCs Emitidas (${regimenSeleccionado})` : 'NCs Emitidas'}
value={ncsEmitidasDisplay}
icon={<FileMinus className="h-4 w-4" />}
trend="neutral"
trendValue="Notas de crédito emitidas"
/>
<KpiCard
title={regimenSeleccionado ? `NCs Recibidas (${regimenSeleccionado})` : 'NCs Recibidas'}
value={ncsRecibidasDisplay}
icon={<FilePlus className="h-4 w-4" />}
trend="neutral"
trendValue="Notas de crédito recibidas"
/>
</div>
{/* Desglose por régimen */}
{!regimenSeleccionado && kpis && (
(kpis.ingresosPorRegimen.length > 1 || kpis.egresosPorRegimen.length > 1 || kpis.ivaBalancePorRegimen.length > 1) && (
(kpis.ingresosPorRegimen.length > 1 || kpis.egresosPorRegimen.length > 1 || kpis.ivaBalancePorRegimen.length > 1 || kpis.ncsEmitidasPorRegimen.length > 1 || kpis.ncsRecibidasPorRegimen.length > 1) && (
<div className="grid gap-4 md:grid-cols-2 3xl:grid-cols-3">
{kpis.ingresosPorRegimen.length > 1 && (
<Card>
@@ -316,6 +341,46 @@ export default function DashboardPage() {
</CardContent>
</Card>
)}
{kpis.ncsEmitidasPorRegimen.length > 1 && (
<Card>
<CardHeader>
<CardTitle className="text-base font-medium">NCs Emitidas por Regimen</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
{kpis.ncsEmitidasPorRegimen.map((r) => (
<div key={r.regimenClave} className="flex items-center justify-between py-2 border-b last:border-0">
<div className="flex items-center gap-3">
<span className="text-xs font-mono font-bold bg-muted px-2 py-1 rounded">{r.regimenClave}</span>
<span className="text-sm">{r.regimenDescripcion}</span>
</div>
<span className="text-sm font-semibold">{formatCurrency(r.monto)}</span>
</div>
))}
</div>
</CardContent>
</Card>
)}
{kpis.ncsRecibidasPorRegimen.length > 1 && (
<Card>
<CardHeader>
<CardTitle className="text-base font-medium">NCs Recibidas por Regimen</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
{kpis.ncsRecibidasPorRegimen.map((r) => (
<div key={r.regimenClave} className="flex items-center justify-between py-2 border-b last:border-0">
<div className="flex items-center gap-3">
<span className="text-xs font-mono font-bold bg-muted px-2 py-1 rounded">{r.regimenClave}</span>
<span className="text-sm">{r.regimenDescripcion}</span>
</div>
<span className="text-sm font-semibold">{formatCurrency(r.monto)}</span>
</div>
))}
</div>
</CardContent>
</Card>
)}
</div>
))}