| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  | import { useState } from 'react'; | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |   Box, CircularProgress, Alert, Table, TableBody, TableCell, TableContainer, | 
					
						
							|  |  |  |   TableHead, TableRow, Paper, Typography, Dialog, DialogTitle, | 
					
						
							|  |  |  |   DialogContent, IconButton | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | } from '@mui/material'; | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  | import CloseIcon from '@mui/icons-material/Close'; | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; | 
					
						
							|  |  |  | import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; | 
					
						
							|  |  |  | import RemoveIcon from '@mui/icons-material/Remove'; | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  | import { formatFullDateTime } from '../utils/formatters'; | 
					
						
							|  |  |  | import type { CotizacionBolsa } from '../models/mercadoModels'; | 
					
						
							|  |  |  | import { useApiData } from '../hooks/useApiData'; | 
					
						
							|  |  |  | import { HistoricalChartWidget } from './HistoricalChartWidget'; | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  | // Usamos el formato de EEUU para los precios en dólares
 | 
					
						
							|  |  |  | const formatCurrency = (num: number) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(num); | 
					
						
							|  |  |  | const formatPercentage = (num: number) => num.toFixed(2); | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | const Variacion = ({ value }: { value: number }) => { | 
					
						
							|  |  |  |   const color = value > 0 ? 'success.main' : value < 0 ? 'error.main' : 'text.secondary'; | 
					
						
							|  |  |  |   const Icon = value > 0 ? ArrowUpwardIcon : value < 0 ? ArrowDownwardIcon : RemoveIcon; | 
					
						
							|  |  |  |   return ( | 
					
						
							|  |  |  |     <Box component="span" sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color }}> | 
					
						
							|  |  |  |       <Icon sx={{ fontSize: '1rem', mr: 0.5 }} /> | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |       <Typography variant="body2" component="span" sx={{ fontWeight: 'bold' }}>{formatPercentage(value)}%</Typography> | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  |     </Box> | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const BolsaUsaWidget = () => { | 
					
						
							|  |  |  |   const { data, loading, error } = useApiData<CotizacionBolsa[]>('/mercados/bolsa/eeuu'); | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |   const [selectedTicker, setSelectedTicker] = useState<string | null>(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleRowClick = (ticker: string) => { | 
					
						
							|  |  |  |     setSelectedTicker(ticker); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const handleCloseDialog = () => { | 
					
						
							|  |  |  |     setSelectedTicker(null); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (loading) { | 
					
						
							|  |  |  |     return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (error) { | 
					
						
							|  |  |  |     return <Alert severity="error">{error}</Alert>; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |   // Recordatorio de que el fetcher puede estar desactivado
 | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  |   if (!data || data.length === 0) { | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |     return <Alert severity="info">No hay datos disponibles para el mercado de EEUU. (El fetcher podría estar desactivado en el Worker).</Alert>; | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return ( | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |     <> | 
					
						
							|  |  |  |       <TableContainer component={Paper}> | 
					
						
							|  |  |  |         <Box sx={{ p: 1, pb: 0 }}> | 
					
						
							|  |  |  |           <Typography variant="caption" sx={{ fontStyle: 'italic', color: 'text.secondary' }}> | 
					
						
							|  |  |  |             Última actualización: {formatFullDateTime(data[0].fechaRegistro)} | 
					
						
							|  |  |  |           </Typography> | 
					
						
							|  |  |  |         </Box> | 
					
						
							|  |  |  |         <Table size="small" aria-label="tabla bolsa eeuu"> | 
					
						
							|  |  |  |           <TableHead> | 
					
						
							|  |  |  |             <TableRow> | 
					
						
							|  |  |  |               <TableCell>Símbolo</TableCell> | 
					
						
							|  |  |  |               <TableCell align="right">Precio Actual</TableCell> | 
					
						
							|  |  |  |               <TableCell align="right">Apertura</TableCell> | 
					
						
							|  |  |  |               <TableCell align="right">Cierre Anterior</TableCell> | 
					
						
							|  |  |  |               <TableCell align="center">% Cambio</TableCell> | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  |             </TableRow> | 
					
						
							| 
									
										
										
										
											2025-07-01 16:05:26 -03:00
										 |  |  |           </TableHead> | 
					
						
							|  |  |  |           <TableBody> | 
					
						
							|  |  |  |             {data.map((row) => ( | 
					
						
							|  |  |  |               <TableRow key={row.ticker} hover sx={{ cursor: 'pointer' }} onClick={() => handleRowClick(row.ticker)}> | 
					
						
							|  |  |  |                 <TableCell component="th" scope="row"><Typography variant="body2" sx={{ fontWeight: 'bold' }}>{row.ticker}</Typography></TableCell> | 
					
						
							|  |  |  |                 <TableCell align="right">{formatCurrency(row.precioActual)}</TableCell> | 
					
						
							|  |  |  |                 <TableCell align="right">{formatCurrency(row.apertura)}</TableCell> | 
					
						
							|  |  |  |                 <TableCell align="right">{formatCurrency(row.cierreAnterior)}</TableCell> | 
					
						
							|  |  |  |                 <TableCell align="center"><Variacion value={row.porcentajeCambio} /></TableCell> | 
					
						
							|  |  |  |               </TableRow> | 
					
						
							|  |  |  |             ))} | 
					
						
							|  |  |  |           </TableBody> | 
					
						
							|  |  |  |         </Table> | 
					
						
							|  |  |  |       </TableContainer> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       <Dialog open={Boolean(selectedTicker)} onClose={handleCloseDialog} maxWidth="md" fullWidth> | 
					
						
							|  |  |  |         <DialogTitle sx={{ m: 0, p: 2 }}> | 
					
						
							|  |  |  |           Historial de 30 días para: {selectedTicker} | 
					
						
							|  |  |  |           <IconButton | 
					
						
							|  |  |  |             aria-label="close" | 
					
						
							|  |  |  |             onClick={handleCloseDialog} | 
					
						
							|  |  |  |             sx={{ position: 'absolute', right: 8, top: 8, color: (theme) => theme.palette.grey[500] }} | 
					
						
							|  |  |  |           > | 
					
						
							|  |  |  |             <CloseIcon /> | 
					
						
							|  |  |  |           </IconButton> | 
					
						
							|  |  |  |         </DialogTitle> | 
					
						
							|  |  |  |         <DialogContent dividers> | 
					
						
							|  |  |  |           {selectedTicker && <HistoricalChartWidget ticker={selectedTicker} mercado="EEUU" />} | 
					
						
							|  |  |  |         </DialogContent> | 
					
						
							|  |  |  |       </Dialog> | 
					
						
							|  |  |  |     </> | 
					
						
							| 
									
										
										
										
											2025-07-01 13:42:16 -03:00
										 |  |  |   ); | 
					
						
							|  |  |  | }; |