What's Happening
The book library (app/dashboard/library/page.jsx) fetches every book in one shot (fetchBooks() → GET /api/books in lib/actions/library/fetch-books.js) and renders the whole list in a grid. There is no way to search, filter, sort, or paginate within the library:
- The only controls on the page are "Create Book" and a bookmarks toggle.
- Books have a
category field (creators type one free-form in book-create-form.jsx, and BookDetailPageClient.jsx renders it as a badge) — but the library offers no category filtering.
- Prices exist (free vs paid USDC) — no "Free"/"Paid" filter, no sort by price/rating/date.
- The global
Searchbox in nav-header.jsx routes to /dashboard/search/[searchparam], a separate cross-entity results page — useful, but browsing within the library still means scrolling an unbounded grid, and the client-side render cost grows linearly with catalog size.
As the catalog grows this page becomes both slow (fetch-all + render-all) and unusable for discovery — a core flow for an Islamic book library.
Where to Find This
app/dashboard/library/page.jsx — page state (books, showBookmarks, loading, error)
lib/actions/library/fetch-books.js — the fetch-all action
components/molecules/dashboard/cards/libraryCard.jsx — the card grid item
components/atoms/skeletons/LibraryBookSkeleton.jsx — existing loading skeletons
- Reference for UI primitives:
components/ui/select.jsx, components/ui/pagination.jsx (already in the repo, unused here)
What We Want
- A toolbar on the library page with: a debounced text search (title/author), a category select (populated from the fetched data or a backend categories endpoint if available), a price filter (All / Free / Paid), and a sort control (Newest, Price, Rating).
- Pagination (or "Load more" infinite scroll — pick one) instead of rendering the full catalog; reuse
components/ui/pagination.jsx.
- Prefer server-side querying: extend
fetch-books.js to pass ?search=&category=&page=&limit=&sort= query params and use the backend's response shape if it supports them. If the backend doesn't yet, implement the filtering client-side behind the same UI and note the needed backend params in the PR (so the API work can be a follow-up) — the UI contract should not change either way.
- Reflect filters in the URL query string (
useSearchParams/router.replace) so filtered views are shareable and survive refresh.
- Keep existing behavior: bookmarks toggle applies on top of filters, own-books exclusion (
book?.author?._id !== user?._id) stays, skeletons during loading, NetworkErrorComp with retry on failure, and a filter-aware empty state ("No books match your filters" with a clear-filters button).
Technical Context
- The page is a client component with plain
useState — this can stay; just mind effect dependencies when filters change (debounce search ~300ms to avoid a request per keystroke).
- Category is free-form text today, so normalize for the dropdown (trim/case-fold) and expect duplicates like "Aqeedah"/"aqeedah".
- The same pattern applies to
/dashboard/courses — keep components (toolbar, hooks) reusable, but courses are out of scope for this issue.
Acceptance Criteria
What's Happening
The book library (
app/dashboard/library/page.jsx) fetches every book in one shot (fetchBooks()→GET /api/booksinlib/actions/library/fetch-books.js) and renders the whole list in a grid. There is no way to search, filter, sort, or paginate within the library:categoryfield (creators type one free-form inbook-create-form.jsx, andBookDetailPageClient.jsxrenders it as a badge) — but the library offers no category filtering.Searchboxinnav-header.jsxroutes to/dashboard/search/[searchparam], a separate cross-entity results page — useful, but browsing within the library still means scrolling an unbounded grid, and the client-side render cost grows linearly with catalog size.As the catalog grows this page becomes both slow (fetch-all + render-all) and unusable for discovery — a core flow for an Islamic book library.
Where to Find This
app/dashboard/library/page.jsx— page state (books,showBookmarks,loading,error)lib/actions/library/fetch-books.js— the fetch-all actioncomponents/molecules/dashboard/cards/libraryCard.jsx— the card grid itemcomponents/atoms/skeletons/LibraryBookSkeleton.jsx— existing loading skeletonscomponents/ui/select.jsx,components/ui/pagination.jsx(already in the repo, unused here)What We Want
components/ui/pagination.jsx.fetch-books.jsto pass?search=&category=&page=&limit=&sort=query params and use the backend's response shape if it supports them. If the backend doesn't yet, implement the filtering client-side behind the same UI and note the needed backend params in the PR (so the API work can be a follow-up) — the UI contract should not change either way.useSearchParams/router.replace) so filtered views are shareable and survive refresh.book?.author?._id !== user?._id) stays, skeletons during loading,NetworkErrorCompwith retry on failure, and a filter-aware empty state ("No books match your filters" with a clear-filters button).Technical Context
useState— this can stay; just mind effect dependencies when filters change (debounce search ~300ms to avoid a request per keystroke)./dashboard/courses— keep components (toolbar, hooks) reusable, but courses are out of scope for this issue.Acceptance Criteria
npm run lintandnpm run buildpass.