- Compras Tasa Cero pagadas en cuotas mensuales sin intereses. Las cuotas
- futuras ya cuentan en el presupuesto de sus meses.
-
diff --git a/frontend/src/pages/Pensions.tsx b/frontend/src/pages/Pensions.tsx
index 0627d75..45e86e8 100644
--- a/frontend/src/pages/Pensions.tsx
+++ b/frontend/src/pages/Pensions.tsx
@@ -1,6 +1,7 @@
import { useState, useMemo, useCallback, useRef } from 'react';
import { useQuery } from '@tanstack/react-query';
import ErrorState from '@/components/ErrorState';
+import { PageHeader } from '@/components/page-header';
import PensionAssumptionsDialog, { type PensionAssumptions } from '@/components/PensionAssumptionsDialog';
import {
LineChart,
@@ -103,7 +104,7 @@ const FUNDS_DEFAULT: Record
= {
key: 'FCL',
name: 'FCL',
fullName: 'Fondo de Capitalización Laboral',
- color: '#3b82f6',
+ color: 'var(--chart-5)',
startBalance: 650_468,
monthlyContribution: 150_000,
annualRate: 7.5,
@@ -115,7 +116,7 @@ const FUNDS_DEFAULT: Record = {
key: 'ROP',
name: 'ROP',
fullName: 'Régimen Obligatorio de Pensiones',
- color: '#10b981',
+ color: 'var(--chart-1)',
startBalance: 18_684_765,
monthlyContribution: 120_000,
annualRate: 6.0,
@@ -127,7 +128,7 @@ const FUNDS_DEFAULT: Record = {
key: 'VOL',
name: 'VOL',
fullName: 'Fondo Voluntario',
- color: '#f43f5e',
+ color: 'var(--chart-4)',
startBalance: 2_500_381,
monthlyContribution: 400_000,
annualRate: 8.0,
@@ -480,17 +481,11 @@ export default function Pensions() {
/>
)}
{/* ── Page Header ─────────────────────────────────────────────────── */}
-
-
-
-
Pensiones
-
- Seguimiento de aportes, rendimientos y proyecciones
-
-
-
+
{/* ── Section 1: Fund Overview Cards ──────────────────────────────── */}
@@ -606,7 +601,7 @@ export default function Pensions() {
className="flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium transition-all border cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
style={{
borderColor: fund.color,
- background: active ? fund.color + '22' : 'transparent',
+ background: active ? `color-mix(in oklab, ${fund.color} 13%, transparent)` : 'transparent',
color: active ? fund.color : 'var(--muted-foreground)',
}}
>
@@ -652,6 +647,7 @@ export default function Pensions() {
stroke={FUNDS[key].color}
strokeWidth={2}
dot={false}
+ isAnimationActive={false}
activeDot={{ r: 4 }}
/>
) : null,
diff --git a/frontend/src/pages/Planificador.tsx b/frontend/src/pages/Planificador.tsx
index 3ba1b3e..f524dcc 100644
--- a/frontend/src/pages/Planificador.tsx
+++ b/frontend/src/pages/Planificador.tsx
@@ -4,6 +4,8 @@ import { Line, LineChart, XAxis, YAxis } from 'recharts';
import { Telescope } from 'lucide-react';
import { getYearlyProjection } from '@/lib/api';
+import { PageHeader } from '@/components/page-header';
+import { StatTile } from '@/components/stat-tile';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
@@ -78,15 +80,11 @@ export default function Planificador() {
return (
-
-
-
-
Planificador
-
- ¿Qué pasa si ahorrás un poco más cada mes?
-
-
-
+
@@ -138,41 +136,18 @@ export default function Planificador() {
-
-
-
- Ritmo actual
-
-
- {formatCRC(baselineEnd)}
-
-
-
-
-
-
- Escenario
-
-
- {formatCRC(scenarioEnd)}
-
-
-
-
-
-
- Diferencia
-
-
- +{formatCRC(scenarioEnd - baselineEnd)}
-
-
-
+
+
+
-
+
Crecimiento comparado
@@ -191,8 +166,8 @@ export default function Planificador() {
formatCRC(Number(value))} />
}
/>
-
-
+
+
diff --git a/frontend/src/pages/Proyecciones.tsx b/frontend/src/pages/Proyecciones.tsx
index 0f2b692..c749393 100644
--- a/frontend/src/pages/Proyecciones.tsx
+++ b/frontend/src/pages/Proyecciones.tsx
@@ -5,6 +5,7 @@ import { useBudget } from '@/hooks/useBudget';
import { formatAmount } from '@/lib/format';
import { cn } from '@/lib/utils';
import { Card, CardContent } from '@/components/ui/card';
+import { PageHeader } from '@/components/page-header';
import ErrorState from '@/components/ErrorState';
import PeriodNavigator from '@/components/PeriodNavigator';
import YearlyOverview from '@/components/budget/YearlyOverview';
@@ -30,20 +31,20 @@ export default function Proyecciones() {
return (
- {/* Header */}
-
-
-
-
Proyecciones
-
-
setYear(year - 1)}
- onNext={() => setYear(year + 1)}
- prevDisabled={year <= MIN_YEAR}
- nextDisabled={year >= MAX_YEAR}
- />
-
+
setYear(year - 1)}
+ onNext={() => setYear(year + 1)}
+ prevDisabled={year <= MIN_YEAR}
+ nextDisabled={year >= MAX_YEAR}
+ />
+ }
+ />
{/* Annual summary cards */}
{projection && (
diff --git a/frontend/src/pages/ServiciosMunicipales.tsx b/frontend/src/pages/ServiciosMunicipales.tsx
index 2a21720..8db80ce 100644
--- a/frontend/src/pages/ServiciosMunicipales.tsx
+++ b/frontend/src/pages/ServiciosMunicipales.tsx
@@ -2,6 +2,7 @@ import { useState, useCallback, useRef, useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { MONTH_NAMES_ES_SHORT as MONTH_NAMES_ES } from '@/lib/dates';
import MeterLabelsDialog from '@/components/MeterLabelsDialog';
+import { PageHeader } from '@/components/page-header';
import ErrorState from '@/components/ErrorState';
import {
BarChart,
@@ -51,13 +52,13 @@ import {
// ─── Constants ───────────────────────────────────────────────────────────────
const METER_COLORS: Record = {
- '7335': '#3b82f6',
- '7345': '#10b981',
- '9345': '#f59e0b',
+ '7335': 'var(--chart-5)',
+ '7345': 'var(--chart-1)',
+ '9345': 'var(--chart-2)',
};
-const DEFAULT_METER_COLOR = '#8b5cf6';
+const DEFAULT_METER_COLOR = 'var(--chart-3)';
// ─── Utilities ───────────────────────────────────────────────────────────────
@@ -105,8 +106,11 @@ function getMeterIds(readings: WaterMeterReading[]): string[] {
// ─── Charge Trend Data ───────────────────────────────────────────────────────
+// Fixed categorical slots (validated palette); series beyond the 5th read
+// muted — never cycle hues.
const CHARGE_COLORS = [
- '#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16',
+ 'var(--chart-1)', 'var(--chart-2)', 'var(--chart-3)', 'var(--chart-4)',
+ 'var(--chart-5)', 'var(--muted-foreground)',
];
interface ChargeTrendPoint {
@@ -306,17 +310,11 @@ export default function ServiciosMunicipales() {
/>
)}
{/* ── Page Header ─────────────────────────────────────────────────── */}
-
-
-
-
-
-
Servicios Municipales
-
- Municipalidad de Belén — recibos y consumo de agua
-
-
-
+
{/* ── Summary Cards ───────────────────────────────────────────────── */}
@@ -546,7 +544,7 @@ export default function ServiciosMunicipales() {
type="monotone"
dataKey={name}
name={name}
- stroke={CHARGE_COLORS[i % CHARGE_COLORS.length]}
+ stroke={CHARGE_COLORS[Math.min(i, CHARGE_COLORS.length - 1)]}
strokeWidth={2}
dot={{ r: 3 }}
activeDot={{ r: 5 }}
diff --git a/frontend/src/pages/SyncStatus.tsx b/frontend/src/pages/SyncStatus.tsx
index 6f5e031..287a02b 100644
--- a/frontend/src/pages/SyncStatus.tsx
+++ b/frontend/src/pages/SyncStatus.tsx
@@ -11,6 +11,7 @@ import { formatRelativeAge } from '@/lib/dates';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
+import { PageHeader } from '@/components/page-header';
import ErrorState from '@/components/ErrorState';
import { Skeleton } from '@/components/ui/skeleton';
@@ -38,29 +39,25 @@ export default function SyncStatus() {
return (
-
-
-
-
-
Sincronización
-
- Estado de los flujos automáticos de datos (n8n y tipo de cambio)
-
-
-
-
-
+
query.refetch()}
+ disabled={query.isFetching}
+ >
+
+ Actualizar
+
+ }
+ />
{query.isError ? (