feat: update bookshelf template with unified search UI
- Change search box to use /api/media-items/search endpoint (was /filtered) - Add autocomplete to all 4 text filters: author, genre, series, language - Add series and language filters (were missing) - Add datalist elements for autocomplete dropdowns - Change filter triggers to Enter key instead of instant search - Preserve existing sort dropdown (all 5 options: title ASC/DESC, author ASC/DESC, created_at ASC/DESC, page_count ASC/DESC) - Preserve Save Filter button and modal - Preserve Load Filter button and dropdown - Preserve Clear Filters button - Update pagination to use /search endpoint - Add Alpine.js event handlers for dropdown population (@input.debounce.300ms) All filter inputs include hidden filter-form via hx-include for combined searches.
This commit is contained in:
1 parent
172536f888
commit
2e24ce00cf
2 files changed
+77
-42
No files matched your search
@@ -52,7 +52,7 @@ func BookShelf(
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"w-full px-4 sm:px-6 lg:px-8 py-8\"><!-- Filter Bar --><div class=\"mb-6 card p-4 rounded-lg border\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex flex-wrap gap-4 items-center\"><!-- Library Selector --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Library</label> <select 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-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"w-full px-4 sm:px-6 lg:px-8 py-8\"><!-- Filter Bar --><div class=\"mb-6 card p-4 rounded-lg border\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex flex-wrap gap-4 items-center\"><!-- Library Selector --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Library</label> <select 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/search\" hx-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -128,7 +128,7 @@ func BookShelf(
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</select></div><!-- Search Input --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Search</label> <input type=\"text\" name=\"search\" 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-target=\"#books-grid\" hx-trigger=\"keyup changed delay:300ms\" hx-include=\"#filter-form\"></div><!-- Author Filter --><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><!-- Genre Filter --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Genre</label> <input type=\"text\" name=\"genre_filter\" 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-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"></div><!-- Year Range --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Year Range</label><div class=\"flex gap-2\"><input type=\"number\" name=\"year_min\" 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-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"> <input type=\"number\" name=\"year_max\" 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-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"></div></div><!-- Has Cover Filter --><div class=\"flex items-end\"><label class=\"flex items-center gap-2 cursor-pointer\"><input type=\"checkbox\" name=\"has_cover\" value=\"true\" class=\"w-4 h-4 rounded\" hx-get=\"/api/media-items/filtered\" hx-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"> <span class=\"text-sm\" style=\"color: var(--text-primary)\">Has Cover</span></label></div><!-- Sort --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Sort By</label> <select 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-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"><option value=\"title ASC\">Title (A-Z)</option> <option value=\"title DESC\">Title (Z-A)</option> <option value=\"author ASC\">Author (A-Z)</option> <option value=\"created_at DESC\">Date Added</option> <option value=\"page_count DESC\">Page Count</option></select></div><!-- Save Filter Button --><div class=\"flex items-end\"><button @click=\"showSaveFilterModal()\" class=\"px-4 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">💾 Save Filter</button></div><!-- Load Filter Button --><div class=\"flex items-end relative\"><button @click=\"toggleFiltersDropdown()\" class=\"px-4 py-2 rounded-lg font-medium border\" style=\"border-color: var(--border); color: var(--text-primary);\">📂 Load Filter</button><!-- Saved Filters Dropdown --><div x-show=\"showFiltersDropdown\" @click.outside=\"showFiltersDropdown = false\" x-transition:enter=\"transition ease-out duration-200\" x-transition:enter-start=\"opacity-0 scale-95\" x-transition:enter-end=\"opacity-100 scale-100\" x-transition:leave=\"transition ease-in duration-150\" x-transition:leave-start=\"opacity-100 scale-100\" x-transition:leave-end=\"opacity-0 scale-95\" class=\"aLine truncated
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</select></div><!-- Search Input --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Search</label> <input name=\"q\" type=\"text\" placeholder=\"Search all fields...\" hx-get=\"/api/media-items/search\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form, keyup changed delay:500ms\" hx-target=\"#books-grid\" hx-include=\"#filter-form, #library-select\"></div><!-- 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/search\" hx-target=\"#books-grid\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\" list=\"author-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchAuthorValues($el)\"> <datalist id=\"author-datalist\"></datalist></div><!-- Genre Filter with Autocomplete --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Genre</label> <input type=\"text\" name=\"genre_filter\" 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/search\" hx-target=\"#books-grid\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\" list=\"genre-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchGenreValues($el)\"> <datalist id=\"genre-datalist\"></datalist></div><!-- Series Filter with Autocomplete (NEW) --><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/search\" hx-target=\"#books-grid\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\" list=\"series-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchSeriesValues($el)\"> <datalist id=\"series-datalist\"></datalist></div><!-- Language Filter with Autocomplete (NEW) --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Language</label> <input type=\"text\" name=\"language_filter\" placeholder=\"Filter by language\" 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=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\" list=\"language-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchLanguageValues($el)\"> <datalist id=\"language-datalist\"></datalist></div><!-- Year Range --><div class=\"flex-1 min-w-[200px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Year Range</label><div class=\"flex gap-2\"><input type=\"number\" name=\"year_min\" 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/search\" hx-target=\"#books-grid\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\"> <input type=\"number\" name=\"year_max\" 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/search\" hx-target=\"#books-grid\" hx-trigger=\"keyup[key=='Enter'] from:#filter-form\" hx-include=\"#filter-form\"></div></div><!-- Has Cover Filter --><div class=\"flex items-end\"><label class=\"flex items-center gap-2 cursor-pointer\"><input type=\"checkbox\" name=\"has_cover\" value=\"true\" class=\"w-4 h-4 rounded\" hx-get=\"/api/media-items/search\" hx-target=\"#books-grid\" hx-trigger=\"change\" hx-include=\"#filter-form\"> <span class=\"text-sm\" style=\"color: var(--text-primary)\">Has Cover</span></label></div><!-- Sort By Dropdown (PRESERVED) --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Sort By</label> <select 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/searchLine truncated
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -140,7 +140,7 @@ func BookShelf(
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(uuidToString(filter.ID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 243, Col: 172}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 270, Col: 172}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -153,7 +153,7 @@ func BookShelf(
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(filter.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 245, Col: 26}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 272, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -174,7 +174,7 @@ func BookShelf(
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div></div><!-- Clear Filters --><div class=\"flex items-end\"><button @click=\"clearFilters()\" class=\"px-4 py-2 rounded-lg font-medium border\" style=\"border-color: var(--border); color: var(--text-primary);\">✕ Clear</button></div></div><!-- Hidden form for HTMX include --><form id=\"filter-form\" class=\"hidden\"><input type=\"hidden\" name=\"limit\" value=\"50\"> <input type=\"hidden\" name=\"offset\" value=\"0\"></form></div><!-- Books Grid --><div id=\"books-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</div></div></div><!-- Clear Filters Button (PRESERVED) --><div class=\"flex items-end\"><button @click=\"clearFilters()\" class=\"px-4 py-2 rounded-lg font-medium border\" style=\"border-color: var(--border); color: var(--text-primary);\">✕ Clear</button></div></div><!-- Hidden form for HTMX include (PRESERVED) --><form id=\"filter-form\" class=\"hidden\"><input type=\"hidden\" name=\"limit\" value=\"50\"> <input type=\"hidden\" name=\"offset\" value=\"0\"></form></div><!-- Books Grid --><div id=\"books-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -196,7 +196,7 @@ func BookShelf(
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if count > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<button 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-target=\"#books-grid\" hx-include=\"#filter-form\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<button 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/search?library_id={ currentLibraryID }&limit={ limit }&offset={ offset - limit }\" hx-target=\"#books-grid\" hx-include=\"#filter-form\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -213,13 +213,13 @@ func BookShelf(
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(offset/limit + 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 306, Col: 32}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 333, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</span> <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-target=\"#books-grid\" hx-include=\"#filter-form\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</span> <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/search?library_id={ currentLibraryID }&limit={ limit }&offset={ offset + limit }\" hx-target=\"#books-grid\" hx-include=\"#filter-form\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -246,7 +246,7 @@ func BookShelf(
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 323, Col: 58}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 350, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
||||
Reference in new issue
Block a user