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}
+ );
+}