feat(collections): Add library-aware filtering to collection detail pages
- Add library_id parameter to BuildSections and getViewAllURL functions - Update dashboard handler to pass libraryID when building sections - Add library_id query param support to collection detail page handler - When library_id is provided, filter collection items by that library - When no library_id, show all books (backward compatible) - Reuses GetCollectionItemsForDashboard query for filtered results - Preserves context when navigating from dashboard to collection detail
This commit is contained in:
@@ -61,7 +61,7 @@ func (h *DashboardHandler) GetSections(c echo.Context) error {
|
|||||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to load dashboard sections"})
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to load dashboard sections"})
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionData := BuildSections(sections)
|
sectionData := BuildSections(sections, libraryID)
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, map[string]interface{}{"sections": sectionData})
|
return c.JSON(http.StatusOK, map[string]interface{}{"sections": sectionData})
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ func (h *DashboardHandler) RestoreSystemCollection(c echo.Context) error {
|
|||||||
return c.JSON(http.StatusOK, map[string]string{"message": "System collection restored to defaults"})
|
return c.JSON(http.StatusOK, map[string]string{"message": "System collection restored to defaults"})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildSections(sections []services.DashboardSection) []SectionData {
|
func BuildSections(sections []services.DashboardSection, currentLibraryID string) []SectionData {
|
||||||
var result []SectionData
|
var result []SectionData
|
||||||
|
|
||||||
for _, ds := range sections {
|
for _, ds := range sections {
|
||||||
@@ -177,7 +177,7 @@ func BuildSections(sections []services.DashboardSection) []SectionData {
|
|||||||
Description: ds.Description,
|
Description: ds.Description,
|
||||||
Icon: ds.Icon,
|
Icon: ds.Icon,
|
||||||
Items: bookCards,
|
Items: bookCards,
|
||||||
ViewAllURL: getViewAllURL(ds.CollectionID.String()),
|
ViewAllURL: getViewAllURL(ds.CollectionID.String(), currentLibraryID),
|
||||||
Priority: ds.Priority,
|
Priority: ds.Priority,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -185,8 +185,11 @@ func BuildSections(sections []services.DashboardSection) []SectionData {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getViewAllURL(collectionID string) string {
|
func getViewAllURL(collectionID string, libraryID string) string {
|
||||||
if collectionID != "" {
|
if collectionID != "" {
|
||||||
|
if libraryID != "" {
|
||||||
|
return "/collections/" + collectionID + "?library_id=" + libraryID
|
||||||
|
}
|
||||||
return "/collections/" + collectionID
|
return "/collections/" + collectionID
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
+59
-17
@@ -191,8 +191,8 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sectionData := handlers.BuildSections(visibleSections)
|
sectionData := handlers.BuildSections(visibleSections, libraryID)
|
||||||
allSectionsData := handlers.BuildSections(allSections)
|
allSectionsData := handlers.BuildSections(allSections, libraryID)
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = templates.Dashboard(user, sectionData, allSectionsData, libData, libraryID, prefs.HiddenCollections, limit, errorMsg).Render(c.Request().Context(), &buf)
|
err = templates.Dashboard(user, sectionData, allSectionsData, libData, libraryID, prefs.HiddenCollections, limit, errorMsg).Render(c.Request().Context(), &buf)
|
||||||
@@ -358,24 +358,66 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// User collection - fetch collection items
|
// User collection - check if library_id filter is present
|
||||||
collItems, err := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
libraryID := c.QueryParam("library_id")
|
||||||
if err != nil {
|
|
||||||
books = []handlers.BookInfo{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to BookInfo format
|
if libraryID != "" {
|
||||||
bookCards := make([]handlers.BookInfo, len(collItems))
|
// Filter by library - reuse dashboard query
|
||||||
for i, item := range collItems {
|
libUUID, err := uuid.Parse(libraryID)
|
||||||
itemUUID, _ := uuid.FromBytes(item.MediaItemID.Bytes[0:16])
|
if err != nil {
|
||||||
bookCards[i] = handlers.BookInfo{
|
return renderErrorPage(c, "Invalid library ID", "invalid_library_id")
|
||||||
MediaItemID: itemUUID.String(),
|
|
||||||
Title: item.Title,
|
|
||||||
Author: getText(item.Author),
|
|
||||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use GetCollectionItemsForDashboard for library-filtered results
|
||||||
|
collItems, err := cfg.Queries.GetCollectionItemsForDashboard(c.Request().Context(),
|
||||||
|
database.GetCollectionItemsForDashboardParams{
|
||||||
|
CollectionID: pgtype.UUID{Bytes: collUUID, Valid: true},
|
||||||
|
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
||||||
|
Limit: 1000,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
books = []handlers.BookInfo{}
|
||||||
|
} else {
|
||||||
|
// Convert to BookInfo format (non-excluded only)
|
||||||
|
var validItems []database.GetCollectionItemsForDashboardRow
|
||||||
|
for _, item := range collItems {
|
||||||
|
if !item.Excluded.Valid || !item.Excluded.Bool {
|
||||||
|
validItems = append(validItems, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bookCards := make([]handlers.BookInfo, len(validItems))
|
||||||
|
for i, item := range validItems {
|
||||||
|
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||||
|
bookCards[i] = handlers.BookInfo{
|
||||||
|
MediaItemID: itemUUID.String(),
|
||||||
|
Title: item.Title,
|
||||||
|
Author: getText(item.Author),
|
||||||
|
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
books = bookCards
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No library filter - show all books in collection
|
||||||
|
collItems, err := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
||||||
|
if err != nil {
|
||||||
|
books = []handlers.BookInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to BookInfo format
|
||||||
|
bookCards := make([]handlers.BookInfo, len(collItems))
|
||||||
|
for i, item := range collItems {
|
||||||
|
itemUUID, _ := uuid.FromBytes(item.MediaItemID.Bytes[0:16])
|
||||||
|
bookCards[i] = handlers.BookInfo{
|
||||||
|
MediaItemID: itemUUID.String(),
|
||||||
|
Title: item.Title,
|
||||||
|
Author: getText(item.Author),
|
||||||
|
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
books = bookCards
|
||||||
}
|
}
|
||||||
books = bookCards
|
|
||||||
}
|
}
|
||||||
// Build collection data
|
// Build collection data
|
||||||
colData := templates.CollectionData{
|
colData := templates.CollectionData{
|
||||||
|
|||||||
Reference in New Issue
Block a user