"use client" import { useState, useEffect } from "react" import Link from "next/link" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, SheetDescription } from "@/components/ui/sheet" import { Button } from "@/components/ui/button" import { List, Loader2, ChevronLeft, ChevronRight } from "lucide-react" interface ReaderTOCProps { novelId: string novelSlug: string currentChapterNumber: number } interface TOCChapter { id: string number: number title: string volumeNumber?: number | null volumeTitle?: string | null volumeChapterNumber?: number | null } export function ReaderTOC({ novelId, novelSlug, currentChapterNumber }: ReaderTOCProps) { const [isOpen, setIsOpen] = useState(false) const [chapters, setChapters] = useState([]) const [loading, setLoading] = useState(false) const [currentPage, setCurrentPage] = useState(1) const [totalPages, setTotalPages] = useState(1) // Each page will fetch 100 chapters to make scrolling efficient const ITEMS_PER_PAGE = 100 // Calculate the initial page where the current chapter belongs useEffect(() => { const initialPage = Math.ceil(currentChapterNumber / ITEMS_PER_PAGE) setCurrentPage(initialPage || 1) }, [currentChapterNumber]) // Fetch chapters when page changes and TOC is open useEffect(() => { if (!isOpen) return const fetchChapters = async () => { setLoading(true) try { const res = await fetch(`/api/truyen/${novelId}/chapters?page=${currentPage}&limit=${ITEMS_PER_PAGE}`) if (res.ok) { const data = await res.json() setChapters(data.chapters) setTotalPages(data.totalPages) } } catch (error) { console.error("Failed to load chapters for TOC", error) } finally { setLoading(false) } } fetchChapters() }, [isOpen, currentPage, novelId]) // Optional: Auto-scroll to the current chapter on initial load useEffect(() => { if (!loading && isOpen && chapters.length > 0) { setTimeout(() => { const activeItem = document.getElementById(`toc-chap-${currentChapterNumber}`) if (activeItem) { activeItem.scrollIntoView({ behavior: 'smooth', block: 'center' }) } }, 100) } }, [loading, isOpen, chapters, currentChapterNumber]) return ( Mục lục chương Danh sách mục lục chương được liệt kê theo danh sách để điều hướng thuận tiện
{loading ? (
) : ( (() => { let lastVolumeKey: string | null = null return chapters.map((chap) => { const isActive = chap.number === currentChapterNumber const volumeKey = chap.volumeNumber || chap.volumeTitle ? `${chap.volumeNumber ?? "no-num"}-${chap.volumeTitle ?? "no-title"}` : null const showVolumeHeader = volumeKey !== null && volumeKey !== lastVolumeKey if (volumeKey !== null) { lastVolumeKey = volumeKey } return (
{showVolumeHeader && (
{chap.volumeTitle || `Quyển ${chap.volumeNumber}`}
)} setIsOpen(false)} className={`block px-3 py-2 text-sm rounded-md transition-colors ${ isActive ? 'bg-primary text-primary-foreground font-medium' : 'hover:bg-muted text-foreground/80' }`} > {chap.volumeChapterNumber || chap.number}. {chap.title}
) }) })() )}
{/* Pagination Details */} {totalPages > 1 && (
Trang {currentPage} / {totalPages}
)}
) }