import Link from "next/link" import { Search } from "lucide-react" import { Input } from "@/components/ui/input" import { NovelCard } from "@/components/novel-card" import { readerApiFetch } from "@/lib/server-api" export const dynamic = "force-dynamic" const PAGE_SIZE = 20 type BrowseNovel = { id: string slug: string title: string authorName: string coverColor: string | null coverUrl: string | null rating: number views: number totalChapters: number status: string seriesId?: string | null } type BrowseResponse = { items: BrowseNovel[] totalCount: number totalPages: number currentPage: number } type GenreItem = { id: string name: string slug: string } function collapseSeriesRows(rows: T[]): T[] { const pickedSeries = new Set() const output: T[] = [] for (const row of rows) { if (!row.seriesId) { output.push(row) continue } if (pickedSeries.has(row.seriesId)) continue pickedSeries.add(row.seriesId) output.push(row) } return output } function buildBrowsePath(params: Record) { const search = new URLSearchParams(params) return `/api/novels/browse?${search.toString()}` } export default async function SearchPage({ searchParams, }: { searchParams: Promise<{ [key: string]: string | undefined }> }) { const resolvedParams = await searchParams const q = resolvedParams.q || "" const sortBy = resolvedParams.sort || "latest" const genreFilter = resolvedParams.genreFilter || "all" const statusFilter = resolvedParams.statusFilter || "all" const requestedPage = Math.max(1, Number(resolvedParams.page || "1") || 1) const genres = await readerApiFetch("/api/genres") let filteredNovels: BrowseNovel[] = [] let totalResults = 0 let totalPages = 1 let currentPage = requestedPage if (q) { const browse = await readerApiFetch( buildBrowsePath({ q, sort: sortBy, genre: genreFilter === "all" ? "" : genreFilter, status: statusFilter === "all" ? "" : statusFilter, page: String(requestedPage), limit: String(PAGE_SIZE), }) ) filteredNovels = browse.items totalResults = browse.totalCount totalPages = Math.max(1, browse.totalPages || 1) currentPage = Math.min(requestedPage, totalPages) } else { const browse = await readerApiFetch( buildBrowsePath({ sort: sortBy, genre: genreFilter === "all" ? "" : genreFilter, status: statusFilter === "all" ? "" : statusFilter, page: String(requestedPage), limit: String(PAGE_SIZE), collapse_series: "true", }) ) filteredNovels = browse.items totalResults = browse.totalCount totalPages = Math.max(1, browse.totalPages || 1) currentPage = Math.min(requestedPage, totalPages) } const pageRangeStart = Math.max(1, currentPage - 2) const pageRangeEnd = Math.min(totalPages, currentPage + 2) const pageNumbers = Array.from( { length: pageRangeEnd - pageRangeStart + 1 }, (_, index) => pageRangeStart + index ) const buildPageHref = (page: number) => { const params = new URLSearchParams() if (q) params.set("q", q) if (sortBy !== "latest") params.set("sort", sortBy) if (genreFilter !== "all") params.set("genreFilter", genreFilter) if (statusFilter !== "all") params.set("statusFilter", statusFilter) params.set("page", String(page)) return `/tim-kiem?${params.toString()}` } return (

Tìm Kiếm Truyện

{totalResults} kết quả {totalResults > 0 && `(Trang ${currentPage}/${totalPages})`}

{filteredNovels.length === 0 ? (

Không tìm thấy truyện

Thử tìm kiếm với từ khóa khác hoặc thay đổi bộ lọc.

) : (
{filteredNovels.map((novel) => ( ))}
)} {totalPages > 1 && (
Trước {pageNumbers.map((page) => ( {page} ))} = totalPages ? "pointer-events-none opacity-50" : "hover:bg-muted"}`} > Sau
)}
) }