labelbase/django/templates/labelbase.html
Xavier Fiechter fff7ac548c wip
2025-12-12 23:47:04 +01:00

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 %}