diff --git a/apps/web/app/(dashboard)/dashboard/page.tsx b/apps/web/app/(dashboard)/dashboard/page.tsx new file mode 100644 index 0000000..ba3a7bf --- /dev/null +++ b/apps/web/app/(dashboard)/dashboard/page.tsx @@ -0,0 +1,147 @@ +'use client'; + +import { Header } from '@/components/layouts/header'; +import { KpiCard } from '@/components/charts/kpi-card'; +import { BarChart } from '@/components/charts/bar-chart'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { useKpis, useIngresosEgresos, useAlertas, useResumenFiscal } from '@/lib/hooks/use-dashboard'; +import { + TrendingUp, + TrendingDown, + Wallet, + Receipt, + FileText, + AlertTriangle, +} from 'lucide-react'; + +export default function DashboardPage() { + const currentYear = new Date().getFullYear(); + const currentMonth = new Date().getMonth() + 1; + + const { data: kpis, isLoading: kpisLoading } = useKpis(currentYear, currentMonth); + const { data: chartData, isLoading: chartLoading } = useIngresosEgresos(currentYear); + const { data: alertas, isLoading: alertasLoading } = useAlertas(5); + const { data: resumenFiscal } = useResumenFiscal(currentYear, currentMonth); + + const formatCurrency = (value: number) => + new Intl.NumberFormat('es-MX', { + style: 'currency', + currency: 'MXN', + minimumFractionDigits: 0, + }).format(value); + + return ( + <> +
+
+ {/* KPIs */} +
+ } + trend="up" + trendValue="+12.5%" + subtitle="vs mes anterior" + /> + } + trend="down" + trendValue="-3.2%" + subtitle="vs mes anterior" + /> + } + trend={kpis?.utilidad && kpis.utilidad > 0 ? 'up' : 'down'} + trendValue={`${kpis?.margen || 0}% margen`} + /> + } + trend={kpis?.ivaBalance && kpis.ivaBalance > 0 ? 'up' : 'down'} + trendValue={kpis?.ivaBalance && kpis.ivaBalance > 0 ? 'Por pagar' : 'A favor'} + /> +
+ + {/* Charts and Alerts */} +
+
+ +
+ + + + + + Alertas + + + + {alertasLoading ? ( +

Cargando...

+ ) : alertas?.length === 0 ? ( +

No hay alertas pendientes

+ ) : ( + alertas?.map((alerta) => ( +
+

{alerta.titulo}

+

+ {alerta.mensaje} +

+
+ )) + )} +
+
+
+ + {/* Resumen Fiscal */} +
+ + +

CFDIs Emitidos

+

{kpis?.cfdisEmitidos || 0}

+
+
+ + +

CFDIs Recibidos

+

{kpis?.cfdisRecibidos || 0}

+
+
+ + +

IVA a Favor Acumulado

+

+ {formatCurrency(resumenFiscal?.ivaAFavor || 0)} +

+
+
+ + +

Declaraciones Pendientes

+

+ {resumenFiscal?.declaracionesPendientes || 0} +

+
+
+
+
+ + ); +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 7d86181..eb44fbb 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import { Inter } from 'next/font/google'; import './globals.css'; import { ThemeProvider } from '@/components/providers/theme-provider'; +import { QueryProvider } from '@/components/providers/query-provider'; const inter = Inter({ subsets: ['latin'] }); @@ -18,7 +19,9 @@ export default function RootLayout({ return ( - {children} + + {children} + ); diff --git a/apps/web/components/providers/query-provider.tsx b/apps/web/components/providers/query-provider.tsx new file mode 100644 index 0000000..591cd8a --- /dev/null +++ b/apps/web/components/providers/query-provider.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useState } from 'react'; + +export function QueryProvider({ children }: { children: React.ReactNode }) { + const [queryClient] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, + refetchOnWindowFocus: false, + }, + }, + }) + ); + + return ( + {children} + ); +}