mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:08:46 +02:00
Localization sweep: shared es-CR dates module, no more en-US strays
src/lib/dates.ts is the single source for month names and date formatting; the three per-page month arrays are gone (with an off-by-one guard — the shared array is 1-indexed). formatDate and the Analytics chart tooltips now speak es-CR, Analytics headings are Spanish, and the cycle selector says 'Todo el período' (UX-19, FE-17, UX-07). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ export default function BillingCycleSelector({ value, onChange }: Props) {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All time</SelectItem>
|
||||
<SelectItem value="all">Todo el período</SelectItem>
|
||||
{cycles.map((c) => (
|
||||
<SelectItem key={`${c.year}-${c.month}`} value={`${c.year}-${c.month}`}>
|
||||
{c.label} ({c.count})
|
||||
|
||||
@@ -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;
|
||||
|
||||
28
frontend/src/lib/dates.ts
Normal file
28
frontend/src/lib/dates.ts
Normal file
@@ -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`;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
<BarChart3 className="w-5 h-5 text-primary" />
|
||||
<h1 className="text-2xl font-bold font-heading">Analytics</h1>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mt-1">Spending breakdown and trends</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">Desglose y tendencias de gasto</p>
|
||||
</div>
|
||||
<BillingCycleSelector value={cycle} onChange={setCycle} />
|
||||
</div>
|
||||
@@ -150,7 +150,7 @@ export default function Analytics() {
|
||||
<CardContent>
|
||||
{byCategory.length === 0 ? (
|
||||
<div className="h-64 flex items-center justify-center text-muted-foreground text-sm">
|
||||
No data for this period
|
||||
Sin datos para este período
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center">
|
||||
@@ -248,7 +248,7 @@ export default function Analytics() {
|
||||
<CardContent>
|
||||
{daily.length === 0 ? (
|
||||
<div className="h-48 flex items-center justify-center text-muted-foreground text-sm">
|
||||
No data for this period
|
||||
Sin datos para este período
|
||||
</div>
|
||||
) : (
|
||||
<ChartContainer data-sensitive config={dailyChartConfig} className="h-[240px] w-full">
|
||||
@@ -272,7 +272,7 @@ export default function Analytics() {
|
||||
<ChartTooltipContent
|
||||
formatter={(value) => 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' })
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<string, string> = {
|
||||
'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)}`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user