feat(templates): add BooksGrid component for reusable book grid rendering
Create new BooksGrid templ component that renders a grid of books with pagination. This component can be reused across multiple pages and returns HTML for HTMX updates. - Add books_grid.templ with BooksGrid component - Renders book cards using existing BookCard component - Includes pagination controls with HTMX attributes - Accepts books list, pagination params, and library ID - Generated books_grid_templ.go from templ compiler
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
|
templ BooksGrid(books []handlers.BookInfo, limit int, offset int, count int, currentLibraryID string) {
|
||||||
|
<div id="books-grid" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4">
|
||||||
|
if len(books) > 0 {
|
||||||
|
for _, book := range books {
|
||||||
|
@BookCard(book)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
<!-- Empty state -->
|
||||||
|
<div class="col-span-full text-center py-12" style="color: var(--text-secondary);">
|
||||||
|
<p class="text-lg mb-2">📚 No books found</p>
|
||||||
|
<p class="text-sm">Try adjusting your filters or add some books to your library.</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<!-- Pagination -->
|
||||||
|
<div id="pagination" class="mt-6 flex justify-center gap-2">
|
||||||
|
if count > 0 {
|
||||||
|
<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"
|
||||||
|
disabled?={ offset <= 0 }
|
||||||
|
>
|
||||||
|
← Previous
|
||||||
|
</button>
|
||||||
|
<span class="px-4 py-2" style="color: var(--text-secondary);">
|
||||||
|
Page { offset / limit + 1 }
|
||||||
|
</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"
|
||||||
|
disabled?={ offset+limit >= count }
|
||||||
|
>
|
||||||
|
Next →
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.1001
|
||||||
|
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/handlers"
|
||||||
|
|
||||||
|
func BooksGrid(books []handlers.BookInfo, limit int, offset int, count int, currentLibraryID string) 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, "<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
|
||||||
|
}
|
||||||
|
if len(books) > 0 {
|
||||||
|
for _, book := range books {
|
||||||
|
templ_7745c5c3_Err = BookCard(book).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!-- Empty state --> <div class=\"col-span-full text-center py-12\" style=\"color: var(--text-secondary);\"><p class=\"text-lg mb-2\">📚 No books found</p><p class=\"text-sm\">Try adjusting your filters or add some books to your library.</p></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</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, 4, "<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, 5, " disabled")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, ">← 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_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(offset/limit + 1)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/books_grid.templ`, Line: 34, Col: 29}
|
||||||
|
}
|
||||||
|
_, 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, 7, "</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, 8, " disabled")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, ">Next →</button>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
Reference in New Issue
Block a user