You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

107 lines
4.4 KiB

{{define "content"}}
<article class="ebooks-page">
<header class="page-header">
<h1>E-Books</h1>
<p class="page-summary">Top-level publications (index events) from Nostr. These are publications that are not contained in any higher-level index event.</p>
</header>
<div class="ebooks-container">
<table id="ebooks-table" class="ebooks-table" aria-label="E-Books listing">
<thead>
<tr>
<th data-sort="title" class="sortable">Title <span class="sort-indicator"></span></th>
<th data-sort="author" class="sortable">Author <span class="sort-indicator"></span></th>
<th data-sort="type" class="sortable">Type <span class="sort-indicator"></span></th>
<th data-sort="created" class="sortable">Created <span class="sort-indicator"></span></th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{range .EBooks}}
<tr>
<td>
<strong>{{.Title}}</strong>
{{if .Summary}}<br><small class="text-muted">{{.Summary}}</small>{{end}}
</td>
<td>{{template "user-badge-simple" .Author}}</td>
<td>{{if .Type}}{{.Type}}{{else}}—{{end}}</td>
<td>
<time datetime="{{.TimeISO}}">{{.Time}}</time>
</td>
<td>
<a href="https://alexandria.gitcitadel.eu/naddr/{{.Naddr}}" target="_blank" rel="noopener noreferrer" class="btn btn-sm"><i data-lucide="external-link" class="icon-inline"></i> View</a>
</td>
</tr>
{{else}}
<tr>
<td colspan="5" class="text-center"><i data-lucide="book-x" class="icon-inline"></i> No e-books found.</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</article>
<script>
(function() {
const table = document.getElementById('ebooks-table');
if (!table) return;
const tbody = table.querySelector('tbody');
const headers = table.querySelectorAll('th.sortable');
let currentSort = { column: null, direction: 'asc' };
headers.forEach(header => {
header.addEventListener('click', function() {
const column = this.dataset.sort;
const direction = currentSort.column === column && currentSort.direction === 'asc' ? 'desc' : 'asc';
// Update sort indicators
headers.forEach(h => {
const indicator = h.querySelector('.sort-indicator');
if (h === this) {
indicator.textContent = direction === 'asc' ? '↑' : '↓';
} else {
indicator.textContent = '↕';
}
});
// Sort rows
const rows = Array.from(tbody.querySelectorAll('tr'));
rows.sort((a, b) => {
let aVal, bVal;
const aCell = a.cells[Array.from(headers).indexOf(this)];
const bCell = b.cells[Array.from(headers).indexOf(this)];
if (column === 'title') {
aVal = aCell.querySelector('strong')?.textContent || '';
bVal = bCell.querySelector('strong')?.textContent || '';
} else if (column === 'created') {
aVal = aCell.querySelector('time')?.getAttribute('datetime') || '';
bVal = bCell.querySelector('time')?.getAttribute('datetime') || '';
} else {
aVal = aCell.textContent.trim();
bVal = bCell.textContent.trim();
}
if (direction === 'asc') {
return aVal.localeCompare(bVal);
} else {
return bVal.localeCompare(aVal);
}
});
// Re-append sorted rows
rows.forEach(row => tbody.appendChild(row));
currentSort = { column, direction };
});
// Add cursor pointer style
header.style.cursor = 'pointer';
});
})();
</script>
{{end}}
{{/* Feed is defined in components.html */}}