feat: Add pagination logic and controls for videos
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
CircularProgress,
|
||||
Container,
|
||||
Grid,
|
||||
Pagination,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
@@ -29,6 +30,8 @@ const CollectionPage: React.FC<CollectionPageProps> = ({ collections, videos, on
|
||||
const [collectionVideos, setCollectionVideos] = useState<Video[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState<boolean>(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const ITEMS_PER_PAGE = 12;
|
||||
|
||||
useEffect(() => {
|
||||
if (collections && collections.length > 0 && id) {
|
||||
@@ -50,8 +53,21 @@ const CollectionPage: React.FC<CollectionPageProps> = ({ collections, videos, on
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
setLoading(false);
|
||||
}, [id, collections, videos, navigate]);
|
||||
|
||||
// Pagination logic
|
||||
const totalPages = Math.ceil(collectionVideos.length / ITEMS_PER_PAGE);
|
||||
const displayedVideos = collectionVideos.slice(
|
||||
(page - 1) * ITEMS_PER_PAGE,
|
||||
page * ITEMS_PER_PAGE
|
||||
);
|
||||
|
||||
const handlePageChange = (event: React.ChangeEvent<unknown>, value: number) => {
|
||||
setPage(value);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
@@ -123,19 +139,35 @@ const CollectionPage: React.FC<CollectionPageProps> = ({ collections, videos, on
|
||||
{collectionVideos.length === 0 ? (
|
||||
<Alert severity="info" variant="outlined">No videos in this collection.</Alert>
|
||||
) : (
|
||||
<Grid container spacing={3}>
|
||||
{collectionVideos.map(video => (
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 3 }} key={video.id}>
|
||||
<VideoCard
|
||||
video={video}
|
||||
collections={collections}
|
||||
onDeleteVideo={onDeleteVideo}
|
||||
showDeleteButton={true}
|
||||
disableCollectionGrouping={true}
|
||||
<Box>
|
||||
<Grid container spacing={3}>
|
||||
{displayedVideos.map(video => (
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4, lg: 3 }} key={video.id}>
|
||||
<VideoCard
|
||||
video={video}
|
||||
collections={collections}
|
||||
onDeleteVideo={onDeleteVideo}
|
||||
showDeleteButton={true}
|
||||
disableCollectionGrouping={true}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'center' }}>
|
||||
<Pagination
|
||||
count={totalPages}
|
||||
page={page}
|
||||
onChange={handlePageChange}
|
||||
color="primary"
|
||||
size="large"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<DeleteCollectionModal
|
||||
|
||||
@@ -11,8 +11,10 @@ import {
|
||||
CircularProgress,
|
||||
Container,
|
||||
Grid,
|
||||
Pagination,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import AuthorsList from '../components/AuthorsList';
|
||||
import Collections from '../components/Collections';
|
||||
@@ -59,6 +61,13 @@ const Home: React.FC<HomeProps> = ({
|
||||
onDownload,
|
||||
onResetSearch
|
||||
}) => {
|
||||
const [page, setPage] = useState(1);
|
||||
const ITEMS_PER_PAGE = 12;
|
||||
|
||||
// Reset page when filters change (though currently no filters other than search which is separate)
|
||||
useEffect(() => {
|
||||
setPage(1);
|
||||
}, [videos, collections]);
|
||||
|
||||
|
||||
// Add default empty array to ensure videos is always an array
|
||||
@@ -101,6 +110,20 @@ const Home: React.FC<HomeProps> = ({
|
||||
});
|
||||
});
|
||||
|
||||
// Pagination logic
|
||||
const totalPages = Math.ceil(filteredVideos.length / ITEMS_PER_PAGE);
|
||||
const displayedVideos = filteredVideos.slice(
|
||||
(page - 1) * ITEMS_PER_PAGE,
|
||||
page * ITEMS_PER_PAGE
|
||||
);
|
||||
|
||||
const handlePageChange = (event: React.ChangeEvent<unknown>, value: number) => {
|
||||
setPage(value);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Helper function to format duration in seconds to MM:SS
|
||||
const formatDuration = (seconds?: number) => {
|
||||
if (!seconds) return '';
|
||||
@@ -270,7 +293,7 @@ const Home: React.FC<HomeProps> = ({
|
||||
{/* Videos grid */}
|
||||
<Grid size={{ xs: 12, md: 9 }}>
|
||||
<Grid container spacing={3}>
|
||||
{filteredVideos.map(video => (
|
||||
{displayedVideos.map(video => (
|
||||
<Grid size={{ xs: 12, sm: 6, lg: 4, xl: 3 }} key={video.id}>
|
||||
<VideoCard
|
||||
video={video}
|
||||
@@ -279,6 +302,20 @@ const Home: React.FC<HomeProps> = ({
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
{totalPages > 1 && (
|
||||
<Box sx={{ mt: 4, display: 'flex', justifyContent: 'center' }}>
|
||||
<Pagination
|
||||
count={totalPages}
|
||||
page={page}
|
||||
onChange={handlePageChange}
|
||||
color="primary"
|
||||
size="large"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Container,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
Pagination,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -47,11 +48,38 @@ const ManagePage: React.FC<ManagePageProps> = ({ videos, onDeleteVideo, collecti
|
||||
const [videoToDelete, setVideoToDelete] = useState<string | null>(null);
|
||||
const [showVideoDeleteModal, setShowVideoDeleteModal] = useState<boolean>(false);
|
||||
|
||||
// Pagination state
|
||||
const [collectionPage, setCollectionPage] = useState(1);
|
||||
const [videoPage, setVideoPage] = useState(1);
|
||||
const ITEMS_PER_PAGE = 10;
|
||||
|
||||
const filteredVideos = videos.filter(video =>
|
||||
video.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
video.author.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
// Pagination logic
|
||||
const totalCollectionPages = Math.ceil(collections.length / ITEMS_PER_PAGE);
|
||||
const displayedCollections = collections.slice(
|
||||
(collectionPage - 1) * ITEMS_PER_PAGE,
|
||||
collectionPage * ITEMS_PER_PAGE
|
||||
);
|
||||
|
||||
const totalVideoPages = Math.ceil(filteredVideos.length / ITEMS_PER_PAGE);
|
||||
const displayedVideos = filteredVideos.slice(
|
||||
(videoPage - 1) * ITEMS_PER_PAGE,
|
||||
videoPage * ITEMS_PER_PAGE
|
||||
);
|
||||
|
||||
const handleCollectionPageChange = (event: React.ChangeEvent<unknown>, value: number) => {
|
||||
setCollectionPage(value);
|
||||
};
|
||||
|
||||
const handleVideoPageChange = (event: React.ChangeEvent<unknown>, value: number) => {
|
||||
setVideoPage(value);
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const confirmDeleteVideo = async () => {
|
||||
if (!videoToDelete) return;
|
||||
|
||||
@@ -150,7 +178,7 @@ const ManagePage: React.FC<ManagePageProps> = ({ videos, onDeleteVideo, collecti
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{collections.map(collection => (
|
||||
{displayedCollections.map(collection => (
|
||||
<TableRow key={collection.id} hover>
|
||||
<TableCell component="th" scope="row" sx={{ fontWeight: 500 }}>
|
||||
{collection.name}
|
||||
@@ -176,6 +204,19 @@ const ManagePage: React.FC<ManagePageProps> = ({ videos, onDeleteVideo, collecti
|
||||
) : (
|
||||
<Alert severity="info" variant="outlined">No collections found.</Alert>
|
||||
)}
|
||||
|
||||
{totalCollectionPages > 1 && (
|
||||
<Box sx={{ mt: 2, display: 'flex', justifyContent: 'center' }}>
|
||||
<Pagination
|
||||
count={totalCollectionPages}
|
||||
page={collectionPage}
|
||||
onChange={handleCollectionPageChange}
|
||||
color="secondary"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
@@ -212,7 +253,7 @@ const ManagePage: React.FC<ManagePageProps> = ({ videos, onDeleteVideo, collecti
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{filteredVideos.map(video => (
|
||||
{displayedVideos.map(video => (
|
||||
<TableRow key={video.id} hover>
|
||||
<TableCell sx={{ width: 140 }}>
|
||||
<Box
|
||||
@@ -246,6 +287,19 @@ const ManagePage: React.FC<ManagePageProps> = ({ videos, onDeleteVideo, collecti
|
||||
<Alert severity="info" variant="outlined">No videos found matching your search.</Alert>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{totalVideoPages > 1 && (
|
||||
<Box sx={{ mt: 2, mb: 4, display: 'flex', justifyContent: 'center' }}>
|
||||
<Pagination
|
||||
count={totalVideoPages}
|
||||
page={videoPage}
|
||||
onChange={handleVideoPageChange}
|
||||
color="primary"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user