feat(ssr): pass library data to collection page templates

Update frontend route handlers for /collections and /collections/:id
to fetch user-visible libraries and pass libData + currentLibraryID
to templates, enabling the library switcher dropdown.

/collections handler:
- Fetch GetUserVisibleLibraries for the current user
- Derive currentLibraryID from query param, falling back to first library
- Convert to []templates.LibraryData and pass to Collection template

/collections/:id handler:
- Fetch GetUserVisibleLibraries alongside existing book fetching
- Pass libData to CollectionDetail template alongside existing libraryID
- Refactored to use shared libraryID variable across system/user paths
This commit is contained in:
2026-05-17 21:12:13 -04:00
parent 0e8b11504e
commit 9b31dc6f68
+55 -29
View File
@@ -585,8 +585,32 @@ func registerFrontendRoutes(cfg *Config) {
} }
} }
userUUID, _ := uuid.Parse(user.ID)
libraries, libErr := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
if libErr != nil {
log.Printf("GetUserVisibleLibraries failed: %v", libErr)
libraries = []database.GetUserVisibleLibrariesRow{}
}
currentLibraryID := c.QueryParam("library_id")
if currentLibraryID == "" && len(libraries) > 0 {
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
currentLibraryID = libUUID.String()
}
libData := make([]templates.LibraryData, len(libraries))
for i, lib := range libraries {
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
libData[i] = templates.LibraryData{
ID: libUUID.String(),
Name: lib.Name,
Description: getText(lib.Description),
TypeName: lib.TypeName,
}
}
var buf bytes.Buffer var buf bytes.Buffer
err = templates.Collection(user, colData, errorMsg).Render(c.Request().Context(), &buf) err = templates.Collection(user, colData, libData, currentLibraryID, errorMsg).Render(c.Request().Context(), &buf)
if err != nil { if err != nil {
return err return err
} }
@@ -674,14 +698,12 @@ func registerFrontendRoutes(cfg *Config) {
userUUID, _ := uuid.Parse(user.ID) userUUID, _ := uuid.Parse(user.ID)
var books []handlers.BookInfo var books []handlers.BookInfo
if collection.QueryType.Valid && collection.QueryType.String != "" {
// System collection - use query type
// System collection - need library_id for system collections
// Get library_id from query param or default to user's first library
libraryID := c.QueryParam("library_id") libraryID := c.QueryParam("library_id")
if collection.QueryType.Valid && collection.QueryType.String != "" {
if libraryID == "" { if libraryID == "" {
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID)) libraries, libErr := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
if err == nil && len(libraries) > 0 { if libErr == nil && len(libraries) > 0 {
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16]) libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
libraryID = libUUID.String() libraryID = libUUID.String()
} }
@@ -689,15 +711,13 @@ func registerFrontendRoutes(cfg *Config) {
libUUID, _ := uuid.Parse(libraryID) libUUID, _ := uuid.Parse(libraryID)
dashboardSvc := services.NewDashboardService(cfg.Queries) dashboardSvc := services.NewDashboardService(cfg.Queries)
sections, err := dashboardSvc.GetDashboardSections(c.Request().Context(), userUUID, libUUID, 1000, []string{}, []string{}) sections, secErr := dashboardSvc.GetDashboardSections(c.Request().Context(), userUUID, libUUID, 1000, []string{}, []string{})
if err != nil { if secErr != nil {
return renderErrorPage(c, "Error loading books", "books_load_error") return renderErrorPage(c, "Error loading books", "books_load_error")
} }
// Find the matching section and convert items
for _, section := range sections { for _, section := range sections {
if section.CollectionID.String() == collectionID { if section.CollectionID.String() == collectionID {
// Convert []database.MediaItems to []handlers.BookInfo
bookCards := make([]handlers.BookInfo, len(section.Items)) bookCards := make([]handlers.BookInfo, len(section.Items))
for i, item := range section.Items { for i, item := range section.Items {
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16]) itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
@@ -713,27 +733,21 @@ func registerFrontendRoutes(cfg *Config) {
} }
} }
} else { } else {
// User collection - check if library_id filter is present
libraryID := c.QueryParam("library_id")
if libraryID != "" { if libraryID != "" {
// Filter by library - reuse dashboard query libUUID, parseErr := uuid.Parse(libraryID)
libUUID, err := uuid.Parse(libraryID) if parseErr != nil {
if err != nil {
return renderErrorPage(c, "Invalid library ID", "invalid_library_id") return renderErrorPage(c, "Invalid library ID", "invalid_library_id")
} }
// Use GetCollectionItemsForDashboard for library-filtered results collItems, collErr := cfg.Queries.GetCollectionItemsForDashboard(c.Request().Context(),
collItems, err := cfg.Queries.GetCollectionItemsForDashboard(c.Request().Context(),
database.GetCollectionItemsForDashboardParams{ database.GetCollectionItemsForDashboardParams{
CollectionID: pgtype.UUID{Bytes: collUUID, Valid: true}, CollectionID: pgtype.UUID{Bytes: collUUID, Valid: true},
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true}, LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
Limit: 1000, Limit: 1000,
}) })
if err != nil { if collErr != nil {
books = []handlers.BookInfo{} books = []handlers.BookInfo{}
} else { } else {
// Convert to BookInfo format (non-excluded only)
var validItems []database.GetCollectionItemsForDashboardRow var validItems []database.GetCollectionItemsForDashboardRow
for _, item := range collItems { for _, item := range collItems {
if !item.Excluded.Valid || !item.Excluded.Bool { if !item.Excluded.Valid || !item.Excluded.Bool {
@@ -754,13 +768,11 @@ func registerFrontendRoutes(cfg *Config) {
books = bookCards books = bookCards
} }
} else { } else {
// No library filter - show all books in collection collItems, collErr := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
collItems, err := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true}) if collErr != nil {
if err != nil {
books = []handlers.BookInfo{} books = []handlers.BookInfo{}
} }
// Convert to BookInfo format
bookCards := make([]handlers.BookInfo, len(collItems)) bookCards := make([]handlers.BookInfo, len(collItems))
for i, item := range collItems { for i, item := range collItems {
itemUUID, _ := uuid.FromBytes(item.MediaItemID.Bytes[0:16]) itemUUID, _ := uuid.FromBytes(item.MediaItemID.Bytes[0:16])
@@ -783,11 +795,25 @@ func registerFrontendRoutes(cfg *Config) {
Icon: collection.Icon.String, Icon: collection.Icon.String,
} }
// Get library_id from query params for template libraries, libErr := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
libraryID := c.QueryParam("library_id") if libErr != nil {
// Render the CollectionDetail template log.Printf("GetUserVisibleLibraries failed: %v", libErr)
libraries = []database.GetUserVisibleLibrariesRow{}
}
libData := make([]templates.LibraryData, len(libraries))
for i, lib := range libraries {
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
libData[i] = templates.LibraryData{
ID: libUUID.String(),
Name: lib.Name,
Description: getText(lib.Description),
TypeName: lib.TypeName,
}
}
var buf bytes.Buffer var buf bytes.Buffer
err = templates.CollectionDetail(user, colData, books, libraryID).Render(c.Request().Context(), &buf) err = templates.CollectionDetail(user, colData, books, libraryID, libData).Render(c.Request().Context(), &buf)
if err != nil { if err != nil {
return err return err
} }