Fix UTXO list page: remove block number column, add fees update button, add RPC load option

**Motivations:**
- Supprimer la colonne "Numéro de bloc" dans la section Ancrages (toujours vide)
- Afficher le bouton de mise à jour des frais même quand la section est vide
- Permettre de charger les statuts et dates complets depuis RPC

**Root causes:**
- La colonne "Numéro de bloc" affichait toujours "-" car blockHeight n'est pas dans le fichier utxo_list.txt
- Le bouton de mise à jour des frais n'était visible que si des frais existaient déjà
- Les statuts (isSpentOnchain, isLockedInMutex) et certaines dates ne sont pas dans le fichier, nécessitent un chargement RPC

**Correctifs:**
- Suppression de la colonne "Numéro de bloc" dans la section Ancrages (header et données)
- Ajout du bouton "Récupérer les frais depuis les ancrages" même quand fees.length === 0
- Ajout d'un bouton "Charger depuis RPC (statuts complets)" pour obtenir les données complètes
- Ajout de la fonction loadUtxoListFromRPC() qui charge depuis /api/utxo/list au lieu de /api/utxo/list.txt

**Evolutions:**
- Nouvelle option de chargement depuis RPC pour obtenir les statuts complets (Dépensé onchain, Verrouillé, Disponible)
- Les dates complètes sont disponibles via le chargement RPC même si blockTime manque dans le fichier

**Pages affectées:**
- signet-dashboard/public/utxo-list.html
This commit is contained in:
ncantu 2026-01-26 03:41:08 +01:00
parent 9d85092487
commit 85c2810dad

View File

