Files
bookhoard/templates/bookshelf_templ.go
T
john-okeefe 05370d236a feat(ui): display media counts in library switcher
The UI had no surface showing how many media items have been imported.
Surface the total in the library switcher shown on the Dashboard, Series,
and Collections pages (via the LibrarySwitcher component) and in the
Bookshelf's inline library filter.

- Add a MediaCount field to LibraryData and a TotalMediaCount helper to
  sum counts for the "All Libraries" / "All Books" option.
- resolveLibrary() now fetches per-library counts (one query) and maps
  them onto each LibraryData entry, so the switcher reflects the active
  scope without changing the component's signature.
- Each library option renders "(N)" and the "All" option renders the
  grand total across the user's visible libraries.

The "All" total is the sum of the user's visible libraries, correctly
respecting per-user library visibility rather than a raw global count.

Regenerated templ files for library_switcher and bookshelf.
2026-07-30 11:41:13 -04:00

323 lines
24 KiB
Go

// Code generated by templ - DO NOT EDIT.
// templ: version: v0.3.1020
package templates
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"bookhoard/internal/database"
"bookhoard/internal/handlers"
)
func BookShelf(
user User,
libraries []LibraryData,
currentLibraryID string,
errorMessage string,
savedFilters []database.SavedFilters,
books []handlers.BookInfo, // NEW
limit int, // NEW
offset int, // NEW
count int, // NEW
) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Library - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body class=\"theme-{ user.Theme }\" x-data=\"bookshelf\" x-init=\"initBookshelf()\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = Header(user, "/bookshelf").Render(ctx, templ_7745c5c3_Buffer)
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);\"><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\" name=\"library_id\" 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 {
return templ_7745c5c3_Err
}
if len(libraries) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<option value=\"\">No libraries available</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
if currentLibraryID == "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<option value=\"\" selected>All Books (")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(TotalMediaCount(libraries))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 57, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, ")</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<option value=\"\">All Books (")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(TotalMediaCount(libraries))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 59, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, ")</option> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
for _, lib := range libraries {
if lib.ID == currentLibraryID {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 63, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" selected>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 63, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " (")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 63, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, ")</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<option value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 65, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 65, Col: 46}
}
_, 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, 14, " (")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(lib.MediaCount)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 65, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, ")</option>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</select></div><!-- Search Input --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Search</label> <input name=\"q\" type=\"text\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" 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] relative\"><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);\" @input.debounce.300ms=\"if($el.value.length >= 2) fetchTagValues($el)\" @keydown=\"onTagFilterKeydown($event)\" @blur=\"hideTagDropdown()\"><div x-show=\"showTagDropdown\" x-transition class=\"absolute z-50 mt-1 w-full rounded-lg shadow-lg border max-h-48 overflow-y-auto\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"><template x-for=\"sug in tagSuggestions\" :key=\"sug.value\"><button type=\"button\" class=\"w-full text-left px-3 py-2 text-sm flex justify-between items-center\" :class=\"tagHighlightIndex === tagSuggestions.indexOf(sug) ? 'opacity-80' : ''\" :style=\"tagHighlightIndex === tagSuggestions.indexOf(sug) ? 'background-color: var(--bg-secondary); color: var(--text-primary);' : 'color: var(--text-primary);'\" @click=\"selectTagSuggestion(sug.value)\"><span x-text=\"sug.value\"></span> <span class=\"text-xs\" style=\"color: var(--text-secondary);\" x-text=\"sug.count + ' books'\"></span></button></template></div></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 - TriState Button --><div class=\"flex-1 min-w-[150px]\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Cover Filter</label> <input type=\"button\" id=\"has-cover-tristate\" class=\"tristate-btn w-full transition-all duration-300 cursor-pointer flex items-center justify-center gap-2 px-4 py-2 rounded-lg border text-sm\" :class=\"{\n 'state-null': hasCoverState === null,\n 'state-true': hasCoverState === true,\n 'state-false': hasCoverState === false\n }\" :value=\"hasCoverState === null ? '○ Cover: Any' : hasCoverState === true ? '✓ Has Cover' : '✗ No Cover'\" @click=\"cycleHasCover()\"><!-- Hidden input for HTMX form submission - conditionally rendered --><template x-if=\"hasCoverState !== null\"><input type=\"hidden\" name=\"has_cover\" :value=\"hasCoverState ? 'true' : 'false'\"></template></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()\" data-load-filter-btn 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=\"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=\"absolute top-full mt-2 w-80 rounded-lg shadow-lg z-50\" :class=\"dropdownAlign === 'left' ? 'left-0' : 'right-0'\" style=\"background-color: var(--bg-secondary); border: 1px solid var(--border); display: none;\"><div class=\"p-4\"><h3 class=\"text-sm font-semibold mb-3\" style=\"color: var(--text-primary)\">Saved Filters</h3><!-- Filter List --><div class=\"space-y-2\" id=\"saved-filters-list\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, filter := range savedFilters {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<div class=\"flex items-center justify-between p-2 rounded hover:opacity-80\" style=\"background-color: var(--bg-primary);\" data-filter-id=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue(uuidToString(filter.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 301, Col: 173}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"><button data-action=\"load-filter\" @click=\"loadFilter($event)\" class=\"flex-1 text-left px-2 py-1 rounded\" style=\"color: var(--text-primary);\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(filter.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 303, Col: 27}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</button> <button data-action=\"delete-filter\" @click=\"deleteFilter($event)\" class=\"p-1 hover:opacity-70 rounded\" style=\"color: var(--text-secondary);\" title=\"Delete filter\">🗑️</button></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "</div><!-- Empty State -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(savedFilters) == 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<div class=\"text-sm py-4 text-center\" style=\"color: var(--text-secondary);\">No saved filters yet</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</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 {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = BooksGrid(books, limit, offset, count, currentLibraryID).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "<!-- Error Message -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if errorMessage != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<div class=\"mt-6 p-4 rounded-lg border bg-red-500/10 border-red-500\"><p style=\"color: var(--text-primary)\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(errorMessage)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/bookshelf.templ`, Line: 337, Col: 59}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><!-- Pagination --><div id=\"pagination\" class=\"mt-6 flex justify-center gap-2\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if count > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<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
}
if offset <= 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, ">← Previous</button> <span class=\"px-4 py-2\" style=\"color: var(--text-secondary);\">Page ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, 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: 356, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</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
}
if offset+limit >= count {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, " disabled")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ">Next →</button>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</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 hx-post=\"/api/saved-filters\" hx-target=\"#saved-filters-list\" hx-swap=\"beforeend\" hx-include=\"#filter-form\" @htmx:afterRequest=\"if(event.detail.xhr.status < 400) { afterFilterSave() }\"><div class=\"mb-4\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Filter Name</label> <input type=\"text\" name=\"filter_name\" 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 id=\"filter-save-error\" style=\"display: none; color: #ef4444; padding: 0.75rem; border-radius: 0.5rem; margin-bottom: 1rem;\"></div><input type=\"hidden\" name=\"resource_type\" value=\"media-items\"></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 {
return templ_7745c5c3_Err
}
return nil
})
}
var _ = templruntime.GeneratedTemplate