feat: add sort parameter (title/latest) to books and series endpoints
Add sort=latest option to GET /books and GET /series API endpoints, and expose a Sort select in the backoffice books and series pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ export default async function BooksPage({
|
||||
const libraryId = typeof searchParamsAwaited.library === "string" ? searchParamsAwaited.library : undefined;
|
||||
const searchQuery = typeof searchParamsAwaited.q === "string" ? searchParamsAwaited.q : "";
|
||||
const readingStatus = typeof searchParamsAwaited.status === "string" ? searchParamsAwaited.status : undefined;
|
||||
const sort = typeof searchParamsAwaited.sort === "string" ? searchParamsAwaited.sort : undefined;
|
||||
const page = typeof searchParamsAwaited.page === "string" ? parseInt(searchParamsAwaited.page) : 1;
|
||||
const limit = typeof searchParamsAwaited.limit === "string" ? parseInt(searchParamsAwaited.limit) : 20;
|
||||
|
||||
@@ -54,7 +55,7 @@ export default async function BooksPage({
|
||||
totalHits = searchResponse.estimated_total_hits;
|
||||
}
|
||||
} else {
|
||||
const booksPage = await fetchBooks(libraryId, undefined, page, limit, readingStatus).catch(() => ({
|
||||
const booksPage = await fetchBooks(libraryId, undefined, page, limit, readingStatus, sort).catch(() => ({
|
||||
items: [] as BookDto[],
|
||||
total: 0,
|
||||
page: 1,
|
||||
@@ -83,7 +84,12 @@ export default async function BooksPage({
|
||||
{ value: "read", label: "Read" },
|
||||
];
|
||||
|
||||
const hasFilters = searchQuery || libraryId || readingStatus;
|
||||
const sortOptions = [
|
||||
{ value: "", label: "Title" },
|
||||
{ value: "latest", label: "Latest added" },
|
||||
];
|
||||
|
||||
const hasFilters = searchQuery || libraryId || readingStatus || sort;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -104,6 +110,7 @@ export default async function BooksPage({
|
||||
{ name: "q", type: "text", label: "Search", placeholder: "Search by title, author, series...", className: "flex-1 w-full" },
|
||||
{ name: "library", type: "select", label: "Library", options: libraryOptions, className: "w-full sm:w-48" },
|
||||
{ name: "status", type: "select", label: "Status", options: statusOptions, className: "w-full sm:w-40" },
|
||||
{ name: "sort", type: "select", label: "Sort", options: sortOptions, className: "w-full sm:w-40" },
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
@@ -16,19 +16,25 @@ export default async function SeriesPage({
|
||||
const libraryId = typeof searchParamsAwaited.library === "string" ? searchParamsAwaited.library : undefined;
|
||||
const searchQuery = typeof searchParamsAwaited.q === "string" ? searchParamsAwaited.q : "";
|
||||
const readingStatus = typeof searchParamsAwaited.status === "string" ? searchParamsAwaited.status : undefined;
|
||||
const sort = typeof searchParamsAwaited.sort === "string" ? searchParamsAwaited.sort : undefined;
|
||||
const page = typeof searchParamsAwaited.page === "string" ? parseInt(searchParamsAwaited.page) : 1;
|
||||
const limit = typeof searchParamsAwaited.limit === "string" ? parseInt(searchParamsAwaited.limit) : 20;
|
||||
|
||||
const [libraries, seriesPage] = await Promise.all([
|
||||
fetchLibraries().catch(() => [] as LibraryDto[]),
|
||||
fetchAllSeries(libraryId, searchQuery || undefined, readingStatus, page, limit).catch(
|
||||
fetchAllSeries(libraryId, searchQuery || undefined, readingStatus, page, limit, sort).catch(
|
||||
() => ({ items: [] as SeriesDto[], total: 0, page: 1, limit }) as SeriesPageDto
|
||||
),
|
||||
]);
|
||||
|
||||
const series = seriesPage.items;
|
||||
const totalPages = Math.ceil(seriesPage.total / limit);
|
||||
const hasFilters = searchQuery || libraryId || readingStatus;
|
||||
const sortOptions = [
|
||||
{ value: "", label: "Title" },
|
||||
{ value: "latest", label: "Latest added" },
|
||||
];
|
||||
|
||||
const hasFilters = searchQuery || libraryId || readingStatus || sort;
|
||||
|
||||
const libraryOptions = [
|
||||
{ value: "", label: "All libraries" },
|
||||
@@ -61,6 +67,7 @@ export default async function SeriesPage({
|
||||
{ name: "q", type: "text", label: "Search", placeholder: "Search by series name...", className: "flex-1 w-full" },
|
||||
{ name: "library", type: "select", label: "Library", options: libraryOptions, className: "w-full sm:w-48" },
|
||||
{ name: "status", type: "select", label: "Status", options: statusOptions, className: "w-full sm:w-40" },
|
||||
{ name: "sort", type: "select", label: "Sort", options: sortOptions, className: "w-full sm:w-40" },
|
||||
]}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
@@ -265,11 +265,13 @@ export async function fetchBooks(
|
||||
page: number = 1,
|
||||
limit: number = 50,
|
||||
readingStatus?: string,
|
||||
sort?: string,
|
||||
): Promise<BooksPageDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (libraryId) params.set("library_id", libraryId);
|
||||
if (series) params.set("series", series);
|
||||
if (readingStatus) params.set("reading_status", readingStatus);
|
||||
if (sort) params.set("sort", sort);
|
||||
params.set("page", page.toString());
|
||||
params.set("limit", limit.toString());
|
||||
|
||||
@@ -303,11 +305,13 @@ export async function fetchAllSeries(
|
||||
readingStatus?: string,
|
||||
page: number = 1,
|
||||
limit: number = 50,
|
||||
sort?: string,
|
||||
): Promise<SeriesPageDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (libraryId) params.set("library_id", libraryId);
|
||||
if (q) params.set("q", q);
|
||||
if (readingStatus) params.set("reading_status", readingStatus);
|
||||
if (sort) params.set("sort", sort);
|
||||
params.set("page", page.toString());
|
||||
params.set("limit", limit.toString());
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user