Skip to content

Commit bb8a76c

Browse files
authored
Create index.html
1 parent e295ce1 commit bb8a76c

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

templates/index.html

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
{% extends "base.html" %}
2+
{% from 'macros.html' import blockchain_id, copy_icon %}
3+
4+
{% block content %}
5+
<div class="row g-4">
6+
<!-- Blockchain Statistics -->
7+
<div class="col-md-6">
8+
<div class="card stats-card h-100">
9+
<div class="card-header">
10+
<h5 class="card-title">Blockchain Statistics</h5>
11+
</div>
12+
<div class="card-body">
13+
{% if stats %}
14+
<div class="d-flex flex-column gap-3">
15+
<div class="stat-item">
16+
<span class="stat-label">Market Price (USD)</span>
17+
<span class="stat-value">${{ "%.2f"|format(stats.market_price_usd|float) }}</span>
18+
</div>
19+
<div class="stat-item">
20+
<span class="stat-label">Hash Rate</span>
21+
<span class="stat-value">{{ "%.2f"|format(stats.hash_rate|float) }} TH/s</span>
22+
</div>
23+
</div>
24+
{% else %}
25+
<div class="text-center text-muted py-4">
26+
<p>Statistics temporarily unavailable</p>
27+
</div>
28+
{% endif %}
29+
</div>
30+
</div>
31+
</div>
32+
33+
<!-- Network Metrics -->
34+
<div class="col-md-6">
35+
<div class="card stats-card h-100">
36+
<div class="card-header">
37+
<h5 class="card-title">Network Metrics</h5>
38+
</div>
39+
<div class="card-body">
40+
{% if stats %}
41+
<div class="d-flex flex-column gap-3">
42+
<div class="stat-item">
43+
<span class="stat-label">Block Time</span>
44+
<span class="stat-value">{{ "%.1f"|format(stats.minutes_between_blocks|float) }} min</span>
45+
</div>
46+
<div class="stat-item">
47+
<span class="stat-label">Total Blocks</span>
48+
<span class="stat-value">{{ "{:,}".format(stats.n_blocks_total|int) }}</span>
49+
</div>
50+
</div>
51+
{% else %}
52+
<div class="text-center text-muted py-4">
53+
<p>Metrics temporarily unavailable</p>
54+
</div>
55+
{% endif %}
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
61+
<!-- Live Transactions -->
62+
<div class="card mt-4">
63+
<div class="card-header d-flex justify-content-between align-items-center">
64+
<h5 class="card-title mb-0">Live Transactions</h5>
65+
<span id="connection-status" class="status-connected">Connected</span>
66+
</div>
67+
<div class="card-body p-0">
68+
<div class="table-responsive">
69+
<table class="table table-hover mb-0">
70+
<thead>
71+
<tr>
72+
<th class="hash-column">Hash</th>
73+
<th class="amount-column">Amount (BTC)</th>
74+
<th class="address-column">From</th>
75+
<th class="address-column">To</th>
76+
</tr>
77+
</thead>
78+
<tbody id="live-transactions">
79+
<!-- Live transactions will be inserted here -->
80+
</tbody>
81+
</table>
82+
</div>
83+
<div class="text-center p-4 border-top">
84+
<a href="/transactions" class="view-more-btn">
85+
View More Transactions
86+
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
87+
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
88+
</svg>
89+
</a>
90+
</div>
91+
</div>
92+
</div>
93+
{% endblock %}
94+
95+
{% block scripts %}
96+
<script>
97+
document.addEventListener('DOMContentLoaded', function() {
98+
const txTable = document.getElementById('live-transactions');
99+
const statusBadge = document.getElementById('connection-status');
100+
const maxRows = 10;
101+
102+
function formatAddress(addr) {
103+
if (!addr) return 'Unknown';
104+
return `${addr.substring(0, 12)}...${addr.substring(addr.length - 8)}`;
105+
}
106+
107+
function formatAddresses(addresses, type) {
108+
if (!addresses || addresses.length === 0) return 'Unknown';
109+
const formatted = formatAddress(addresses[0]);
110+
const extra = addresses.length > 1 ? ` +${addresses.length - 1}` : '';
111+
const addressClass = type === 'sender' ? 'sender' : 'receiver';
112+
return `
113+
<div class="blockchain-id ${addressClass}">
114+
<a href="/address/${addresses[0]}">${formatted}${extra}</a>
115+
<button class="copy-button" data-clipboard="${addresses[0]}" title="Copy ${type} address">
116+
{{ copy_icon() }}
117+
</button>
118+
</div>
119+
`;
120+
}
121+
122+
function removeOldTransactions() {
123+
const rows = txTable.getElementsByTagName('tr');
124+
if (rows.length > maxRows) {
125+
const rowsToRemove = Array.from(rows).slice(maxRows);
126+
rowsToRemove.forEach(row => {
127+
row.classList.add('removing-transaction');
128+
setTimeout(() => {
129+
if (row.parentNode === txTable) {
130+
txTable.removeChild(row);
131+
}
132+
}, 300);
133+
});
134+
}
135+
}
136+
137+
function addTransaction(tx) {
138+
const row = document.createElement('tr');
139+
const amountClass = tx.amount >= 0 ? 'amount-positive' : 'amount-negative';
140+
141+
row.innerHTML = `
142+
<td class="hash-column">
143+
<div class="blockchain-id">
144+
<a href="/tx/${tx.hash}">${tx.hash}</a>
145+
<button class="copy-button" data-clipboard="${tx.hash}" title="Copy transaction hash">
146+
{{ copy_icon() }}
147+
</button>
148+
</div>
149+
</td>
150+
<td class="amount-column ${amountClass}">${tx.amount.toFixed(8)}</td>
151+
<td class="address-column">${formatAddresses(tx.from, 'sender')}</td>
152+
<td class="address-column">${formatAddresses(tx.to, 'receiver')}</td>
153+
`;
154+
155+
row.classList.add('new-transaction');
156+
txTable.insertBefore(row, txTable.firstChild);
157+
158+
// Add copy functionality
159+
row.querySelectorAll('.copy-button').forEach(button => {
160+
button.addEventListener('click', function() {
161+
const textToCopy = this.dataset.clipboard;
162+
navigator.clipboard.writeText(textToCopy).then(() => {
163+
const tooltip = document.createElement('div');
164+
tooltip.className = 'copy-tooltip';
165+
tooltip.textContent = 'Copied!';
166+
tooltip.style.top = `${this.getBoundingClientRect().top - 30}px`;
167+
tooltip.style.left = `${this.getBoundingClientRect().left}px`;
168+
document.body.appendChild(tooltip);
169+
setTimeout(() => tooltip.remove(), 1500);
170+
});
171+
});
172+
});
173+
174+
// Remove old transactions after animation completes
175+
setTimeout(removeOldTransactions, 300);
176+
}
177+
178+
const evtSource = new EventSource('/transactions/stream');
179+
180+
evtSource.onmessage = function(event) {
181+
const data = JSON.parse(event.data);
182+
if (data.ping) return;
183+
addTransaction(data);
184+
};
185+
186+
evtSource.onerror = function() {
187+
statusBadge.textContent = 'Disconnected';
188+
statusBadge.classList.remove('status-connected');
189+
statusBadge.classList.add('status-disconnected');
190+
};
191+
192+
evtSource.onopen = function() {
193+
statusBadge.textContent = 'Connected';
194+
statusBadge.classList.remove('status-disconnected');
195+
statusBadge.classList.add('status-connected');
196+
};
197+
});
198+
</script>
199+
{% endblock %}

0 commit comments

Comments
 (0)