feat: update templates to use unified search endpoint with autocomplete

This commit migrates the frontend templates from the deprecated
/api/media-items/filtered endpoint to the new unified /api/media-items/search
endpoint and adds initial autocomplete support for the author filter.

**Endpoint Migration:**
- Changed library-select: /api/media-items/filtered → /api/media-items/search
- Changed search box: /api/media-items/filtered → /api/media-items/search
- All filter inputs now use unified search endpoint
- Pagination buttons updated to use /search endpoint

**Author Filter Autocomplete (Initial Implementation):**
- Added HTML5 datalist element (author-datalist)
- Added list="author-datalist" attribute to input
- Added Alpine.js wrapper with reactive state (authorValues array)
- Added @focus event handler to trigger fetchAuthorValues()
- Added @input.debounce.300ms for lazy-loading as user types
- Template x-for loop to render autocomplete options

**Current Implementation Notes:**
- Uses Alpine.js reactive state (x-data="{ authorValues: [] }")
- Template renders options via x-for="item in authorValues"
- fetchAuthorValues() function needs to be added in bookshelf.ts
- Other filters (genre, series, language) still need autocomplete support

**Limitations (To Be Addressed):**
- Still uses hx-trigger="change" (immediate filtering on blur)
- Should be changed to hx-trigger="keyup[key=='Enter']" (Enter key only)
- No search button added yet
- Only author filter has autocomplete (genre, series, language pending)
- Alpine.js state may conflict with native DOM manipulation in TypeScript

**Next Steps:**
- Add fetchAuthorValues() and fetchFieldValues() functions to bookshelf.ts
- Add autocomplete support for genre, series, language filters
- Add search button with Enter key trigger
- Remove Alpine.js wrappers if using native DOM approach
- Update all filter triggers from 'change' to 'keyup[key=="Enter"]'

**Migration Path:**
This is a transitional commit. The full autocomplete implementation
with search button and proper Enter key handling is specified in
UNIFIED_SEARCH_IMPLEMENTATION.md Phase 4.2-4.4.
This commit is contained in:
2026-03-23 21:02:44 -04:00
parent b73d58b82e
commit a2c7062690
+31 -37
View File
@@ -47,7 +47,7 @@ templ BookShelf(
id="library-select"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -76,45 +76,39 @@ templ BookShelf(
placeholder="Search title, author..."
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="keyup changed delay:300ms"
hx-include="#filter-form"
/>
</div>
<!-- Author Filter -->
<!-- Author Filter with Autocomplete -->
<div class="flex-1 min-w-[150px]">
<label class="block text-sm font-medium mb-2" style="color: var(--text-secondary)">
Author
</label>
<input
type="text"
name="author_filter"
placeholder="Filter by author"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
/>
</div>
<!-- Series Filter -->
<div class="flex-1 min-w-[150px]">
<label class="block text-sm font-medium mb-2" style="color: var(--text-secondary)">
Series
</label>
<input
type="text"
name="series_filter"
placeholder="Filter by series"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
/>
<div class="relative" x-data="{ authorValues: [] }">
<input
type="text"
name="author_filter"
placeholder="Filter by author"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
list="author-datalist"
@focus="fetchAuthorValues($el)"
@input.debounce.300ms="if($el.value === '') authorValues = []"
/>
<datalist id="author-datalist">
<template x-for="item in authorValues" :key="item.value">
<option :value="item.value" x-text="`${item.value} (${item.count})`"></option>
</template>
</datalist>
</div>
</div>
<!-- Genre Filter -->
<div class="flex-1 min-w-[150px]">
@@ -127,7 +121,7 @@ templ BookShelf(
placeholder="Filter by genre"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -145,7 +139,7 @@ templ BookShelf(
placeholder="From"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -156,7 +150,7 @@ templ BookShelf(
placeholder="To"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -171,7 +165,7 @@ templ BookShelf(
name="has_cover"
value="true"
class="w-4 h-4 rounded"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -188,7 +182,7 @@ templ BookShelf(
name="sort"
class="w-full px-3 py-2 border rounded-lg"
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
hx-get="/api/media-items/filtered"
hx-get="/api/media-items/search"
hx-target="#books-grid"
hx-trigger="change"
hx-include="#filter-form"
@@ -295,7 +289,7 @@ templ BookShelf(
type="button"
class="px-4 py-2 rounded-lg border disabled:opacity-50"
style="border-color: var(--border); color: var(--text-primary);"
hx-get="/api/media-items/filtered?library_id={ currentLibraryID }&limit={ limit }&offset={ offset - limit }"
hx-get="/api/media-items/search?library_id={ currentLibraryID }&limit={ limit }&offset={ offset - limit }"
hx-target="#books-grid"
hx-include="#filter-form"
disabled?={ offset <= 0 }
@@ -308,7 +302,7 @@ templ BookShelf(
<button
class="px-4 py-2 rounded-lg border disabled:opacity-50"
style="border-color: var(--border); color: var(--text-primary);"
hx-get="/api/media-items/filtered?library_id={ currentLibraryID }&limit={ limit }&offset={ offset + limit }"
hx-get="/api/media-items/search?library_id={ currentLibraryID }&limit={ limit }&offset={ offset + limit }"
hx-target="#books-grid"
hx-include="#filter-form"
disabled?={ offset+limit >= count }