feat: enhance book detail star rating display with visual half-stars

Improve star rating display on book detail page to show always-visible
5-star rating scale with theme-aware colors and visual half-star rendering.

Changes:

Enhanced renderStars() function:
- Always displays 5 stars (0/5 now shows 5 grey stars instead of empty)
- Filled stars use var(--accent) color (theme-aware highlight)
- Empty stars use var(--text-secondary) (theme-aware grey, adapts to light/dark themes)
- Half-stars use CSS linear-gradient (90deg) to split star vertically:
  - Left half: var(--accent) (filled, color)
  - Right half: var(--text-secondary) (empty, grey)
- Uses webkit-background-clip and text-fill-color transparent for gradient effect

Added getBookRating() helper function:
- Returns rating value or 0 if book.Rating is nil
- Allows unrated books to display 0/5 (5 grey stars)
- Makes rating section always visible instead of hiding when nil

Template changes:
- Updated rating display to always show (no conditionals)
- Removed text-yellow-400 class (colors now inline with theme vars)
- Added templ.Raw() wrapper for HTML rendering (prevents escaping)
- Simplified rating display logic

Benefits:
- Users can now see rating scale even when book isn't rated
- Visual half-star is much more intuitive than ½ text character
- Theme-aware colors adapt to light/dark mode automatically
- Follows existing patterns ( UnsafeHTML, CSS variables, etc.)

