feat(collections): add collection-specific search/filter (Limitation #3)
Add search and filter within a collection: Frontend Implementation: - Search input box in collection detail toolbar - filterCollectionBooks() JavaScript function - Real-time filtering of books by title and author - Case-insensitive search - Hides non-matching book cards - Shows all books when search is cleared How It Works: - AllBooks array contains book data from server render - On keyup, filters books by title and author - Toggles display property on book cards - Empty state has ID and is not hidden - Pure client-side filtering (no server round-trips) User Experience: - Instant search results as user types - No page reload required - Works with existing multi-select for bulk operations - Search box always visible in toolbar Resolves Limitation #3: Collection-Specific Search
This commit is contained in:
@@ -247,6 +247,12 @@ templ CollectionDetail(user User, collection CollectionDetailData, books []BookD
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<div class="flex-1 max-w-md">
|
||||
<input type="text" id="collection-search" placeholder="Search within collection..."
|
||||
onkeyup="filterCollectionBooks()"
|
||||
class="w-full px-4 py-2 border rounded-lg"
|
||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);">
|
||||
</div>
|
||||
<button id="bulk-remove-btn" onclick="removeSelectedBooks()" disabled
|
||||
class="btn-danger px-4 py-2 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
🗑️ Remove Selected
|
||||
@@ -259,7 +265,7 @@ templ CollectionDetail(user User, collection CollectionDetailData, books []BookD
|
||||
|
||||
<div id="books-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
if len(books) == 0 {
|
||||
<div class="col-span-full text-center py-16" style="color: var(--text-secondary)">No books in this collection yet.</div>
|
||||
<div id="empty-state" class="col-span-full text-center py-16" style="color: var(--text-secondary)">No books in this collection yet.</div>
|
||||
}
|
||||
|
||||
for _, book := range books {
|
||||
@@ -319,11 +325,45 @@ templ CollectionDetail(user User, collection CollectionDetailData, books []BookD
|
||||
let collectionId = '{ collection.ID }';
|
||||
let selectedBooks = new Set();
|
||||
let booksToRemove = new Set();
|
||||
let allBooks = [
|
||||
{ range books }{
|
||||
{
|
||||
id: '{ book.MediaItemID }',
|
||||
title: '{ book.Title }',
|
||||
author: '{ book.Author }',
|
||||
cover: '{ book.CoverImagePath }'
|
||||
},
|
||||
}{ end }
|
||||
];
|
||||
|
||||
function backToCollections() {
|
||||
window.location.href = '/collections';
|
||||
}
|
||||
|
||||
function filterCollectionBooks() {
|
||||
const searchTerm = document.getElementById('collection-search').value.toLowerCase();
|
||||
const booksContainer = document.getElementById('books-container');
|
||||
const bookCards = booksContainer.children;
|
||||
|
||||
let visibleCount = 0;
|
||||
for (let i = 0; i < bookCards.length; i++) {
|
||||
const card = bookCards[i];
|
||||
if (card.id === 'empty-state') continue;
|
||||
|
||||
const title = card.querySelector('.font-semibold')?.textContent.toLowerCase() || '';
|
||||
const author = card.querySelector('.text-sm')?.textContent.toLowerCase() || '';
|
||||
|
||||
const matches = title.includes(searchTerm) || author.includes(searchTerm);
|
||||
|
||||
if (matches || searchTerm === '') {
|
||||
card.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showAddBooksModal() {
|
||||
document.getElementById('add-books-modal').classList.remove('hidden');
|
||||
selectedBooks.clear();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user