Feat: Currency 2 Decimal Format - Fix: Charts Scale

This commit is contained in:
2025-07-14 11:18:46 -03:00
parent 2cd57d0e60
commit c9b3127f55
3 changed files with 44 additions and 14 deletions

View File

@@ -2,7 +2,7 @@ import { Box, CircularProgress, Alert } from '@mui/material';
import type { CotizacionBolsa } from '../models/mercadoModels';
import { useApiData } from '../hooks/useApiData';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { formatFullDateTime } from '../utils/formatters';
import { formatFullDateTime, formatCurrency2Decimal } from '../utils/formatters';
interface HistoricalChartWidgetProps {
ticker: string;
@@ -10,6 +10,11 @@ interface HistoricalChartWidgetProps {
dias: number;
}
// Formateador para el eje X que solo muestra día/mes
const formatXAxis = (tickItem: string) => {
return new Date(tickItem).toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit' });
};
const formatTooltipLabel = (label: string) => {
return formatFullDateTime(label);
};
@@ -23,16 +28,25 @@ export const HistoricalChartWidget = ({ ticker, mercado, dias }: HistoricalChart
}
if (error) {
return <Alert severity="error" sx={{height: 300}}>{error}</Alert>;
return <Alert severity="error" sx={{ height: 300 }}>{error}</Alert>;
}
if (!data || data.length < 2) {
return <Alert severity="info" sx={{height: 300}}>No hay suficientes datos históricos para graficar.</Alert>;
return <Alert severity="info" sx={{ height: 300 }}>No hay suficientes datos históricos para graficar.</Alert>;
}
// Formateador para el eje X que solo muestra día/mes
const formatXAxis = (tickItem: string) => {
return new Date(tickItem).toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit' });
// 1. Calcular el dominio del eje Y con un margen
const prices = data.map(p => p.precioActual);
const dataMin = Math.min(...prices);
const dataMax = Math.max(...prices);
const padding = (dataMax - dataMin) * 0.05; // 5% de padding
const domainMin = Math.floor(dataMin - padding);
const domainMax = Math.ceil(dataMax + padding);
// 2. Formateador de ticks para el eje Y más robusto
const yAxisTickFormatter = (tick: number) => {
// Usamos el formateador de moneda
return `$${formatCurrency2Decimal(tick)}`;
};
return (
@@ -40,13 +54,17 @@ export const HistoricalChartWidget = ({ ticker, mercado, dias }: HistoricalChart
<LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="fechaRegistro" tickFormatter={formatXAxis} />
<YAxis domain={['dataMin - dataMin * 0.02', 'dataMax + dataMax * 0.02']} tickFormatter={(tick) => `$${tick.toLocaleString('es-AR')}`} />
<Tooltip
formatter={(value: number) => [`$${value.toFixed(2)}`, 'Precio']}
labelFormatter={formatTooltipLabel}
<YAxis
domain={[domainMin, domainMax]}
tickFormatter={yAxisTickFormatter}
width={80} // Damos un poco más de espacio para números grandes
/>
<Tooltip
formatter={(value: number) => [`$${formatCurrency2Decimal(value)}`, 'Precio']}
labelFormatter={formatTooltipLabel}
/>
<Legend />
<Line type="monotone" dataKey="precioActual" name="Precio de Cierre" stroke="#028fbe" strokeWidth={2} dot={false} />
<Line type="monotone" dataKey="precioActual" name="Precio de Cierre" stroke="#8884d8" strokeWidth={2} dot={false} />
</LineChart>
</ResponsiveContainer>
);

View File

@@ -5,7 +5,7 @@ import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import RemoveIcon from '@mui/icons-material/Remove';
import type { CotizacionBolsa } from '../models/mercadoModels';
import { formatInteger, formatCurrency } from '../utils/formatters';
import { formatInteger, formatCurrency, formatCurrency2Decimal } from '../utils/formatters';
import { HistoricalChartWidget } from './HistoricalChartWidget';
import { useApiData } from '../hooks/useApiData';
@@ -53,7 +53,7 @@ export const MervalHeroCard = () => {
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
<Box>
<Typography variant="h5" component="h2" sx={{ fontWeight: 'bold' }}>Índice S&P MERVAL</Typography>
<Typography variant="h3" component="p" sx={{ fontWeight: 'bold', mt:1 }}>{formatInteger(mervalData.precioActual)}</Typography>
<Typography variant="h3" component="p" sx={{ fontWeight: 'bold', mt:1 }}>{formatCurrency2Decimal(mervalData.precioActual)}</Typography>
</Box>
<Box sx={{ pt: 2 }}>
<VariacionMerval actual={mervalData.precioActual} anterior={mervalData.cierreAnterior} />

View File

@@ -11,6 +11,18 @@ export const formatCurrency = (num: number, currency = 'ARS') => {
}).format(num);
};
export const formatCurrency2Decimal = (num: number, currency = 'ARS') => {
const style = currency === 'USD' ? 'currency' : 'decimal';
const locale = currency === 'USD' ? 'en-US' : 'es-AR';
return new Intl.NumberFormat(locale, {
style: style,
currency: currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(num);
};
export const formatInteger = (num: number) => {
return new Intl.NumberFormat('es-AR').format(num);
};
@@ -23,7 +35,7 @@ export const formatFullDateTime = (dateString: string) => {
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hourCycle: 'h23', // <--- LA CLAVE PARA EL FORMATO 24HS
hourCycle: 'h23',
timeZone: 'America/Argentina/Buenos_Aires',
});
};