diff --git a/frontend/src/components/BillingCycleSelector.tsx b/frontend/src/components/BillingCycleSelector.tsx index 9a5444d..4ef010e 100644 --- a/frontend/src/components/BillingCycleSelector.tsx +++ b/frontend/src/components/BillingCycleSelector.tsx @@ -49,7 +49,7 @@ export default function BillingCycleSelector({ value, onChange }: Props) { - All time + Todo el período {cycles.map((c) => ( {c.label} ({c.count}) diff --git a/frontend/src/components/budget/YearlyOverview.tsx b/frontend/src/components/budget/YearlyOverview.tsx index 5957dc2..9c354d5 100644 --- a/frontend/src/components/budget/YearlyOverview.tsx +++ b/frontend/src/components/budget/YearlyOverview.tsx @@ -1,4 +1,5 @@ import ConfirmDialog from '@/components/ConfirmDialog'; +import { MONTH_NAMES_ES_SHORT as MONTH_NAMES } from '@/lib/dates'; import { useState, useRef, useEffect } from 'react'; import { Pencil } from 'lucide-react'; @@ -15,10 +16,6 @@ import { TableRow, } from '@/components/ui/table'; -const MONTH_NAMES = [ - '', 'Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', - 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic', -]; const FRESH_START_YEAR = 2026; const FRESH_START_MONTH = 3; diff --git a/frontend/src/lib/dates.ts b/frontend/src/lib/dates.ts new file mode 100644 index 0000000..75f99bd --- /dev/null +++ b/frontend/src/lib/dates.ts @@ -0,0 +1,28 @@ +/** Single source of truth for date display — the app speaks es-CR (UX-19). */ + +export const MONTH_NAMES_ES = [ + '', 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', + 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre', +]; + +export const MONTH_NAMES_ES_SHORT = [ + '', 'Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', + 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic', +]; + +/** "2 abr" */ +export function formatShortDate(dateStr: string): string { + return new Date(dateStr).toLocaleDateString('es-CR', { + month: 'short', + day: 'numeric', + }); +} + +/** "hace 3 días" / "hace 2 horas" / "hace un momento" */ +export function formatRelativeAge(iso: string): string { + const ms = Date.now() - new Date(iso).getTime(); + const hours = ms / 3_600_000; + if (hours < 1) return 'hace un momento'; + if (hours < 48) return `hace ${Math.round(hours)} h`; + return `hace ${Math.round(hours / 24)} días`; +} diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts index 3976610..e968fe9 100644 --- a/frontend/src/lib/format.ts +++ b/frontend/src/lib/format.ts @@ -1,3 +1,5 @@ +import { formatShortDate } from './dates'; + export function formatAmount(amount: number, currency: string) { const abs = Math.abs(amount); if (currency === 'BTC') return abs.toFixed(8); @@ -17,5 +19,5 @@ export function formatLocalDatetime(d: Date): string { } export function formatDate(dateStr: string) { - return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + return formatShortDate(dateStr); } diff --git a/frontend/src/pages/Analytics.tsx b/frontend/src/pages/Analytics.tsx index 65bcb3f..ebf9246 100644 --- a/frontend/src/pages/Analytics.tsx +++ b/frontend/src/pages/Analytics.tsx @@ -65,7 +65,7 @@ const trendChartConfig = { const dailyChartConfig = { total: { - label: 'Daily Spending', + label: 'Gasto Diario', color: 'var(--chart-2)', }, } satisfies ChartConfig; @@ -127,7 +127,7 @@ export default function Analytics() {

Analytics

-

Spending breakdown and trends

+

Desglose y tendencias de gasto

@@ -150,7 +150,7 @@ export default function Analytics() { {byCategory.length === 0 ? (
- No data for this period + Sin datos para este período
) : (
@@ -248,7 +248,7 @@ export default function Analytics() { {daily.length === 0 ? (
- No data for this period + Sin datos para este período
) : ( @@ -272,7 +272,7 @@ export default function Analytics() { formatCRC(Number(value))} labelFormatter={(label) => - new Date(label).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + new Date(label).toLocaleDateString('es-CR', { month: 'short', day: 'numeric' }) } /> } diff --git a/frontend/src/pages/Budget.tsx b/frontend/src/pages/Budget.tsx index 86af5c7..b644a6a 100644 --- a/frontend/src/pages/Budget.tsx +++ b/frontend/src/pages/Budget.tsx @@ -4,6 +4,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import api, { type Transaction } from '@/lib/api'; +import { MONTH_NAMES_ES as MONTH_NAMES } from '@/lib/dates'; import { useBudget } from '@/hooks/useBudget'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; @@ -12,10 +13,6 @@ import MonthlyDetail from '@/components/budget/MonthlyDetail'; import RecurringItemsManager from '@/components/budget/RecurringItemsManager'; import TransactionList from '@/components/TransactionList'; -const MONTH_NAMES = [ - '', 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', - 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre', -]; const MIN_YEAR = 2026; const MAX_YEAR = 2030; diff --git a/frontend/src/pages/ServiciosMunicipales.tsx b/frontend/src/pages/ServiciosMunicipales.tsx index 6e58deb..6ec4cd3 100644 --- a/frontend/src/pages/ServiciosMunicipales.tsx +++ b/frontend/src/pages/ServiciosMunicipales.tsx @@ -1,5 +1,6 @@ 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 ErrorState from '@/components/ErrorState'; import { BarChart, @@ -53,10 +54,6 @@ const METER_COLORS: Record = { '9345': '#f59e0b', }; -const MONTH_NAMES_ES = [ - 'Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', - 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic', -]; const DEFAULT_METER_COLOR = '#8b5cf6'; @@ -67,7 +64,8 @@ const formatCRC = (amount: number): string => function periodLabel(period: string): string { const [yearStr, monthStr] = period.split('-'); - const monthIdx = parseInt(monthStr, 10) - 1; + // The shared month array is 1-indexed ('' at index 0). + const monthIdx = parseInt(monthStr, 10); return `${MONTH_NAMES_ES[monthIdx]} ${yearStr.slice(2)}`; }