working
This commit is contained in:
@@ -251,6 +251,7 @@
|
|||||||
let wsReconnectTimer = null;
|
let wsReconnectTimer = null;
|
||||||
let wsReconnectAttempts = 0;
|
let wsReconnectAttempts = 0;
|
||||||
let maxReconnectAttempts = 10;
|
let maxReconnectAttempts = 10;
|
||||||
|
let watchlistUpdateTimeout = null;
|
||||||
|
|
||||||
let state = {
|
let state = {
|
||||||
spots: [],
|
spots: [],
|
||||||
@@ -281,6 +282,7 @@
|
|||||||
wsStatus: 'disconnected',
|
wsStatus: 'disconnected',
|
||||||
activeTab: 'stats',
|
activeTab: 'stats',
|
||||||
watchlist: [],
|
watchlist: [],
|
||||||
|
watchlistShowOnlyActive: false,
|
||||||
spotFilters: {
|
spotFilters: {
|
||||||
showAll: true,
|
showAll: true,
|
||||||
showNewDXCC: false,
|
showNewDXCC: false,
|
||||||
@@ -372,6 +374,7 @@
|
|||||||
case 'stats':
|
case 'stats':
|
||||||
state.stats = message.data;
|
state.stats = message.data;
|
||||||
updateStatsOnly();
|
updateStatsOnly();
|
||||||
|
updateStatusIndicators();
|
||||||
break;
|
break;
|
||||||
case 'spots':
|
case 'spots':
|
||||||
state.spots = message.data || [];
|
state.spots = message.data || [];
|
||||||
@@ -381,6 +384,16 @@
|
|||||||
if (state.activeTab === 'stats') {
|
if (state.activeTab === 'stats') {
|
||||||
updateBandPropagation();
|
updateBandPropagation();
|
||||||
}
|
}
|
||||||
|
if (state.activeTab === 'watchlist') {
|
||||||
|
if (watchlistUpdateTimeout) {
|
||||||
|
clearTimeout(watchlistUpdateTimeout);
|
||||||
|
}
|
||||||
|
watchlistUpdateTimeout = setTimeout(() => {
|
||||||
|
updateWatchlistItems();
|
||||||
|
updateWatchlistCounters();
|
||||||
|
watchlistUpdateTimeout = null;
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'spotters':
|
case 'spotters':
|
||||||
state.topSpotters = message.data || [];
|
state.topSpotters = message.data || [];
|
||||||
@@ -554,6 +567,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateSpotCounts() {
|
function updateSpotCounts() {
|
||||||
// Vider le cache
|
// Vider le cache
|
||||||
countCache = {};
|
countCache = {};
|
||||||
@@ -1624,7 +1638,17 @@
|
|||||||
<!-- Watchlist Tab -->
|
<!-- Watchlist Tab -->
|
||||||
<div class="tab-content ${state.activeTab === 'watchlist' ? 'active' : ''}" style="height: 100%; display: ${state.activeTab === 'watchlist' ? 'flex' : 'none'}; flex-direction: column; overflow: hidden;">
|
<div class="tab-content ${state.activeTab === 'watchlist' ? 'active' : ''}" style="height: 100%; display: ${state.activeTab === 'watchlist' ? 'flex' : 'none'}; flex-direction: column; overflow: hidden;">
|
||||||
<div class="p-3 border-b border-slate-700/50 flex-shrink-0">
|
<div class="p-3 border-b border-slate-700/50 flex-shrink-0">
|
||||||
<h2 class="text-lg font-bold mb-2">Watchlist</h2>
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<h2 class="text-lg font-bold">Watchlist</h2>
|
||||||
|
<button
|
||||||
|
onclick="toggleWatchlistActiveOnly()"
|
||||||
|
class="px-3 py-1.5 text-xs rounded transition-colors flex items-center gap-2 ${state.watchlistShowOnlyActive ? 'bg-blue-600 text-white' : 'bg-slate-700/50 text-slate-300 hover:bg-slate-700'}">
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
|
||||||
|
</svg>
|
||||||
|
${state.watchlistShowOnlyActive ? 'Show All' : 'Active Only'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<p class="text-xs text-slate-400 mb-3 watchlist-counter">${countWatchlistSpots()} matching spots</p>
|
<p class="text-xs text-slate-400 mb-3 watchlist-counter">${countWatchlistSpots()} matching spots</p>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -1760,6 +1784,9 @@
|
|||||||
updateBandPropagation();
|
updateBandPropagation();
|
||||||
updateTopSpotters(); // Au cas où
|
updateTopSpotters(); // Au cas où
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateStatusIndicators();
|
||||||
|
|
||||||
// Restaurer le scroll de la table des spots
|
// Restaurer le scroll de la table des spots
|
||||||
const newSpotsContainer = document.querySelector('.scrollable');
|
const newSpotsContainer = document.querySelector('.scrollable');
|
||||||
if (newSpotsContainer && scrollTop > 0) {
|
if (newSpotsContainer && scrollTop > 0) {
|
||||||
@@ -1863,7 +1890,6 @@
|
|||||||
// Grouper les spots par callsign/prefix
|
// Grouper les spots par callsign/prefix
|
||||||
const spotsByCallsign = {};
|
const spotsByCallsign = {};
|
||||||
watchlistSpots.forEach(spot => {
|
watchlistSpots.forEach(spot => {
|
||||||
// Trouver le pattern correspondant
|
|
||||||
let matchedPattern = '';
|
let matchedPattern = '';
|
||||||
for (const pattern of state.watchlist) {
|
for (const pattern of state.watchlist) {
|
||||||
if (spot.dx === pattern || spot.dx.startsWith(pattern)) {
|
if (spot.dx === pattern || spot.dx.startsWith(pattern)) {
|
||||||
@@ -1878,7 +1904,26 @@
|
|||||||
spotsByCallsign[matchedPattern].push(spot);
|
spotsByCallsign[matchedPattern].push(spot);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.innerHTML = state.watchlist.map(callsign => {
|
// ✅ Filtrer la liste selon le toggle "Active Only"
|
||||||
|
const displayList = state.watchlistShowOnlyActive
|
||||||
|
? state.watchlist.filter(callsign => (spotsByCallsign[callsign] || []).length > 0)
|
||||||
|
: state.watchlist;
|
||||||
|
|
||||||
|
// ✅ Afficher un message si aucun callsign avec spots actifs
|
||||||
|
if (displayList.length === 0 && state.watchlistShowOnlyActive) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="text-center py-8 text-slate-400">
|
||||||
|
<svg class="w-12 h-12 mx-auto mb-3 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4" />
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm">No active spots for watchlist callsigns</p>
|
||||||
|
<p class="text-xs mt-1 text-slate-500">Click "Active Only" to see all watchlist entries</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = displayList.map(callsign => {
|
||||||
const spots = spotsByCallsign[callsign] || [];
|
const spots = spotsByCallsign[callsign] || [];
|
||||||
const matchingCount = spots.length;
|
const matchingCount = spots.length;
|
||||||
|
|
||||||
@@ -1922,21 +1967,28 @@
|
|||||||
</div>
|
</div>
|
||||||
` : '<div class="mt-2 text-xs text-slate-500 text-center py-2 bg-slate-800/30 rounded">No active spots</div>';
|
` : '<div class="mt-2 text-xs text-slate-500 text-center py-2 bg-slate-800/30 rounded">No active spots</div>';
|
||||||
|
|
||||||
const neededCount = spots.filter(s => !s.workedBandMode).length;
|
// ✅ Ne calculer le badge que s'il y a des spots
|
||||||
let neededBadge = '';
|
let neededBadge = '';
|
||||||
if (matchingCount > 0) {
|
if (matchingCount > 0) {
|
||||||
neededBadge = neededCount > 0
|
const neededCount = spots.filter(s => !s.workedBandMode).length;
|
||||||
? `<span class="px-1.5 py-0.5 bg-orange-500/20 text-orange-400 rounded text-xs font-semibold">${neededCount} needed</span>`
|
neededBadge = neededCount > 0
|
||||||
: '<span class="px-1.5 py-0.5 bg-green-500/20 text-green-400 rounded text-xs font-semibold">All worked</span>';
|
? `<span class="px-1.5 py-0.5 bg-orange-500/20 text-orange-400 rounded text-xs font-semibold">${neededCount} needed</span>`
|
||||||
}
|
: '<span class="px-1.5 py-0.5 bg-green-500/20 text-green-400 rounded text-xs font-semibold">All worked</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const neededCount = spots.filter(s => !s.workedBandMode).length;
|
||||||
|
const borderClass = neededCount > 0 ? 'border-orange-500/30' : 'border-slate-700/50';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="mb-3 p-3 bg-slate-900/30 rounded hover:bg-slate-700/30 transition-colors border ${neededCount > 0 ? 'border-orange-500/30' : 'border-slate-700/50'}" data-watchlist="${callsign}">
|
<div class="mb-3 p-3 bg-slate-900/30 rounded hover:bg-slate-700/30 transition-colors border ${borderClass}" data-watchlist="${callsign}">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
<div class="font-bold text-pink-400 text-lg">${callsign}</div>
|
<div class="font-bold text-pink-400 text-lg">${callsign}</div>
|
||||||
<span class="text-xs text-slate-400">${matchingCount} active spot${matchingCount !== 1 ? 's' : ''}</span>
|
${matchingCount > 0
|
||||||
|
? `<span class="text-xs text-slate-400">${matchingCount} active spot${matchingCount !== 1 ? 's' : ''}</span>`
|
||||||
|
: '<span class="text-xs text-slate-500">No active spots</span>'
|
||||||
|
}
|
||||||
${neededBadge}
|
${neededBadge}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1960,6 +2012,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleWatchlistActiveOnly() {
|
||||||
|
state.watchlistShowOnlyActive = !state.watchlistShowOnlyActive;
|
||||||
|
|
||||||
|
// Sauvegarder la préférence
|
||||||
|
localStorage.setItem('watchlistShowOnlyActive', state.watchlistShowOnlyActive);
|
||||||
|
|
||||||
|
// Mettre à jour visuellement le bouton
|
||||||
|
const button = document.querySelector('button[onclick="toggleWatchlistActiveOnly()"]');
|
||||||
|
if (button) {
|
||||||
|
if (state.watchlistShowOnlyActive) {
|
||||||
|
button.className = 'px-3 py-1.5 text-xs rounded transition-colors flex items-center gap-2 bg-blue-600 text-white';
|
||||||
|
button.innerHTML = `
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
|
||||||
|
</svg>
|
||||||
|
Show All
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
button.className = 'px-3 py-1.5 text-xs rounded transition-colors flex items-center gap-2 bg-slate-700/50 text-slate-300 hover:bg-slate-700';
|
||||||
|
button.innerHTML = `
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
|
||||||
|
</svg>
|
||||||
|
Active Only
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour la liste
|
||||||
|
updateWatchlistItems();
|
||||||
|
}
|
||||||
|
|
||||||
async function initPropagation() {
|
async function initPropagation() {
|
||||||
state.solarData = await fetchPropagationData();
|
state.solarData = await fetchPropagationData();
|
||||||
|
|
||||||
@@ -1973,6 +2057,10 @@
|
|||||||
// Initialize
|
// Initialize
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
|
const savedPref = localStorage.getItem('watchlistShowOnlyActive');
|
||||||
|
if (savedPref !== null) {
|
||||||
|
state.watchlistShowOnlyActive = savedPref === 'true';
|
||||||
|
}
|
||||||
await initPropagation();
|
await initPropagation();
|
||||||
await fetchLogData();
|
await fetchLogData();
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
["H44MS","5X2I","PY0FB","PY0FBS","XT2AW","ZL7IO","YJ0CA","FW5K","J38","E6AD","E51MWA","PJ6Y","5J0EA","5K0UA","VP2M"]
|
["H44MS","5X2I","PY0FB","PY0FBS","XT2AW","ZL7IO","YJ0CA","FW5K","J38","E6AD","E51MWA","PJ6Y","5J0EA","5K0UA","VP2M","5X1XA","C5R","C5LT","EL2BG"]
|
||||||
Reference in New Issue
Block a user