mirror of
https://github.com/Labelbase/Labelbase.git
synced 2026-08-16 13:01:02 +02:00
389 lines
14 KiB
HTML
389 lines
14 KiB
HTML
{% extends "_base.html" %}
|
|
{% load i18n %}
|
|
{% load labelbase_tags %}
|
|
{% load importer_tags %}
|
|
{% load sekizai_tags %}
|
|
{% load bootstrap %}
|
|
|
|
{% block content %}
|
|
|
|
{% include "_labelbase_header_info_menu.html" %}
|
|
|
|
{% if labelbase %}
|
|
{% if request.GET.tag %}
|
|
Hashtag filter is active:
|
|
<span class="badge badge-hashtag badge-hashtag-nohover">
|
|
<tt style="pointer-events: none;">{{ request.GET.tag }}</tt>
|
|
<button onclick="window.location='{% url 'labelbase' labelbase.id %}'";
|
|
type="button"
|
|
class="btn-close"
|
|
style="padding-left: 0.4rem; margin-right: -0.1rem; font-size: .6rem; font-weight: bolder !important;"></button>
|
|
</span>
|
|
{% endif %}
|
|
|
|
<div class="table-responsive d-none d-md-block" style="padding-top:1em">
|
|
{% if label_list %}
|
|
|
|
<ul class="nav nav-tabs" id="typeFilterTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if not request.GET.type or request.GET.type == 'all' %}active{% endif %}" data-type="all">All</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'tx' %}active{% endif %}" data-type="tx">Transactions</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'output' %}active{% endif %}" data-type="output">Outputs</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'input' %}active{% endif %}" data-type="input">Inputs</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'addr' %}active{% endif %}" data-type="addr">Addresses</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'pubkey' %}active{% endif %}" data-type="pubkey">Pubkeys</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link {% if request.GET.type == 'xpub' %}active{% endif %}" data-type="xpub">XPubs</button>
|
|
</li>
|
|
</ul>
|
|
<br>
|
|
<table id="bip329labels" class="table table-striped table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">#</th>
|
|
<th scope="col">type</th>
|
|
<th scope="col">ref</th>
|
|
<th scope="col">label</th>
|
|
<th scope="col">origin</th>
|
|
<th scope="col">spendable</th>
|
|
<th scope="col">health</th>
|
|
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
<div id="datatable-info-regular"></div>
|
|
{% else %}
|
|
<p>There are no labels in this labelbase.</p>
|
|
<p>You can create new labels by using the 'New Label' button on the top right, or <a href="#" data-bs-toggle="modal" data-bs-target="#importLabelbaseModal">import your existing labels</a>.</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="d-md-none" style="padding-top:0.5em;">
|
|
<div id="datatable-info-mobile"></div>
|
|
<div class="input-group mb-3" style="padding-top:0.5em;">
|
|
<input type="text" id="mobileSearch" class="form-control" placeholder="Search">
|
|
</div>
|
|
<div id="mobileLabels" class="row">
|
|
<!-- Cards will be injected here -->
|
|
</div>
|
|
<div id="mobilePagination" class="mt-3">
|
|
<!-- DataTable pagination will be injected here -->
|
|
</div>
|
|
</div>
|
|
|
|
{% include "_modal_add_label.html" %}
|
|
{% include "_modal_edit_labelbase.html" %}
|
|
{% include "_modal_connect_api_key.html" %}
|
|
{% include "_modal_delete_labelbase.html" %}
|
|
|
|
<style>
|
|
@media (max-width: 767.98px) {
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: center;
|
|
width: 100%;
|
|
}
|
|
|
|
.pagination .page-item {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.pagination .page-link {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 0.5rem;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.pagination .page-item:first-child .page-link,
|
|
.pagination .page-item:last-child .page-link {
|
|
font-size: 1rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.card {
|
|
/* margin-bottom: 1rem;*/
|
|
|
|
}
|
|
|
|
.card .card-body {
|
|
padding: 1rem;
|
|
padding-bottom: 0 !important;
|
|
}
|
|
|
|
.card-title {
|
|
margin-bottom: 0.75rem;
|
|
font-size: 1.25rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.card-text {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.card-text strong {
|
|
display: inline-block;
|
|
min-width: 80px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.td-key {
|
|
width: 6rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
{% addtoblock "js" %}
|
|
$(document).ready(function () {
|
|
window.currentTypeFilter = 'all';
|
|
const dt_table = $('#bip329labels').DataTable({
|
|
order: [[0, "asc"]],
|
|
columns: [
|
|
{ data: 'id', orderable: true, searchable: true },
|
|
{ data: 'type', orderable: true, searchable: true },
|
|
{ data: 'ref', orderable: true, searchable: true },
|
|
{ data: 'label', orderable: true, searchable: true },
|
|
{ data: 'origin', orderable: true, searchable: true },
|
|
{ data: 'spendable', orderable: true, searchable: true },
|
|
{ data: 'get_fee_health_status_display', orderable: false, searchable: false}
|
|
|
|
],
|
|
|
|
searching: true,
|
|
processing: false,
|
|
serverSide: true,
|
|
stateSave: true,
|
|
responsive: true,
|
|
ajax: {
|
|
// url: "{% url 'labelbase_label_data' labelbase.pk %}?tag={{ request.GET.tag }}",
|
|
url: "{% url 'labelbase_label_data' labelbase.pk %}?tag={{ request.GET.tag }}&type={{ request.GET.type|default:'all' }}",
|
|
type: 'GET',
|
|
dataSrc: 'data'
|
|
},
|
|
drawCallback: function(settings) {
|
|
let isMobile = $(window).width() < 768;
|
|
if (isMobile) {
|
|
renderMobileView(settings.json.data, settings._iDisplayStart, settings._iDisplayLength, settings._iRecordsDisplay, settings.fnRecordsTotal());
|
|
}
|
|
}
|
|
});
|
|
|
|
// Tab click handler - full page reload
|
|
$('#typeFilterTabs button').on('click', function() {
|
|
const typeFilter = $(this).data('type');
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
|
|
if (typeFilter === 'all') {
|
|
urlParams.delete('type');
|
|
} else {
|
|
urlParams.set('type', typeFilter);
|
|
}
|
|
|
|
// Full page reload with new URL
|
|
window.location.search = urlParams.toString();
|
|
});
|
|
|
|
function createCard(data) {
|
|
let originText = '';
|
|
if (data.origin) {
|
|
originText = `<p class="card-text"><strong>Origin:</strong> ${data.origin}</p>`;
|
|
}
|
|
let spendableText = '';
|
|
if (data.type === '<tt>output</tt>' && (data.spendable === "<tt>true</tt>" || data.spendable === "<tt>false</tt>")) {
|
|
spendableText = `<tr><td> <strong>Spendable:</strong></td><td> ${data.spendable === "<tt>true</tt>" ? 'true' : 'false'}</td></tr>`;
|
|
}
|
|
let healthText = '';
|
|
if (data.get_fee_health_status_display) {
|
|
healthText = `<tr><td><strong>Health:</strong></td><td>${data.get_fee_health_status_display}</td></tr>`;
|
|
}
|
|
|
|
|
|
return `
|
|
<div class="col-12 mb-3">
|
|
<div class="card">
|
|
<div class="card-body position-relative">
|
|
<strong>
|
|
<span class="card-title" style="max-width: 88%; font-size: 1.2em; display: inline-block;">
|
|
${data.label}
|
|
</span>
|
|
</strong>
|
|
<button type="button" class="btn btn-link btn-sm position-absolute top-0 end-0 m-2">
|
|
#${data.id}
|
|
</button>
|
|
<table class="table table-borderless mt-3">
|
|
<tbody>
|
|
<tr>
|
|
<td class="td-key"><strong>Type:</strong></td>
|
|
<td>${data.type}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Ref:</strong></td>
|
|
<td>${data.ref}</td>
|
|
</tr>
|
|
${originText ? `<tr><td colspan="2">${originText}</td></tr>` : ''}
|
|
${spendableText ? `${spendableText}` : ''}
|
|
${healthText}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
|
|
|
|
function renderMobileView(data, start, length, totalRecords, totalRecordsAll) {
|
|
$('#mobileLabels').empty();
|
|
data.forEach(function(item) {
|
|
$('#mobileLabels').append(createCard(item));
|
|
});
|
|
|
|
renderMobilePagination(start, length, totalRecords);
|
|
}
|
|
|
|
function renderMobilePagination(start, length, totalRecords) {
|
|
const totalPages = Math.ceil(totalRecords / length);
|
|
const currentPage = Math.ceil(start / length) + 1;
|
|
|
|
let paginationHtml = '<nav><ul class="pagination">';
|
|
if (currentPage > 1) {
|
|
paginationHtml += `<li class="page-item"><a class="page-link" href="#">Previous</a></li>`;
|
|
}
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
paginationHtml += `<li class="page-item ${i === currentPage ? 'active' : ''}"><a class="page-link" href="#">${i}</a></li>`;
|
|
}
|
|
if (currentPage < totalPages) {
|
|
paginationHtml += `<li class="page-item"><a class="page-link" href="#">Next</a></li>`;
|
|
}
|
|
paginationHtml += '</ul></nav>';
|
|
|
|
$('#mobilePagination').html(paginationHtml);
|
|
|
|
$('.page-link').click(function (e) {
|
|
e.preventDefault();
|
|
let page = $(this).text();
|
|
if (page === 'Previous') {
|
|
page = currentPage - 1;
|
|
} else if (page === 'Next') {
|
|
page = currentPage + 1;
|
|
} else {
|
|
page = parseInt(page);
|
|
}
|
|
const newStart = (page - 1) * length;
|
|
dt_table.page(page - 1).draw(false);
|
|
});
|
|
}
|
|
|
|
function updateInfo(start, end, totalFiltered, total, isMobile) {
|
|
let info = `Showing ${start} to ${end} of ${totalFiltered} entries`;
|
|
if (totalFiltered !== total) {
|
|
info += ` (filtered from ${total} total entries)`;
|
|
}
|
|
if (isMobile) {
|
|
$('#datatable-info-mobile').html(info);
|
|
$('#datatable-info-regular').empty(); // Clear regular info
|
|
} else {
|
|
$('#datatable-info-regular').html(info);
|
|
$('#datatable-info-mobile').empty(); // Clear mobile info
|
|
}
|
|
}
|
|
|
|
$('#mobileSearch').on('keyup', function() {
|
|
$('#bip329labels_filter input').val(this.value).trigger('keyup');
|
|
update_showing();
|
|
|
|
});
|
|
|
|
$('#bip329labels_filter input').on('keyup', function() {
|
|
$('#mobileSearch').val(this.value);
|
|
});
|
|
/*
|
|
$(window).resize(function() {
|
|
let isMobile = $(window).width() < 768;
|
|
if (isMobile) {
|
|
dt_table.ajax.reload(function(json) {
|
|
renderMobileView(json.data, dt_table.page.info().start, dt_table.page.info().length, dt_table.page.info().recordsDisplay, dt_table.page.info().recordsTotal);
|
|
updateInfo(dt_table.page.info().start + 1, Math.min(dt_table.page.info().start + dt_table.page.info().length, dt_table.page.info().recordsDisplay), dt_table.page.info().recordsDisplay, dt_table.page.info().recordsTotal, true);
|
|
});
|
|
}
|
|
});
|
|
|
|
if ($(window).width() < 768) {
|
|
dt_table.ajax.reload(function(json) {
|
|
renderMobileView(json.data, dt_table.page.info().start, dt_table.page.info().length, dt_table.page.info().recordsDisplay, dt_table.page.info().recordsTotal);
|
|
updateInfo(dt_table.page.info().start + 1, Math.min(dt_table.page.info().start + dt_table.page.info().length, dt_table.page.info().recordsDisplay), dt_table.page.info().recordsDisplay, dt_table.page.info().recordsTotal, true);
|
|
});
|
|
} */
|
|
|
|
|
|
|
|
function update_showing() {
|
|
if ($(window).width() < 768) {
|
|
dt_table.ajax.reload(function(json) {
|
|
let start = dt_table.page.info().start;
|
|
let length = json.data.length;
|
|
let recordsDisplay = dt_table.page.info().recordsDisplay;
|
|
let recordsTotal = dt_table.page.info().recordsTotal;
|
|
|
|
renderMobileView(json.data, start, length, recordsDisplay, recordsTotal);
|
|
updateInfo(Math.min(recordsDisplay, start +1), Math.min(start + length, recordsDisplay), length, recordsTotal, true);
|
|
});
|
|
}
|
|
}
|
|
// run once
|
|
update_showing();
|
|
|
|
$(window).resize(function() {
|
|
let isMobile = $(window).width() < 768;
|
|
if (isMobile) {
|
|
update_showing();
|
|
/*dt_table.ajax.reload(function(json) {
|
|
let start = dt_table.page.info().start;
|
|
let length = json.data.length;
|
|
let recordsDisplay = dt_table.page.info().recordsDisplay;
|
|
let recordsTotal = dt_table.page.info().recordsTotal;
|
|
|
|
renderMobileView(json.data, start, length, recordsDisplay, recordsTotal);
|
|
updateInfo(start + 1, Math.min(start + length, recordsDisplay), length, recordsTotal, true);
|
|
});*/
|
|
}
|
|
});
|
|
|
|
|
|
|
|
var qrcode = new QRCode("qrcode", {
|
|
text: JSON.stringify({
|
|
api_key: '{{ api_token }}',
|
|
api_base: 'https://labelbase.space/api/',
|
|
labelbase_id: {{ labelbase.id }},
|
|
name: '{{ labelbase.name }}',
|
|
fingerprint: '{{ labelbase.fingerprint }}'
|
|
}),
|
|
width: 200,
|
|
height: 200,
|
|
colorDark : "#000000",
|
|
colorLight : "#ffffff",
|
|
correctLevel : QRCode.CorrectLevel.H
|
|
});
|
|
});
|
|
{% endaddtoblock %}
|
|
</script>
|
|
|
|
{% endif %}
|
|
{% endblock %}
|