This makes the rating section more discoverable and user-friendly.
This commit is contained in:
2026-03-29 00:26:50 -04:00
parent a157c546fd
commit e89ed4dbc6
3 changed files with 246 additions and 243 deletions
+8 -10
View File
@@ -93,16 +93,14 @@ templ BookDetail(user User, book handlers.MediaDetail, errorMessage string) {
</button>
</div>
<!-- Rating Display -->
if book.Rating != nil {
<div class="mb-6">
<span class="text-yellow-400 text-2xl">
{ renderStars(book.Rating.Rating) }
</span>
<span class="ml-2 text-sm" style="color: var(--text-secondary);">
({ fmt.Sprintf("%.1f", float64(book.Rating.Rating)/2.0) } / 5)
</span>
</div>
}
<div class="mb-6">
<span class="text-2xl">
@templ.Raw(renderStars(getBookRating(book.Rating)))
</span>
<span class="ml-2 text-sm" style="color: var(--text-secondary);">
({ fmt.Sprintf("%.1f", float64(getBookRating(book.Rating))/2.0) } / 5)
</span>
</div>
<!-- Series Badge -->
if book.Series.Valid && book.Series.String != "" {
<div class="mb-4">
+210 -225
View File
@@ -166,90 +166,75 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</button><!-- Edit Metadata (placeholder) --><button @click=\"showMetadataEditorPlaceholder()\" class=\"px-6 py-3 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary); background-color: var(--bg-secondary);\">✏️ Edit Metadata</button></div><!-- Rating Display -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "</button><!-- Edit Metadata (placeholder) --><button @click=\"showMetadataEditorPlaceholder()\" class=\"px-6 py-3 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary); background-color: var(--bg-secondary);\">✏️ Edit Metadata</button></div><!-- Rating Display --><div class=\"mb-6\"><span class=\"text-2xl\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.Rating != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<div class=\"mb-6\"><span class=\"text-yellow-400 text-2xl\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(renderStars(book.Rating.Rating))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 99, Col: 42}
}
_, 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, 19, "</span> <span class=\"ml-2 text-sm\" style=\"color: var(--text-secondary);\">(")
templ_7745c5c3_Err = templ.Raw(renderStars(getBookRating(book.Rating))).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</span> <span class=\"ml-2 text-sm\" style=\"color: var(--text-secondary);\">(")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", float64(getBookRating(book.Rating))/2.0))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 101, Col: 71}
}
_, 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, 19, " / 5)</span></div><!-- Series Badge -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.Series.Valid && book.Series.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<div class=\"mb-4\"><span class=\"px-3 py-1 rounded-full text-sm font-semibold\" style=\"background-color: var(--accent); color: white;\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", float64(book.Rating.Rating)/2.0))
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(book.Series.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 102, Col: 64}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 111, Col: 29}
}
_, 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, 20, " / 5)</span></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<!-- Series Badge -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.Series.Valid && book.Series.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "<div class=\"mb-4\"><span class=\"px-3 py-1 rounded-full text-sm font-semibold\" style=\"background-color: var(--accent); color: white;\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(book.Series.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 113, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, " ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.SeriesNumber.Valid && book.SeriesNumber.Int32 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "#")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "#")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(book.SeriesNumber.Int32)
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(book.SeriesNumber.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 115, Col: 36}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 113, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</span></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</span></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<!-- Description/Synopsis -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<!-- Description/Synopsis -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.Description.Valid && book.Description.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<div class=\"mb-6\"><h3 class=\"font-semibold mb-2\" style=\"color: var(--text-primary)\">Synopsis</h3><div class=\"max-h-[20rem] overflow-y-auto pr-2\" style=\"color: var(--text-secondary)\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"mb-6\"><h3 class=\"font-semibold mb-2\" style=\"color: var(--text-primary)\">Synopsis</h3><div class=\"max-h-[20rem] overflow-y-auto pr-2\" style=\"color: var(--text-secondary)\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -259,65 +244,84 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</div></div><!-- Progress Section -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</div></div><!-- Progress Section -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.ReadingProgress != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Reading Progress</h3>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Reading Progress</h3>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.ActiveConflict != nil {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<div class=\"mb-4 p-3 rounded-lg border\" style=\"background-color: #f59e0b20; border-color: #f59e0b;\"><p style=\"color: var(--text-primary);\">⚠️ Progress conflict detected - Click \"Sync Progress\" to review and resolve</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<div class=\"mb-4 p-3 rounded-lg border\" style=\"background-color: #f59e0b20; border-color: #f59e0b;\"><p style=\"color: var(--text-primary);\">⚠️ Progress conflict detected - Click \"Sync Progress\" to review and resolve</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<!-- Progress Bar --><div class=\"mb-4\"><div class=\"w-full rounded-full h-3\" style=\"background-color: var(--bg-primary);\"><div class=\"h-3 rounded-full transition-all\" style=\"width: { book.ReadingProgress.Percentage.Float64 }%; background-color: var(--accent);\"></div></div></div><!-- Progress Stats Grid --><div class=\"grid grid-cols-1 md:grid-cols-4 gap-4\"><div><p style=\"color: var(--text-secondary)\">Progress</p><p class=\"text-2xl font-bold\" style=\"color: var(--accent);\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "<!-- Progress Bar --><div class=\"mb-4\"><div class=\"w-full rounded-full h-3\" style=\"background-color: var(--bg-primary);\"><div class=\"h-3 rounded-full transition-all\" style=\"width: { book.ReadingProgress.Percentage.Float64 }%; background-color: var(--accent);\"></div></div></div><!-- Progress Stats Grid --><div class=\"grid grid-cols-1 md:grid-cols-4 gap-4\"><div><p style=\"color: var(--text-secondary)\">Progress</p><p class=\"text-2xl font-bold\" style=\"color: var(--accent);\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", book.ReadingProgress.Percentage.Float64))
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", book.ReadingProgress.Percentage.Float64))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 164, Col: 71}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 162, Col: 71}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
_, 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, 33, "%</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "%</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.ReadingProgress.CurrentPage.Valid && book.ReadingProgress.TotalPages.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<div><p style=\"color: var(--text-secondary)\">Page</p><p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div><p style=\"color: var(--text-secondary)\">Page</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.CurrentPage.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 168, Col: 52}
}
_, 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, 33, " / ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.CurrentPage.Int32)
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.TotalPages.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 170, Col: 52}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 168, Col: 96}
}
_, 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, 35, " / ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if book.ReadingProgress.LastReadAt.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<div><p style=\"color: var(--text-secondary)\">Last Read</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.TotalPages.Int32)
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastReadAt.Time.Format("2006-01-02 15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 170, Col: 96}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 174, Col: 77}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
@@ -328,15 +332,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.ReadingProgress.LastReadAt.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><p style=\"color: var(--text-secondary)\">Last Read</p><p>")
if book.ReadingProgress.LastSyncSource.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div><p style=\"color: var(--text-secondary)\">Source</p><p class=\"capitalize\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastReadAt.Time.Format("2006-01-02 15:04"))
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastSyncSource.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 176, Col: 77}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 180, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
@@ -347,43 +351,43 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.ReadingProgress.LastSyncSource.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div><p style=\"color: var(--text-secondary)\">Source</p><p class=\"capitalize\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(book.ReadingProgress.LastSyncSource.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 182, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "<!-- Metadata Grid --><div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Metadata</h3><div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\"><!-- Publication Info -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<!-- Metadata Grid --><div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Metadata</h3><div class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\"><!-- Publication Info -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.Publisher.Valid && book.Publisher.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<div><p style=\"color: var(--text-secondary)\">Publisher</p><p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<div><p style=\"color: var(--text-secondary)\">Publisher</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(book.Publisher.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 197, Col: 34}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if book.DatePublished.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<div><p style=\"color: var(--text-secondary)\">Published</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(book.Publisher.String)
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(book.DatePublished.Time.Format("2006-01-02"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 199, Col: 34}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 203, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
@@ -394,15 +398,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.DatePublished.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div><p style=\"color: var(--text-secondary)\">Published</p><p>")
if book.Isbn.Valid && book.Isbn.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div><p style=\"color: var(--text-secondary)\">Isbn</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(book.DatePublished.Time.Format("2006-01-02"))
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(book.Isbn.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 205, Col: 57}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 209, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
@@ -413,15 +417,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.Isbn.Valid && book.Isbn.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div><p style=\"color: var(--text-secondary)\">Isbn</p><p>")
if book.Language.Valid && book.Language.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div><p style=\"color: var(--text-secondary)\">Language</p><p class=\"capitalize\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(book.Isbn.String)
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(book.Language.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 211, Col: 29}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 215, Col: 52}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
if templ_7745c5c3_Err != nil {
@@ -432,15 +436,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.Language.Valid && book.Language.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div><p style=\"color: var(--text-secondary)\">Language</p><p class=\"capitalize\">")
if book.Edition.Valid && book.Edition.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<div><p style=\"color: var(--text-secondary)\">Edition</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(book.Language.String)
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(book.Edition.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 217, Col: 52}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 221, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
@@ -451,15 +455,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.Edition.Valid && book.Edition.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div><p style=\"color: var(--text-secondary)\">Edition</p><p>")
if book.PageCount.Valid && book.PageCount.Int32 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<div><p style=\"color: var(--text-secondary)\">Pages</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(book.Edition.String)
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(book.PageCount.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 223, Col: 32}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 227, Col: 33}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {
@@ -470,15 +474,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.PageCount.Valid && book.PageCount.Int32 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<div><p style=\"color: var(--text-secondary)\">Pages</p><p>")
if book.Genre.Valid && book.Genre.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<div><p style=\"color: var(--text-secondary)\">Genre</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var22 string
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(book.PageCount.Int32)
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(book.Genre.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 229, Col: 33}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 233, Col: 30}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
if templ_7745c5c3_Err != nil {
@@ -489,15 +493,15 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.Genre.Valid && book.Genre.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<div><p style=\"color: var(--text-secondary)\">Genre</p><p>")
if book.CopyrightYear.Valid && book.CopyrightYear.Int32 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "<div><p style=\"color: var(--text-secondary)\">Copyright Year</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(book.Genre.String)
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(book.CopyrightYear.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 235, Col: 30}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 239, Col: 37}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
@@ -508,79 +512,78 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
return templ_7745c5c3_Err
}
}
if book.CopyrightYear.Valid && book.CopyrightYear.Int32 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<div><p style=\"color: var(--text-secondary)\">Copyright Year</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(book.CopyrightYear.Int32)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 241, Col: 37}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<!-- Technical Info --><div><p style=\"color: var(--text-secondary)\">Format</p><p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "<!-- Technical Info --><div><p style=\"color: var(--text-secondary)\">Format</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(book.MimeType.String)
var templ_7745c5c3_Var24 string
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(book.MimeType.String)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 247, Col: 32}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 245, Col: 32}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.FileSize.Valid && book.FileSize.Int64 > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<div><p style=\"color: var(--text-secondary)\">File Size</p><p>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<div><p style=\"color: var(--text-secondary)\">File Size</p><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(formatFileSize(book.FileSize.Int64))
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(formatFileSize(book.FileSize.Int64))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 252, Col: 48}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 250, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</p></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</p></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</div><!-- External IDs Section -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "</div><!-- External IDs Section -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.GoodreadsID.Valid || book.OpenlibraryID.Valid || book.GoogleBooksID.Valid || book.Asin.Valid || book.Isbn.Valid {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<div class=\"mt-4 pt-4 border-t\" style=\"border-color: var(--border);\"><h4 class=\"text-sm font-semibold mb-3\" style=\"color: var(--text-primary)\">External Links</h4><div class=\"flex flex-wrap gap-3\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "<div class=\"mt-4 pt-4 border-t\" style=\"border-color: var(--border);\"><h4 class=\"text-sm font-semibold mb-3\" style=\"color: var(--text-primary)\">External Links</h4><div class=\"flex flex-wrap gap-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if book.GoodreadsID.Valid && book.GoodreadsID.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var26 templ.SafeURL
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("goodreads", book.GoodreadsID.String, book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 261, Col: 105}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">📚 Goodreads</a> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var27 templ.SafeURL
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("goodreads", book.GoodreadsID.String, book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("goodreads", "", book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 263, Col: 105}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 271, Col: 84}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil {
@@ -590,34 +593,34 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
}
if book.OpenlibraryID.Valid && book.OpenlibraryID.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var28 templ.SafeURL
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("goodreads", "", book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("openlibrary", book.OpenlibraryID.String, book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 273, Col: 84}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 282, Col: 109}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">📚 Goodreads</a> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">📖 Open Library</a> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if book.OpenlibraryID.Valid && book.OpenlibraryID.String != "" {
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var29 templ.SafeURL
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("openlibrary", book.OpenlibraryID.String, book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("openlibrary", "", book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 284, Col: 109}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 292, Col: 86}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
if templ_7745c5c3_Err != nil {
@@ -627,34 +630,34 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
}
if book.GoogleBooksID.Valid && book.GoogleBooksID.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var30 templ.SafeURL
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("openlibrary", "", book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("googlebooks", book.GoogleBooksID.String, book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 294, Col: 86}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 303, Col: 109}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">📖 Open Library</a> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">🔍 Google Books</a> ")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if book.GoogleBooksID.Valid && book.GoogleBooksID.String != "" {
} else {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var31 templ.SafeURL
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("googlebooks", book.GoogleBooksID.String, book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("googlebooks", "", book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 305, Col: 109}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 313, Col: 86}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
if templ_7745c5c3_Err != nil {
@@ -664,34 +667,34 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else {
}
if book.Asin.Valid && book.Asin.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var32 templ.SafeURL
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("googlebooks", "", book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("amazon", book.Asin.String, book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 315, Col: 86}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 324, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">🔍 Google Books</a> ")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">🛒 Amazon</a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
if book.Asin.Valid && book.Asin.String != "" {
} else if book.Isbn.Valid && book.Isbn.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var33 templ.SafeURL
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("amazon", book.Asin.String, book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("amazon", "", book.Isbn, book.Title, book.Author))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 326, Col: 95}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 334, Col: 81}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
if templ_7745c5c3_Err != nil {
@@ -701,90 +704,72 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
} else if book.Isbn.Valid && book.Isbn.String != "" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<a href=\"")
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "</div><!-- Collections Section -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(book.Collections) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "<div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Collections</h3><div class=\"flex flex-wrap gap-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, col := range book.Collections {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "<a href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var34 templ.SafeURL
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs(getExternalURL("amazon", "", book.Isbn, book.Title, book.Author))
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinURLErrs("/collections/" + uuidToString(col.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 336, Col: 81}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 357, Col: 54}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-sm hover:underline flex items-center gap-1\" style=\"color: var(--accent);\">🛒 Amazon</a>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\" class=\"px-3 py-2 rounded-lg border flex items-center gap-2 hover:opacity-80 transition-opacity\" style=\"border-color: { col.Color.String }; background-color: var(--bg-primary); text-decoration: none;\"><span style=\"color: { col.Color.String };\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</div><!-- Collections Section -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(book.Collections) > 0 {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<div class=\"card p-6 rounded-lg border mb-6\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><h3 class=\"font-semibold mb-4\" style=\"color: var(--text-primary)\">Collections</h3><div class=\"flex flex-wrap gap-3\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, col := range book.Collections {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "<a href=\"")
var templ_7745c5c3_Var35 string
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(col.Icon.String)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var35 templ.SafeURL
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinURLErrs("/collections/" + uuidToString(col.ID))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 359, Col: 54}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 361, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "\" class=\"px-3 py-2 rounded-lg border flex items-center gap-2 hover:opacity-80 transition-opacity\" style=\"border-color: { col.Color.String }; background-color: var(--bg-primary); text-decoration: none;\"><span style=\"color: { col.Color.String };\">")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</span> <span style=\"color: var(--text-primary);\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var36 string
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(col.Icon.String)
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 363, Col: 69}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 362, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</span> <span style=\"color: var(--text-primary);\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var37 string
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 364, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</span></a>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</span></a>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</div></div>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</div><!-- Modals -->")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</div><!-- Modals -->")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -800,7 +785,7 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</body></html>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</body></html>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+28 -8
View File
@@ -1,7 +1,9 @@
package templates
import (
"bookhoard/internal/database"
"fmt"
"strings"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
@@ -36,20 +38,30 @@ func uuidToString(id pgtype.UUID) string {
}
// renderStars converts rating (1-10 scale) to star display
// Rating scale: 1-10 where odd numbers = half stars (1=0.5★, 3=1.5★, etc.)
// Always displays 5 stars total with filled stars in yellow
// and empty stars in grey (using theme variable)
// Rating scale: 1-10 where each 2 points = 1 full star, odd numbers = half stars
func renderStars(rating int32) string {
stars := ""
const totalStars = 5
fullStars := rating / 2
hasHalf := rating%2 != 0
for i := int32(0); i < fullStars; i++ {
stars += "★"
}
if hasHalf {
stars += "½"
var stars strings.Builder
for i := int32(0); i < totalStars; i++ {
if i < fullStars {
// Filled star - accent color (theme-aware highlight)
stars.WriteString(`<span style="color: var(--accent);">★</span>`)
} else if i == fullStars && hasHalf {
// Half star - gradient from accent (left) to grey (right)
stars.WriteString(`<span style="background: linear-gradient(90deg, var(--accent) 50%, var(--text-secondary) 50%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">★</span>`)
} else {
// Empty star - grey using theme variable
stars.WriteString(`<span style="color: var(--text-secondary);">★</span>`)
}
}
return stars
return stars.String()
}
// formatFileSize converts bytes to human-readable format
@@ -122,3 +134,11 @@ func getExternalURL(service string, id string, isbn pgtype.Text, title string, a
return baseURL + searchTerm
}
// getBookRating returns the rating value or 0 if not rated
func getBookRating(rating *database.MediaRatings) int32 {
if rating != nil {
return rating.Rating
}
return 0
}