feat: add template helper functions for book detail page
Add utility functions for rendering book metadata: - renderStars(): Convert rating (1-10 scale) to star display - formatFileSize(): Convert bytes to human-readable format (KB, MB, GB) - getExternalURL(): Generate URLs for external book services with smart fallback: ID → ISBN → Title+Author search Supports Goodreads, Open Library, Google Books, Amazon These helpers make book detail template cleaner and follow DRY principle.
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
@@ -32,3 +34,91 @@ func uuidToString(id pgtype.UUID) string {
|
|||||||
}
|
}
|
||||||
return u.String()
|
return u.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.)
|
||||||
|
func renderStars(rating int32) string {
|
||||||
|
stars := ""
|
||||||
|
fullStars := rating / 2
|
||||||
|
hasHalf := rating%2 != 0
|
||||||
|
|
||||||
|
for i := int32(0); i < fullStars; i++ {
|
||||||
|
stars += "★"
|
||||||
|
}
|
||||||
|
if hasHalf {
|
||||||
|
stars += "½"
|
||||||
|
}
|
||||||
|
|
||||||
|
return stars
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatFileSize converts bytes to human-readable format
|
||||||
|
func formatFileSize(bytes int64) string {
|
||||||
|
const (
|
||||||
|
KB = 1024
|
||||||
|
MB = KB * 1024
|
||||||
|
GB = MB * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case bytes >= GB:
|
||||||
|
return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
|
||||||
|
case bytes >= MB:
|
||||||
|
return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB))
|
||||||
|
case bytes >= KB:
|
||||||
|
return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB))
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("%d B", bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getExternalURL generates URL for external book services
|
||||||
|
// Priority: ID > ISBN > Title+Author search
|
||||||
|
func getExternalURL(service string, id string, isbn pgtype.Text, title string, author pgtype.Text) string {
|
||||||
|
baseURL := ""
|
||||||
|
searchTerm := ""
|
||||||
|
|
||||||
|
// Determine search term: ID > ISBN > Title+Author
|
||||||
|
if id != "" {
|
||||||
|
searchTerm = id
|
||||||
|
} else if isbn.Valid && isbn.String != "" {
|
||||||
|
searchTerm = isbn.String
|
||||||
|
} else {
|
||||||
|
// Build title+author search query
|
||||||
|
if author.Valid && author.String != "" {
|
||||||
|
searchTerm = fmt.Sprintf("%s %s", title, author.String)
|
||||||
|
} else {
|
||||||
|
searchTerm = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build URL based on service
|
||||||
|
switch service {
|
||||||
|
case "goodreads":
|
||||||
|
if id != "" {
|
||||||
|
baseURL = "https://www.goodreads.com/book/show/"
|
||||||
|
} else {
|
||||||
|
baseURL = "https://www.goodreads.com/search?q="
|
||||||
|
}
|
||||||
|
case "openlibrary":
|
||||||
|
if id != "" {
|
||||||
|
baseURL = "https://openlibrary.org/books/"
|
||||||
|
} else {
|
||||||
|
baseURL = "https://openlibrary.org/search?q="
|
||||||
|
}
|
||||||
|
case "googlebooks":
|
||||||
|
if id != "" {
|
||||||
|
baseURL = "https://books.google.com/books?id="
|
||||||
|
} else {
|
||||||
|
baseURL = "https://www.google.com/search?tbm=bks&q="
|
||||||
|
}
|
||||||
|
case "amazon":
|
||||||
|
// Amazon doesn't have direct book IDs, always search
|
||||||
|
baseURL = "https://www.amazon.com/s?k="
|
||||||
|
if isbn.Valid && isbn.String != "" {
|
||||||
|
searchTerm = isbn.String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseURL + searchTerm
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user