diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 27418c8..c34ba2e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,6 +9,7 @@ import Budget from './pages/Budget'; import Analytics from './pages/Analytics'; import Salarios from './pages/Salarios'; import Pensions from './pages/Pensions'; +import Proyecciones from './pages/Proyecciones'; import ServiciosMunicipales from './pages/ServiciosMunicipales'; function ProtectedRoute({ children }: { children: React.ReactNode }) { @@ -35,6 +36,7 @@ function AppRoutes() { } /> } /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index b66db3f..c06cf28 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -7,6 +7,7 @@ import { PiggyBank, Droplets, LogOut, + TrendingUp, Wallet, Menu, Sun, @@ -51,6 +52,7 @@ const navSections: NavSection[] = [ { to: '/budget', icon: Calculator, label: 'Presupuesto' }, { to: '/salarios', icon: Landmark, label: 'Salarios' }, { to: '/pensions', icon: PiggyBank, label: 'Pensiones' }, + { to: '/proyecciones', icon: TrendingUp, label: 'Proyecciones' }, { to: '/analytics', icon: BarChart3, label: 'Analytics' }, ], }, diff --git a/frontend/src/pages/Proyecciones.tsx b/frontend/src/pages/Proyecciones.tsx new file mode 100644 index 0000000..cc5d504 --- /dev/null +++ b/frontend/src/pages/Proyecciones.tsx @@ -0,0 +1,120 @@ +import { ChevronLeft, ChevronRight, Loader2, TrendingUp } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; + +import { useBudget } from '@/hooks/useBudget'; +import { formatAmount } from '@/lib/format'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import YearlyOverview from '@/components/budget/YearlyOverview'; + +const MIN_YEAR = 2026; +const MAX_YEAR = 2030; + +export default function Proyecciones() { + const currentYear = Math.max(MIN_YEAR, new Date().getFullYear()); + const { + year, + setYear, + setSelectedMonth, + projection, + loading, + saveBalanceOverride, + clearBalanceOverride, + } = useBudget(currentYear); + + const navigate = useNavigate(); + + return ( +
+ {/* Header */} +
+
+ +

Proyecciones

+
+
+ + {year} + +
+
+ + {/* Annual summary cards */} + {projection && ( +
+ + +

Ingresos Anuales

+

+ {formatAmount(projection.annual_income, 'CRC')} +

+
+
+ + +

Egresos Anuales

+

+ {formatAmount(projection.annual_expenses, 'CRC')} +

+
+
+ + +

Ahorro Anual

+

+ {formatAmount(projection.annual_savings, 'CRC')} +

+
+
+ + +

Balance Neto Anual

+

= 0 ? 'text-primary' : 'text-destructive', + )} + > + {projection.annual_net >= 0 ? '+' : ''} + {formatAmount(projection.annual_net, 'CRC')} +

+
+
+
+ )} + + {/* Yearly table */} + {loading ? ( +
+ +
+ ) : projection ? ( + + + { + setSelectedMonth(m); + navigate('/budget'); + }} + onSaveOverride={async (month, value) => { + await saveBalanceOverride(year, month, value); + }} + onClearOverride={async (month) => { + await clearBalanceOverride(year, month); + }} + /> + + + ) : null} +
+ ); +}