Spaces:
Sleeping
Sleeping
File size: 4,329 Bytes
4bea261 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | import { useState, useRef } from 'react';
import MovieCard from './MovieCard';
import Pagination from './Pagination';
export default function PaginatedGrid({ initialData, category, title, query, topic, country, type }) {
const [data, setData] = useState(initialData);
const [loading, setLoading] = useState(false);
const containerRef = useRef(null);
const moviesList = data?.items || [];
const currentPage = data?.pagination?.currentPage || 1;
const totalPages = data?.pagination?.totalPages || 1;
// Xây dựng đường dẫn cơ sở basePath động chuẩn chỉnh cho chuyển trang
let basePath = '';
if (typeof window !== 'undefined') {
const url = new URL(window.location.href);
url.searchParams.delete('page');
// Giữ nguyên các tham số tìm kiếm khác (như q) nếu có
basePath = url.pathname + url.search;
} else {
// Giải pháp dự phòng khi Render trên máy chủ (SSR Fallback)
if (query) {
basePath = `/tim-kiem?q=${encodeURIComponent(query)}`;
} else if (topic) {
basePath = `/chu-de/${topic}`;
} else {
basePath = category ? `/the-loai/${category}` : '';
}
}
const handlePageChange = async (pageNum) => {
if (pageNum === currentPage || loading) return;
setLoading(true);
// Cuộn mượt mà lên đầu danh sách/bảng phim với offset hợp lý
if (containerRef.current) {
containerRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
try {
let apiUrl = `/api/movies?page=${pageNum}`;
if (category) apiUrl += `&category=${encodeURIComponent(category)}`;
if (topic) apiUrl += `&topic=${encodeURIComponent(topic)}`;
if (country) apiUrl += `&country=${encodeURIComponent(country)}`;
if (query) apiUrl += `&q=${encodeURIComponent(query)}`;
if (type) apiUrl += `&type=${encodeURIComponent(type)}`;
const res = await fetch(apiUrl);
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
const json = await res.json();
// Giãn cách một chút (300ms) để hiệu ứng chuyển trang trông premium, mượt mà và trực quan
await new Promise((resolve) => setTimeout(resolve, 300));
setData(json);
// Cập nhật địa chỉ URL trình duyệt mà không gây tải lại toàn bộ trang
const newUrl = new URL(window.location.href);
newUrl.searchParams.set('page', String(pageNum));
window.history.pushState({}, '', newUrl.toString());
} catch (err) {
console.error('Lỗi khi tải trang mới:', err);
} finally {
setLoading(false);
}
};
return (
<div ref={containerRef} className="space-y-12 relative scroll-mt-28">
{/* Hiệu ứng phủ kính Glassmorphism Loading Siêu Sang Trọng */}
{loading && (
<div className="absolute inset-0 z-50 bg-black/40 backdrop-blur-[2px] rounded-3xl flex items-center justify-center transition-all duration-300 animate-fade-in">
<div className="flex flex-col items-center gap-3 bg-[#12141d]/85 border border-white/10 rounded-2xl p-6 shadow-2xl">
<div className="h-10 w-10 border-4 border-primary/20 border-t-primary rounded-full animate-spin"></div>
<span className="text-xs font-black uppercase tracking-widest text-primary animate-pulse">Đang cập nhật...</span>
</div>
</div>
)}
{moviesList.length === 0 ? (
<div className="py-20 text-center text-gray-500 font-bold uppercase tracking-widest text-xs">
Không tìm thấy phim nào
</div>
) : (
<>
{/* Grid hiển thị danh sách phim hiện tại */}
<div className={`grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 transition-all duration-300 ${loading ? 'opacity-30 blur-[1px]' : 'opacity-100'}`}>
{moviesList.map((movie) => (
<MovieCard key={movie._id || movie.slug} movie={movie} />
))}
</div>
{/* Phân trang tiêu chuẩn cao cấp bằng AJAX */}
<Pagination
currentPage={currentPage}
totalPages={totalPages}
basePath={basePath}
onPageChange={handlePageChange}
/>
</>
)}
</div>
);
}
|