@ -303,7 +303,8 @@
<button class="consolidate-button" id="consolidate-button" onclick="consolidateSmallUtxos()" style="margin-left: 10px; background: #ffc107; color: #000; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 1em; margin-top: 10px;">Chargement...</button> <button class="consolidate-button" id="consolidate-button" onclick="consolidateSmallUtxos()" style="margin-left: 10px; background: #ffc107; color: #000; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 1em; margin-top: 10px;">Chargement...</button>
<p><strong>Montant total :</strong> <span id="total-amount" class="total-amount">-</span></p> <p><strong>Montant total :</strong> <span id="total-amount" class="total-amount">-</span></p>
<p><strong>Dernière mise à jour :</strong> <span id="last-update">-</span></p> <p><strong>Dernière mise à jour :</strong> <span id="last-update">-</span></p>
<button class="refresh-button" onclick="loadUtxoList()">Actualiser</button> <button class="refresh-button" onclick="loadUtxoList()">Actualiser (fichier)</button>
<button class="refresh-button" onclick="loadUtxoListFromRPC()" style="margin-left: 10px; background: #007bff;">Charger depuis RPC (statuts complets)</button>
<a href="/api/utxo/list.txt" download="utxo_list.txt" style="margin-left: 10px; color: #007bff; text-decoration: none;">📥 Télécharger le fichier texte</a> <a href="/api/utxo/list.txt" download="utxo_list.txt" style="margin-left: 10px; color: #007bff; text-decoration: none;">📥 Télécharger le fichier texte</a>
</div> </div>
</div> </div>
@ -455,7 +456,6 @@
if (isAnchors) { if (isAnchors) {
tableHTML += ` tableHTML += `
<th>Numéro de bloc</th>
<th class="sortable-header" style="text-align: right;" onclick="toggleSort('${categoryName}', 'amount')">Montant (✅)${getSortArrow('amount', state.sortColumn, state.sortDirection)}</th> <th class="sortable-header" style="text-align: right;" onclick="toggleSort('${categoryName}', 'amount')">Montant (✅)${getSortArrow('amount', state.sortColumn, state.sortDirection)}</th>
<th>Date</th> <th>Date</th>
<th class="sortable-header" onclick="toggleSort('${categoryName}', 'confirmed')">Confirmé${getSortArrow('confirmed', state.sortColumn, state.sortDirection)}</th> <th class="sortable-header" onclick="toggleSort('${categoryName}', 'confirmed')">Confirmé${getSortArrow('confirmed', state.sortColumn, state.sortDirection)}</th>
@ -504,7 +504,6 @@
tableHTML += `<td>${utxo.vout}</td>`; tableHTML += `<td>${utxo.vout}</td>`;
if (isAnchors) { if (isAnchors) {
tableHTML += `<td>${utxo.blockHeight !== null && utxo.blockHeight !== undefined ? utxo.blockHeight.toLocaleString('fr-FR') : '-'}</td>`;
tableHTML += `<td class="amount-cell">${amountSats.toLocaleString('fr-FR')} ✅</td>`; tableHTML += `<td class="amount-cell">${amountSats.toLocaleString('fr-FR')} ✅</td>`;
// Date pour les ancrages // Date pour les ancrages
if (utxo.blockTime) { if (utxo.blockTime) {
@ -610,15 +609,91 @@
} }
} }
async function loadUtxoList() { async function loadUtxoListFromRPC() {
const contentDiv = document.getElementById('content'); const contentDiv = document.getElementById('content');
const refreshButton = document.querySelector('.refresh-button'); const buttons = document.querySelectorAll('.refresh-button');
const progressSection = document.getElementById('progress-section'); const progressSection = document.getElementById('progress-section');
const progressBarFill = document.getElementById('progress-bar-fill'); const progressBarFill = document.getElementById('progress-bar-fill');
const progressPercent = document.getElementById('progress-percent'); const progressPercent = document.getElementById('progress-percent');
const progressStats = document.getElementById('progress-stats'); const progressStats = document.getElementById('progress-stats');
refreshButton.disabled = true; buttons.forEach(btn => btn.disabled = true);
contentDiv.innerHTML = '<div class="loading">Chargement depuis RPC (peut prendre plusieurs minutes)...</div>';
utxosFromFile = false;
progressSection.style.display = 'block';
progressBarFill.style.width = '0%';
progressPercent.textContent = '0 %';
progressStats.textContent = 'Chargement depuis RPC...';
loadSmallUtxosInfo();
try {
const response = await fetch(`${API_BASE_URL}/api/utxo/list`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
progressBarFill.style.width = '100%';
progressPercent.textContent = '100 %';
progressStats.textContent = 'Données chargées depuis RPC';
const blocRewards = data.blocRewards || [];
const anchors = data.anchors || [];
const changes = data.changes || [];
const fees = data.fees || [];
const minAnchorAmount = 2000 / 100000000;
const total = blocRewards.length + anchors.length + changes.length + fees.length;
const availableForAnchor = anchors.filter(u =>
u.amount >= minAnchorAmount && (u.confirmations || 0) > 0 && !u.isSpentOnchain && !u.isLockedInMutex
).length;
const totalAmount = blocRewards.reduce((s, u) => s + u.amount, 0) +
anchors.reduce((s, u) => s + u.amount, 0) +
changes.reduce((s, u) => s + u.amount, 0);
currentUtxosData = { blocRewards, anchors, changes, fees };
document.getElementById('utxo-count').textContent = total.toLocaleString('fr-FR');
document.getElementById('available-for-anchor').textContent = availableForAnchor.toLocaleString('fr-FR');
document.getElementById('total-amount').textContent = formatBTC(totalAmount);
updateLastUpdateTime();
progressSection.style.display = 'none';
document.getElementById('navigation').style.display = 'flex';
let html = '';
html += renderTable(blocRewards, 'bloc-rewards', '💰 Bloc Rewards (Récompenses de minage)');
html += renderTable(anchors, 'ancrages', '🔗 Ancrages (Transactions d\'ancrage)');
html += renderTable(changes, 'changes', '🔄 Changes (Monnaie de retour)');
html += renderFeesTable(fees, 'fees', '💸 Frais (Transactions d\'ancrage)');
contentDiv.innerHTML = html;
} catch (error) {
console.error('Error loading UTXO list from RPC:', error);
progressSection.style.display = 'none';
contentDiv.innerHTML = `<div class="error">Erreur lors du chargement depuis RPC : ${error.message}</div>`;
} finally {
buttons.forEach(btn => btn.disabled = false);
loadSmallUtxosInfo();
}
}
async function loadUtxoList() {
const contentDiv = document.getElementById('content');
const buttons = document.querySelectorAll('.refresh-button');
const progressSection = document.getElementById('progress-section');
const progressBarFill = document.getElementById('progress-bar-fill');
const progressPercent = document.getElementById('progress-percent');
const progressStats = document.getElementById('progress-stats');
buttons.forEach(btn => {
if (btn.textContent.includes('fichier')) {
btn.disabled = true;
}
});
contentDiv.innerHTML = ''; contentDiv.innerHTML = '';
utxosFromFile = true; utxosFromFile = true;
progressSection.style.display = 'block'; progressSection.style.display = 'block';
@ -731,7 +806,11 @@
progressSection.style.display = 'none'; progressSection.style.display = 'none';
contentDiv.innerHTML = `<div class="error">Erreur lors du chargement de la liste des UTXO : ${error.message}</div>`; contentDiv.innerHTML = `<div class="error">Erreur lors du chargement de la liste des UTXO : ${error.message}</div>`;
} finally { } finally {
refreshButton.disabled = false; buttons.forEach(btn => {
if (btn.textContent.includes('fichier')) {
btn.disabled = false;
}
});
loadSmallUtxosInfo(); loadSmallUtxosInfo();
} }
} }
@ -795,6 +874,10 @@
<span><strong>Nombre :</strong> 0</span> <span><strong>Nombre :</strong> 0</span>
<span><strong>Total des frais :</strong> 0 ✅</span> <span><strong>Total des frais :</strong> 0 ✅</span>
</div> </div>
<button id="update-fees-button" class="refresh-button" onclick="updateFeesFromAnchors()" style="margin-top: 10px;">
<span id="update-fees-spinner" style="display: none;"></span>
Récupérer les frais depuis les ancrages
</button>
</div> </div>
<div class="empty-message">${emptyReason}</div> <div class="empty-message">${emptyReason}</div>
</div> </div>