Merge pull request #37 from josesalazar2025/jose
Browse files
frontend/dist/assets/{index-QBJ3liVo.css β index-B5AdzxgP.css}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/dist/assets/{index-BfINZMV7.js β index-Dk2_c6vF.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>PolySignal β Dashboard de Inteligencia de Mercados</title>
|
| 7 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 8 |
-
<script type="module" crossorigin src="/assets/index-
|
| 9 |
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<!-- Global loader: visible por defecto (sin JS) hasta que bootstrap lo oculte -->
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>PolySignal β Dashboard de Inteligencia de Mercados</title>
|
| 7 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
| 8 |
+
<script type="module" crossorigin src="/assets/index-Dk2_c6vF.js"></script>
|
| 9 |
+
<link rel="stylesheet" crossorigin href="/assets/index-B5AdzxgP.css">
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<!-- Global loader: visible por defecto (sin JS) hasta que bootstrap lo oculte -->
|
frontend/src/app.js
CHANGED
|
@@ -770,6 +770,22 @@ function hasAlert(marketId) {
|
|
| 770 |
return state.watchlist.some((w) => w.marketId === marketId && w.alertThreshold != null)
|
| 771 |
}
|
| 772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 773 |
async function toggleWatchlistCard(marketId, wBtn, aBtn) {
|
| 774 |
if (isInWatchlist(marketId)) {
|
| 775 |
try {
|
|
@@ -781,6 +797,8 @@ async function toggleWatchlistCard(marketId, wBtn, aBtn) {
|
|
| 781 |
aBtn.textContent = 'β‘ Alertas'
|
| 782 |
aBtn.classList.remove('active')
|
| 783 |
aBtn.title = 'Activar alerta de precio'
|
|
|
|
|
|
|
| 784 |
} catch (e) { console.warn('Error al quitar de watchlist:', e) }
|
| 785 |
} else {
|
| 786 |
try {
|
|
@@ -789,6 +807,8 @@ async function toggleWatchlistCard(marketId, wBtn, aBtn) {
|
|
| 789 |
wBtn.textContent = 'β
Seguimiento'
|
| 790 |
wBtn.classList.add('active')
|
| 791 |
wBtn.title = 'Quitar de seguimiento'
|
|
|
|
|
|
|
| 792 |
} catch (e) { console.warn('Error al aΓ±adir a watchlist:', e) }
|
| 793 |
}
|
| 794 |
}
|
|
@@ -804,6 +824,8 @@ function toggleAlertCard(marketId, aBtn, thresholdRow) {
|
|
| 804 |
aBtn.textContent = 'β‘ Alertas'
|
| 805 |
aBtn.classList.remove('active')
|
| 806 |
aBtn.title = 'Activar alerta de precio'
|
|
|
|
|
|
|
| 807 |
})
|
| 808 |
.catch((e) => console.warn('Error al desactivar alerta:', e))
|
| 809 |
} else {
|
|
@@ -823,6 +845,8 @@ async function setAlertThreshold(marketId, threshold, aBtn, thresholdRow) {
|
|
| 823 |
aBtn.classList.add('active')
|
| 824 |
aBtn.title = 'Desactivar alerta'
|
| 825 |
thresholdRow.classList.add('hidden')
|
|
|
|
|
|
|
| 826 |
} catch (e) { console.warn('Error al configurar alerta:', e) }
|
| 827 |
}
|
| 828 |
|
|
@@ -1342,9 +1366,7 @@ function renderPositions() {
|
|
| 1342 |
|
| 1343 |
async function closePositionById(id) {
|
| 1344 |
await simulator.closePosition(id)
|
| 1345 |
-
|
| 1346 |
-
renderPositions()
|
| 1347 |
-
renderMiniPositions()
|
| 1348 |
}
|
| 1349 |
|
| 1350 |
/* βββ Render watchlist view βββ */
|
|
@@ -1403,6 +1425,7 @@ async function removeFromWatchlistById(marketId) {
|
|
| 1403 |
try { await api.removeFromWatchlist(marketId) } catch (e) { console.warn(e) }
|
| 1404 |
state.watchlist = state.watchlist.filter((w) => w.marketId !== marketId)
|
| 1405 |
renderWatchlist()
|
|
|
|
| 1406 |
}
|
| 1407 |
|
| 1408 |
/* βββ Render alerts view βββ */
|
|
@@ -1509,6 +1532,12 @@ async function loadPositions() {
|
|
| 1509 |
}
|
| 1510 |
}
|
| 1511 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1512 |
async function loadWatchlist() {
|
| 1513 |
try {
|
| 1514 |
state.watchlist = await api.getWatchlist()
|
|
@@ -1563,6 +1592,11 @@ async function initAppData() {
|
|
| 1563 |
map.init('map-container', state.markets, state.signals, selectMarket)
|
| 1564 |
simulator.init(state)
|
| 1565 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1566 |
state.activeMarketId = state.markets[0]?.id || null
|
| 1567 |
renderSignals()
|
| 1568 |
renderDetail()
|
|
@@ -1669,6 +1703,6 @@ export async function init() {
|
|
| 1669 |
socket.on('price_alert', (data) => {
|
| 1670 |
if (!data?.marketId || !data.type) return
|
| 1671 |
state.alerts.unshift(data)
|
| 1672 |
-
|
| 1673 |
})
|
| 1674 |
}
|
|
|
|
| 770 |
return state.watchlist.some((w) => w.marketId === marketId && w.alertThreshold != null)
|
| 771 |
}
|
| 772 |
|
| 773 |
+
function updateSignalCardWatchlistState(marketId) {
|
| 774 |
+
document.querySelectorAll(`#signals-list .market-card[data-market="${CSS.escape(marketId)}"]`).forEach((card) => {
|
| 775 |
+
const wBtn = card.querySelector('.card-btn-watch')
|
| 776 |
+
const aBtn = card.querySelector('.card-btn-alert')
|
| 777 |
+
if (!wBtn || !aBtn) return
|
| 778 |
+
const inWl = isInWatchlist(marketId)
|
| 779 |
+
const hasAl = hasAlert(marketId)
|
| 780 |
+
wBtn.textContent = inWl ? 'β
Seguimiento' : 'β Seguimiento'
|
| 781 |
+
wBtn.classList.toggle('active', inWl)
|
| 782 |
+
wBtn.title = inWl ? 'Quitar de seguimiento' : 'AΓ±adir a seguimiento'
|
| 783 |
+
aBtn.textContent = hasAl ? 'β‘ Alerta activa' : 'β‘ Alertas'
|
| 784 |
+
aBtn.classList.toggle('active', hasAl)
|
| 785 |
+
aBtn.title = hasAl ? 'Desactivar alerta' : 'Activar alerta de precio'
|
| 786 |
+
})
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
async function toggleWatchlistCard(marketId, wBtn, aBtn) {
|
| 790 |
if (isInWatchlist(marketId)) {
|
| 791 |
try {
|
|
|
|
| 797 |
aBtn.textContent = 'β‘ Alertas'
|
| 798 |
aBtn.classList.remove('active')
|
| 799 |
aBtn.title = 'Activar alerta de precio'
|
| 800 |
+
renderWatchlist()
|
| 801 |
+
updateSignalCardWatchlistState(marketId)
|
| 802 |
} catch (e) { console.warn('Error al quitar de watchlist:', e) }
|
| 803 |
} else {
|
| 804 |
try {
|
|
|
|
| 807 |
wBtn.textContent = 'β
Seguimiento'
|
| 808 |
wBtn.classList.add('active')
|
| 809 |
wBtn.title = 'Quitar de seguimiento'
|
| 810 |
+
renderWatchlist()
|
| 811 |
+
updateSignalCardWatchlistState(marketId)
|
| 812 |
} catch (e) { console.warn('Error al aΓ±adir a watchlist:', e) }
|
| 813 |
}
|
| 814 |
}
|
|
|
|
| 824 |
aBtn.textContent = 'β‘ Alertas'
|
| 825 |
aBtn.classList.remove('active')
|
| 826 |
aBtn.title = 'Activar alerta de precio'
|
| 827 |
+
renderWatchlist()
|
| 828 |
+
updateSignalCardWatchlistState(marketId)
|
| 829 |
})
|
| 830 |
.catch((e) => console.warn('Error al desactivar alerta:', e))
|
| 831 |
} else {
|
|
|
|
| 845 |
aBtn.classList.add('active')
|
| 846 |
aBtn.title = 'Desactivar alerta'
|
| 847 |
thresholdRow.classList.add('hidden')
|
| 848 |
+
renderWatchlist()
|
| 849 |
+
updateSignalCardWatchlistState(marketId)
|
| 850 |
} catch (e) { console.warn('Error al configurar alerta:', e) }
|
| 851 |
}
|
| 852 |
|
|
|
|
| 1366 |
|
| 1367 |
async function closePositionById(id) {
|
| 1368 |
await simulator.closePosition(id)
|
| 1369 |
+
// El evento 'positions:changed' se encarga de refrescar las listas
|
|
|
|
|
|
|
| 1370 |
}
|
| 1371 |
|
| 1372 |
/* βββ Render watchlist view βββ */
|
|
|
|
| 1425 |
try { await api.removeFromWatchlist(marketId) } catch (e) { console.warn(e) }
|
| 1426 |
state.watchlist = state.watchlist.filter((w) => w.marketId !== marketId)
|
| 1427 |
renderWatchlist()
|
| 1428 |
+
updateSignalCardWatchlistState(marketId)
|
| 1429 |
}
|
| 1430 |
|
| 1431 |
/* βββ Render alerts view βββ */
|
|
|
|
| 1532 |
}
|
| 1533 |
}
|
| 1534 |
|
| 1535 |
+
async function refreshPositions() {
|
| 1536 |
+
await loadPositions()
|
| 1537 |
+
renderPositions()
|
| 1538 |
+
renderMiniPositions()
|
| 1539 |
+
}
|
| 1540 |
+
|
| 1541 |
async function loadWatchlist() {
|
| 1542 |
try {
|
| 1543 |
state.watchlist = await api.getWatchlist()
|
|
|
|
| 1592 |
map.init('map-container', state.markets, state.signals, selectMarket)
|
| 1593 |
simulator.init(state)
|
| 1594 |
|
| 1595 |
+
// Refrescar listas de posiciones cuando el simulador notifique cambios
|
| 1596 |
+
document.addEventListener('positions:changed', () => {
|
| 1597 |
+
refreshPositions()
|
| 1598 |
+
})
|
| 1599 |
+
|
| 1600 |
state.activeMarketId = state.markets[0]?.id || null
|
| 1601 |
renderSignals()
|
| 1602 |
renderDetail()
|
|
|
|
| 1703 |
socket.on('price_alert', (data) => {
|
| 1704 |
if (!data?.marketId || !data.type) return
|
| 1705 |
state.alerts.unshift(data)
|
| 1706 |
+
renderAlerts()
|
| 1707 |
})
|
| 1708 |
}
|
frontend/src/style.css
CHANGED
|
@@ -2362,8 +2362,7 @@ td {
|
|
| 2362 |
border-color: var(--blue2);
|
| 2363 |
}
|
| 2364 |
|
| 2365 |
-
#btn-notif
|
| 2366 |
-
#btn-auth-mobile {
|
| 2367 |
display: flex;
|
| 2368 |
}
|
| 2369 |
|
|
|
|
| 2362 |
border-color: var(--blue2);
|
| 2363 |
}
|
| 2364 |
|
| 2365 |
+
#btn-notif {
|
|
|
|
| 2366 |
display: flex;
|
| 2367 |
}
|
| 2368 |
|