refactor: convert filter form from hidden to visible structure
Restructured the bookshelf filter form to be a proper visible form instead of individual inputs with HTMX attributes pointing to a hidden form. Changes: - Wrapped all filter inputs in a visible <form id="filter-form"> with hx-get="/api/media-items/search" and hx-target="#books-grid" - Removed redundant HTMX attributes from individual inputs since they're now part of the form - Added "Search" submit button to explicitly trigger form submission - Moved hidden pagination state inputs (limit, offset) inside the form - Preserved all existing functionality: autocomplete, fuzzy search, saved filters, clear filters button - Added checked attribute to has_cover checkbox for default state This change fixes the architectural issue where filter inputs were outside the form and relied on hx-include, which was fragile and made form handling complex. The new structure is more maintainable and follows standard HTML form patterns. The form now properly includes all filter parameters when submitted, ensuring that search, filters, and pagination work correctly together.
This commit is contained in:
1 parent
ba243c223d
commit
77cbeb0bcf
2 files changed
+28
-56
No files matched your search
+16
-44
@@ -37,6 +37,7 @@ templ BookShelf(
|
|||||||
class="mb-6 card p-4 rounded-lg border"
|
class="mb-6 card p-4 rounded-lg border"
|
||||||
style="background-color: var(--bg-secondary); border-color: var(--border);"
|
style="background-color: var(--bg-secondary); border-color: var(--border);"
|
||||||
>
|
>
|
||||||
|
<form id="filter-form" hx-get="/api/media-items/search" hx-target="#books-grid">
|
||||||
<div class="flex flex-wrap gap-4 items-center">
|
<div class="flex flex-wrap gap-4 items-center">
|
||||||
<!-- Library Selector -->
|
<!-- Library Selector -->
|
||||||
<div class="flex-1 min-w-[200px]">
|
<div class="flex-1 min-w-[200px]">
|
||||||
@@ -47,10 +48,6 @@ templ BookShelf(
|
|||||||
id="library-select"
|
id="library-select"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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 len(libraries) == 0 {
|
if len(libraries) == 0 {
|
||||||
<option value="">No libraries available</option>
|
<option value="">No libraries available</option>
|
||||||
@@ -74,10 +71,6 @@ templ BookShelf(
|
|||||||
name="q"
|
name="q"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search all fields..."
|
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>
|
</div>
|
||||||
<!-- Author Filter with Autocomplete -->
|
<!-- Author Filter with Autocomplete -->
|
||||||
@@ -91,10 +84,6 @@ templ BookShelf(
|
|||||||
placeholder="Filter by author"
|
placeholder="Filter by author"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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"
|
list="author-datalist"
|
||||||
@input.debounce.300ms="if($el.value.length >= 2) fetchAuthorValues($el)"
|
@input.debounce.300ms="if($el.value.length >= 2) fetchAuthorValues($el)"
|
||||||
/>
|
/>
|
||||||
@@ -109,10 +98,6 @@ templ BookShelf(
|
|||||||
placeholder="Filter by tags"
|
placeholder="Filter by tags"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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="tags-datalist"
|
list="tags-datalist"
|
||||||
@input.debounce.300ms="if($el.value.length >= 2) fetchTagValues($el)"
|
@input.debounce.300ms="if($el.value.length >= 2) fetchTagValues($el)"
|
||||||
/>
|
/>
|
||||||
@@ -129,10 +114,6 @@ templ BookShelf(
|
|||||||
// placeholder="Filter by genre"
|
// placeholder="Filter by genre"
|
||||||
// class="w-full px-3 py-2 border rounded-lg"
|
// class="w-full px-3 py-2 border rounded-lg"
|
||||||
// style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
// 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"
|
// list="genre-datalist"
|
||||||
// @input.debounce.300ms="if($el.value.length >= 2) fetchGenreValues($el)"
|
// @input.debounce.300ms="if($el.value.length >= 2) fetchGenreValues($el)"
|
||||||
// />
|
// />
|
||||||
@@ -149,10 +130,6 @@ templ BookShelf(
|
|||||||
placeholder="Filter by series"
|
placeholder="Filter by series"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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"
|
list="series-datalist"
|
||||||
@input.debounce.300ms="if($el.value.length >= 2) fetchSeriesValues($el)"
|
@input.debounce.300ms="if($el.value.length >= 2) fetchSeriesValues($el)"
|
||||||
/>
|
/>
|
||||||
@@ -169,10 +146,6 @@ templ BookShelf(
|
|||||||
placeholder="Filter by language"
|
placeholder="Filter by language"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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"
|
list="language-datalist"
|
||||||
@input.debounce.300ms="if($el.value.length >= 2) fetchLanguageValues($el)"
|
@input.debounce.300ms="if($el.value.length >= 2) fetchLanguageValues($el)"
|
||||||
/>
|
/>
|
||||||
@@ -190,10 +163,6 @@ templ BookShelf(
|
|||||||
placeholder="From"
|
placeholder="From"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@@ -201,25 +170,19 @@ templ BookShelf(
|
|||||||
placeholder="To"
|
placeholder="To"
|
||||||
class="w-full px-3 py-2 border rounded-lg"
|
class="w-full px-3 py-2 border rounded-lg"
|
||||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
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>
|
||||||
</div>
|
</div>
|
||||||
<!-- Has Cover Filter -->
|
<!-- Has Cover Filter -->
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
<label class="flex items-center gap-2 cursor-pointer">
|
<label class="flex items-center gap-2 cursor-pointer">
|
||||||
|
<input type="hidden" name="has_cover" value="false"/>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="has_cover"
|
name="has_cover"
|
||||||
value="true"
|
value="true"
|
||||||
|
checked
|
||||||
class="w-4 h-4 rounded"
|
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>
|
<span class="text-sm" style="color: var(--text-primary)">Has Cover</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -245,6 +208,16 @@ templ BookShelf(
|
|||||||
<option value="page_count DESC">Page Count</option>
|
<option value="page_count DESC">Page Count</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Submit Button -->
|
||||||
|
<div class="flex items-end">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="px-6 py-2 rounded-lg font-medium"
|
||||||
|
style="background-color: var(--accent); color: var(--bg-primary);"
|
||||||
|
>
|
||||||
|
🔍 Search
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<!-- Save Filter Button (PRESERVED) -->
|
<!-- Save Filter Button (PRESERVED) -->
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
<button
|
<button
|
||||||
@@ -313,10 +286,6 @@ templ BookShelf(
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<!-- Books Grid -->
|
<!-- Books Grid -->
|
||||||
@@ -411,6 +380,9 @@ templ BookShelf(
|
|||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Hidden pagination state -->
|
||||||
|
<input type="hidden" name="limit" value="50"/>
|
||||||
|
<input type="hidden" name="offset" value="0"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func BookShelf(
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
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/search\" 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);\"><form id=\"filter-form\" hx-get=\"/api/media-items/search\" hx-target=\"#books-grid\"><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);\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 60, Col: 33}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 57, Col: 34}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -84,7 +84,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 60, Col: 55}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 57, Col: 56}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -102,7 +102,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 62, Col: 33}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 59, Col: 34}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -115,7 +115,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 62, Col: 46}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 59, Col: 47}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -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 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><!-- Tags Filter with Autocomplete --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Tags</label> <input type=\"text\" name=\"tags_filter\" placeholder=\"Filter by tags\" 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=\"tags-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchTagValues($el)\"> <datalist id=\"tags-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/search\" hx-taLine 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...\"></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);\" list=\"author-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchAuthorValues($el)\"> <datalist id=\"author-datalist\"></datalist></div><!-- Tags Filter with Autocomplete --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Tags</label> <input type=\"text\" name=\"tags_filter\" placeholder=\"Filter by tags\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" list=\"tags-datalist\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchTagValues($el)\"> <datalist id=\"tags-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);\" 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);\" 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);\"> <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);\"></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\"> <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/search\" 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><!-- Submit Button --><div class=\"flex items-end\"><button type=\"submit\" class=\"px-6 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">🔍 Search</button></div><!-- Save Filter Button (PRESERVED) --><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 (PRESERVED) --><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 (PRESERVED) --><div x-show=\"showFiltersDropdownLine truncated
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(uuidToString(filter.ID))
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(uuidToString(filter.ID))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 288, Col: 172}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 259, Col: 173}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -153,7 +153,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(filter.Name)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(filter.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 290, Col: 26}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 261, Col: 27}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -174,7 +174,7 @@ func BookShelf(
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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\">")
|
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></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 {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 328, Col: 59}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 295, Col: 59}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -227,7 +227,7 @@ func BookShelf(
|
|||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(offset/limit + 1)
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(offset/limit + 1)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 347, Col: 32}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 314, Col: 32}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -248,7 +248,7 @@ func BookShelf(
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div><!-- Save Filter Modal --><div x-show=\"showSaveModal\" @click.self=\"showSaveModal = false\" x-transition class=\"fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7); display: none;\"><div @click.stop class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Save Filter</h2><button @click=\"showSaveModal = false\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><form @submit=\"saveFilter($event)\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Filter Name</label> <input type=\"text\" x-model=\"filterName\" placeholder=\"My Custom Filter\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" required></div><div class=\"flex justify-end gap-2\"><button type=\"button\" @click=\"showSaveModal = false\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button type=\"submit\" class=\"px-4 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">Save</button></div></form></div></div></body></html>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div><!-- Save Filter Modal --><div x-show=\"showSaveModal\" @click.self=\"showSaveModal = false\" x-transition class=\"fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7); display: none;\"><div @click.stop class=\"card rounded-lg p-6 w-full max-w-md mx-4\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex justify-between items-center mb-4\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Save Filter</h2><button @click=\"showSaveModal = false\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><form @submit=\"saveFilter($event)\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Filter Name</label> <input type=\"text\" x-model=\"filterName\" placeholder=\"My Custom Filter\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" required></div><div class=\"flex justify-end gap-2\"><button type=\"button\" @click=\"showSaveModal = false\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button type=\"submit\" class=\"px-4 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">Save</button></div><!-- Hidden pagination state --><input type=\"hidden\" name=\"limit\" value=\"50\"> <input type=\"hidden\" name=\"offset\" value=\"0\"></form></div></div></body></html>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user