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 type { CotizacionBolsa } from '../models/mercadoModels';
import { useApiData } from '../hooks/useApiData'; import { useApiData } from '../hooks/useApiData';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { formatFullDateTime } from '../utils/formatters'; import { formatFullDateTime, formatCurrency2Decimal } from '../utils/formatters';
interface HistoricalChartWidgetProps { interface HistoricalChartWidgetProps {
ticker: string; ticker: string;
@@ -10,6 +10,11 @@ interface HistoricalChartWidgetProps {
dias: number; 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) => { const formatTooltipLabel = (label: string) => {
return formatFullDateTime(label); return formatFullDateTime(label);
}; };
@@ -23,16 +28,25 @@ export const HistoricalChartWidget = ({ ticker, mercado, dias }: HistoricalChart
} }
if (error) { 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) { 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 // 1. Calcular el dominio del eje Y con un margen
const formatXAxis = (tickItem: string) => { const prices = data.map(p => p.precioActual);
return new Date(tickItem).toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit' }); 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 ( return (
@@ -40,13 +54,17 @@ export const HistoricalChartWidget = ({ ticker, mercado, dias }: HistoricalChart
<LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}> <LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" /> <CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="fechaRegistro" tickFormatter={formatXAxis} /> <XAxis dataKey="fechaRegistro" tickFormatter={formatXAxis} />
<YAxis domain={['dataMin - dataMin * 0.02', 'dataMax + dataMax * 0.02']} tickFormatter={(tick) => `$${tick.toLocaleString('es-AR')}`} /> <YAxis
<Tooltip domain={[domainMin, domainMax]}
formatter={(value: number) => [`$${value.toFixed(2)}`, 'Precio']} tickFormatter={yAxisTickFormatter}
labelFormatter={formatTooltipLabel} width={80} // Damos un poco más de espacio para números grandes
/>
<Tooltip
formatter={(value: number) => [`$${formatCurrency2Decimal(value)}`, 'Precio']}
labelFormatter={formatTooltipLabel}
/> />
<Legend /> <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> </LineChart>
</ResponsiveContainer> </ResponsiveContainer>
); );

View File

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

View File

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