From c4eead1033f4e69ae4a91349c7ff13ab93ecae62 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Tue, 1 Jul 2025 13:42:16 -0300 Subject: [PATCH] feat: Implement remaining data widgets for Agro, Grains, and USA Stocks --- frontend/src/App.tsx | 15 ++-- frontend/src/components/BolsaLocalWidget.tsx | 20 ++--- frontend/src/components/BolsaUsaWidget.tsx | 82 +++++++++++++++++++ frontend/src/components/GranosWidget.tsx | 80 ++++++++++++++++++ frontend/src/components/MercadoAgroWidget.tsx | 69 ++++++++++++++++ 5 files changed, 243 insertions(+), 23 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 980ddbd..3e9e629 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,8 +1,10 @@ import { Box, Container, Typography, AppBar, Toolbar, CssBaseline } from '@mui/material'; import { BolsaLocalWidget } from './components/BolsaLocalWidget'; +import { MercadoAgroWidget } from './components/MercadoAgroWidget'; +import { GranosWidget } from './components/GranosWidget'; +import { BolsaUsaWidget } from './components/BolsaUsaWidget'; function App() { - return ( <> @@ -22,22 +24,19 @@ function App() { {/* --- Sección 1: Mercado Agroganadero de Cañuelas --- */} Mercado Agroganadero de Cañuelas - {/* Aquí irá el */} - Widget en construcción... + {/* --- Sección 2: Granos - Bolsa de Comercio de Rosario --- */} Granos - Bolsa de Comercio de Rosario - {/* Aquí irá el */} - Widget en construcción... + {/* --- Sección 3: Mercado de Valores de Estados Unidos --- */} Mercado de Valores de Estados Unidos - {/* Aquí irá el */} - Widget en construcción... + {/* --- Sección 4: Mercado de Valores Local --- */} @@ -49,7 +48,7 @@ function App() { - Desarrollado con Arquitectura Moderna - {new Date().getFullYear()} + Desarrollado por El Día - {new Date().getFullYear()} diff --git a/frontend/src/components/BolsaLocalWidget.tsx b/frontend/src/components/BolsaLocalWidget.tsx index 89426c8..10f95d9 100644 --- a/frontend/src/components/BolsaLocalWidget.tsx +++ b/frontend/src/components/BolsaLocalWidget.tsx @@ -1,16 +1,6 @@ import { - Box, - CircularProgress, - Alert, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Paper, - Typography, - Tooltip + Box, CircularProgress, Alert, Table, TableBody, TableCell, + TableContainer, TableHead, TableRow, Paper, Typography, Tooltip } from '@mui/material'; import type { CotizacionBolsa } from '../models/mercadoModels'; import { useApiData } from '../hooks/useApiData'; @@ -30,7 +20,7 @@ const formatNumber = (num: number) => { 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 ( @@ -84,9 +74,9 @@ export const BolsaLocalWidget = () => { ))} - + - Fuente: Yahoo Finance + Fuente: Yahoo Finance diff --git a/frontend/src/components/BolsaUsaWidget.tsx b/frontend/src/components/BolsaUsaWidget.tsx index e69de29..a854a1f 100644 --- a/frontend/src/components/BolsaUsaWidget.tsx +++ b/frontend/src/components/BolsaUsaWidget.tsx @@ -0,0 +1,82 @@ +import { + Box, CircularProgress, Alert, Table, TableBody, TableCell, + TableContainer, TableHead, TableRow, Paper, Typography, Tooltip +} from '@mui/material'; +import type { CotizacionBolsa } from '../models/mercadoModels'; +import { useApiData } from '../hooks/useApiData'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import RemoveIcon from '@mui/icons-material/Remove'; + +const formatNumber = (num: number) => { + return new Intl.NumberFormat('en-US', { // Usamos formato de EEUU + style: 'currency', + currency: 'USD', + }).format(num); +}; + +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 ( + + + + {value.toFixed(2)}% + + + ); +}; + +export const BolsaUsaWidget = () => { + const { data, loading, error } = useApiData('/mercados/bolsa/eeuu'); + + if (loading) { + return ; + } + + if (error) { + return {error}; + } + + if (!data || data.length === 0) { + return No hay datos disponibles para el mercado de EEUU. (El fetcher puede estar desactivado); + } + + return ( + + + + + Símbolo + Precio Actual + Apertura + Cierre Anterior + % Cambio + + + + {data.map((row) => ( + + + {row.ticker} + + {formatNumber(row.precioActual)} + {formatNumber(row.apertura)} + {formatNumber(row.cierreAnterior)} + + + + + ))} + +
+ + + Fuente: Finnhub + + +
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/GranosWidget.tsx b/frontend/src/components/GranosWidget.tsx index e69de29..0260ffa 100644 --- a/frontend/src/components/GranosWidget.tsx +++ b/frontend/src/components/GranosWidget.tsx @@ -0,0 +1,80 @@ +import { + Box, CircularProgress, Alert, Table, TableBody, TableCell, + TableContainer, TableHead, TableRow, Paper, Typography, Tooltip +} from '@mui/material'; +import type { CotizacionGrano } from '../models/mercadoModels'; +import { useApiData } from '../hooks/useApiData'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import RemoveIcon from '@mui/icons-material/Remove'; + +const formatNumber = (num: number) => { + return new Intl.NumberFormat('es-AR', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(num); +}; + +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 ( + + + + {formatNumber(value)} + + + ); +}; + +export const GranosWidget = () => { + const { data, loading, error } = useApiData('/mercados/granos'); + + if (loading) { + return ; + } + + if (error) { + return {error}; + } + + if (!data || data.length === 0) { + return No hay datos de granos disponibles en este momento.; + } + + return ( + + + + + Grano + Precio ($/Tn) + Variación + Fecha Operación + + + + {data.map((row) => ( + + + {row.nombre} + + ${formatNumber(row.precio)} + + + + {new Date(row.fechaOperacion).toLocaleDateString('es-AR')} + + ))} + +
+ + + Fuente: Bolsa de Comercio de Rosario + + +
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/MercadoAgroWidget.tsx b/frontend/src/components/MercadoAgroWidget.tsx index e69de29..34d886b 100644 --- a/frontend/src/components/MercadoAgroWidget.tsx +++ b/frontend/src/components/MercadoAgroWidget.tsx @@ -0,0 +1,69 @@ +import { + Box, CircularProgress, Alert, Table, TableBody, TableCell, + TableContainer, TableHead, TableRow, Paper, Typography, Tooltip +} from '@mui/material'; +import type { CotizacionGanado } from '../models/mercadoModels'; +import { useApiData } from '../hooks/useApiData'; + +const formatNumber = (num: number, fractionDigits = 2) => { + return new Intl.NumberFormat('es-AR', { + minimumFractionDigits: fractionDigits, + maximumFractionDigits: fractionDigits, + }).format(num); +}; + +export const MercadoAgroWidget = () => { + const { data, loading, error } = useApiData('/mercados/agroganadero'); + + if (loading) { + return ; + } + + if (error) { + return {error}; + } + + if (!data || data.length === 0) { + return No hay datos del mercado agroganadero disponibles.; + } + + return ( + + + + + Categoría + Especificaciones + Máximo + Mínimo + Mediano + Cabezas + Kilos Totales + Importe Total + + + + {data.map((row) => ( + + + {row.categoria} + + {row.especificaciones} + ${formatNumber(row.maximo)} + ${formatNumber(row.minimo)} + ${formatNumber(row.mediano)} + {formatNumber(row.cabezas, 0)} + {formatNumber(row.kilosTotales, 0)} Kg + ${formatNumber(row.importeTotal)} + + ))} + +
+ + + Fuente: Mercado Agroganadero S.A. + + +
+ ); +}; \ No newline at end of file