feat: Cleanup tx view

This commit is contained in:
2025-11-20 16:50:09 +11:00
parent 11267421b7
commit 47719db094
2 changed files with 143 additions and 3 deletions

View File

@@ -87,7 +87,7 @@ main {
/* Info Grid */ /* Info Grid */
.info-grid { .info-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 0.75rem; gap: 0.75rem;
} }
@@ -266,6 +266,74 @@ main {
margin: 0; margin: 0;
} }
/* Transaction Details */
.tx-details {
color: #e0e0e0;
}
.tx-section {
margin-bottom: 1.5rem;
}
.tx-section h4 {
color: #ff6b35;
margin-bottom: 1rem;
font-size: 1.1rem;
}
.tx-io-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.tx-io-item {
background: rgba(20, 20, 20, 0.5);
border: 1px solid rgba(255, 107, 53, 0.15);
border-radius: 6px;
padding: 0.75rem;
}
.tx-io-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.tx-io-index {
color: #ff6b35;
font-weight: 600;
font-size: 0.9rem;
}
.tx-io-value {
color: #f7931e;
font-weight: 600;
font-size: 1rem;
}
.tx-io-address {
color: #b0b0b0;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
margin-bottom: 0.5rem;
word-break: break-all;
}
.tx-io-hash {
color: #808080;
font-size: 0.75rem;
word-break: break-all;
}
.tx-covenant {
color: #ff6b35;
font-size: 0.85rem;
margin-top: 0.5rem;
font-style: italic;
}
/* Tabs */ /* Tabs */
.search-tabs { .search-tabs {
display: flex; display: flex;

View File

@@ -194,6 +194,71 @@
`; `;
} }
// Format transaction data nicely
function formatTransactionData(tx) {
if (!tx || tx.error) {
return `<div class="error">Error: ${tx.error || 'Invalid transaction data'}</div>`;
}
const formatValue = (value) => (value / 1e6).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' HNS';
const formatRate = (value) => (value / 1e3).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' doo/vB';
let html = `
<div class="tx-details">
<div class="tx-section">
<h4>Transaction Info</h4>
<div class="info-grid">
<div class="info-item full-width"><strong>Hash:</strong> <span class="mono">${tx.hash}</span></div>
<div class="info-item full-width"><strong>Block:</strong> <span class="mono tx-block-hash">${tx.block || 'Unconfirmed'}</span></div>
<div class="info-item"><strong>Height:</strong> ${tx.height >= 0 ? tx.height.toLocaleString() : 'Pending'}</div>
<div class="info-item"><strong>Confirmations:</strong> ${tx.confirmations.toLocaleString()}</div>
<div class="info-item"><strong>Fee:</strong> ${formatValue(tx.fee)}</div>
<div class="info-item"><strong>Rate:</strong> ${formatRate(tx.rate)}</div>
</div>
</div>
<div class="tx-section">
<h4>Inputs (${tx.inputs.length})</h4>
<div class="tx-io-list">
${tx.inputs.map((input, i) => `
<div class="tx-io-item">
<div class="tx-io-header">
<span class="tx-io-index">#${i}</span>
<span class="tx-io-value">${formatValue(input.coin.value)}</span>
</div>
<div class="tx-io-address">${input.coin.address}</div>
<div class="tx-io-hash mono">${input.prevout.hash}:${input.prevout.index}</div>
</div>
`).join('')}
</div>
</div>
<div class="tx-section">
<h4>Outputs (${tx.outputs.length})</h4>
<div class="tx-io-list">
${tx.outputs.map((output, i) => `
<div class="tx-io-item">
<div class="tx-io-header">
<span class="tx-io-index">#${i}</span>
<span class="tx-io-value">${formatValue(output.value)}</span>
</div>
<div class="tx-io-address">${output.address}</div>
${output.covenant.action !== 'NONE' ? `<div class="tx-covenant">Covenant: ${output.covenant.action}</div>` : ''}
</div>
`).join('')}
</div>
</div>
<div class="tx-section">
<button class="secondary-btn" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'">Show Raw JSON</button>
<pre style="display: none;">${JSON.stringify(tx, null, 2)}</pre>
</div>
</div>
`;
return html;
}
// View transaction details // View transaction details
async function viewTransaction(txId) { async function viewTransaction(txId) {
const data = await apiCall(`tx/${txId}`); const data = await apiCall(`tx/${txId}`);
@@ -213,7 +278,7 @@
<button class="tx-modal-close" onclick="this.parentElement.parentElement.parentElement.remove()">×</button> <button class="tx-modal-close" onclick="this.parentElement.parentElement.parentElement.remove()">×</button>
</div> </div>
<div class="tx-modal-body"> <div class="tx-modal-body">
${data.error ? `<div class="error">Error: ${data.error}</div>` : `<pre>${JSON.stringify(data, null, 2)}</pre>`} ${data.error ? `<div class="error">Error: ${data.error}</div>` : formatTransactionData(data)}
</div> </div>
</div> </div>
`; `;
@@ -287,7 +352,14 @@
return; return;
} }
const data = await apiCall(`tx/${txId}`); const data = await apiCall(`tx/${txId}`);
displayResult('tx-result', data);
// Use formatted display instead of raw JSON
const resultElement = document.getElementById('tx-result');
if (data.error) {
resultElement.innerHTML = `<div class="error">Error: ${data.error}</div>`;
} else {
resultElement.innerHTML = formatTransactionData(data);
}
} }
async function searchAddressTx() { async function searchAddressTx() {