refactor(templates): generalize SeriesDetail into reusable BrowseDetail
Replace the single-purpose SeriesDetail template with a parameterized BrowseDetail component that accepts badge icon/label, title, page title, back URL/label, empty state icon/message, and book list. Both series detail and new tag detail pages use the same template with different params, eliminating duplication. Series detail: 📚 Series, back to /series, "All Series" Tag detail: 🏷️ Tag, back to /bookshelf, "Bookshelf" Deleted series_detail.templ and series_detail_templ.go. Updated frontend.go series route to call BrowseDetail with series params. Added /tags/detail route calling BrowseDetail with tag params.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bookhoard/internal/handlers"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
templ BrowseDetail(user User, badgeIcon string, badgeLabel string, title string, pageTitle string, backUrl string, backLabel string, emptyIcon string, emptyMessage string, books []handlers.BookInfo, errorMessage string) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>{ pageTitle } - Bookhoard</title>
|
||||
<link href="/static/style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body class="theme-{ user.Theme }">
|
||||
@Header(user, backUrl)
|
||||
<div class="sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b" style="background-color: var(--bg-primary);">
|
||||
<div class="w-full px-4 py-3 flex items-center gap-4">
|
||||
<a href={ backUrl } class="text-sm hover:opacity-80 transition-opacity" style="color: var(--text-secondary); text-decoration: none;">
|
||||
← { backLabel }
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full px-4 py-8">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<span class="px-3 py-1 rounded-full text-sm font-semibold" style="background-color: var(--accent); color: white;">
|
||||
{ badgeIcon } { badgeLabel }
|
||||
</span>
|
||||
<h1 class="text-3xl font-bold" style="color: var(--text-primary)">{ title }</h1>
|
||||
<span class="text-sm" style="color: var(--text-secondary)">{ fmt.Sprintf("%d", len(books)) } books</span>
|
||||
</div>
|
||||
if len(books) > 0 {
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-4">
|
||||
for _, book := range books {
|
||||
@BookCard(book)
|
||||
}
|
||||
</div>
|
||||
} else {
|
||||
<div class="text-center py-16" style="color: var(--text-secondary)">
|
||||
<div class="text-6xl mb-4">{ emptyIcon }</div>
|
||||
<h3 class="text-xl font-semibold mb-2" style="color: var(--text-primary)">No Books Found</h3>
|
||||
<p>{ emptyMessage }</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@ErrorToast(errorMessage)
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
Reference in New Issue
Block a user