Spaces:
Running
Running
import { useEffect, useState } from 'react'; | |
import HeroSection from '../components/HeroSection'; | |
import ContentRow from '../components/ContentRow'; | |
import { getRecentItems, getGenreCategories, getGenresItems, getMovieCard, getTvShowCard } from '../lib/api'; | |
import { useToast } from '@/hooks/use-toast'; | |
// GenreRow component for dynamic loading of a genre row | |
const GenreRow = ({ genre, type }) => { | |
const [loading, setLoading] = useState(true); | |
const [items, setItems] = useState([]); | |
const { toast } = useToast(); | |
useEffect(() => { | |
const fetchGenreData = async () => { | |
try { | |
// Fetch titles for the given genre and type | |
const genreItems = await getGenresItems([genre], type, 10, 1); | |
const titles = | |
type === "movie" | |
? (genreItems && Array.isArray(genreItems.movies) | |
? genreItems.movies.map(item => item.title) | |
: []) | |
: (genreItems && Array.isArray(genreItems.series) | |
? genreItems.series.map(item => item.title) | |
: []); | |
if (titles.length === 0) { | |
setLoading(false); | |
return; | |
} | |
// For each title, fetch the card details | |
const fetchCard = async (title) => { | |
try { | |
if (type === "movie") { | |
const movieInfo = await getMovieCard(title); | |
if (movieInfo) { | |
return { | |
type: 'movie', | |
title, | |
image: movieInfo.image, | |
description: movieInfo.overview, | |
genre: movieInfo.genres?.map((g) => g.name) || [], | |
year: movieInfo.year | |
}; | |
} | |
} else { | |
const showInfo = await getTvShowCard(title); | |
if (showInfo) { | |
return { | |
type: 'tvshow', | |
title, | |
image: showInfo.image, | |
description: showInfo.overview, | |
genre: showInfo.genres?.map((g) => g.name) || [], | |
year: showInfo.year | |
}; | |
} | |
} | |
} catch (error) { | |
console.error(`Error fetching card for ${title}:`, error); | |
return null; | |
} | |
return null; | |
}; | |
const cardPromises = titles.map((title) => fetchCard(title)); | |
const cards = await Promise.all(cardPromises); | |
setItems(cards.filter(item => item !== null)); | |
} catch (error) { | |
console.error(`Error fetching ${type} items for genre ${genre}:`, error); | |
toast({ | |
title: `Error loading ${type} items`, | |
description: `Failed to load ${genre} ${type} items`, | |
variant: "destructive" | |
}); | |
} finally { | |
setLoading(false); | |
} | |
}; | |
fetchGenreData(); | |
}, [genre, type, toast]); | |
// While data is being fetched, show a simple loader in place of the row | |
if (loading) { | |
return ( | |
<div className="px-4 md:px-8 py-8"> | |
<h2 className="text-xl font-bold mb-4">{genre} {type === "movie" ? "Movies" : "Shows"}</h2> | |
<div className="flex items-center justify-center h-32"> | |
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-netflix-red"></div> | |
</div> | |
</div> | |
); | |
} | |
// If no items were found, render nothing | |
if (items.length === 0) return null; | |
return ( | |
<ContentRow title={`${genre} ${type === "movie" ? "Movies" : "Shows"}`} items={items} /> | |
); | |
}; | |
const HomePage = () => { | |
const [loading, setLoading] = useState(true); | |
const [heroContent, setHeroContent] = useState(null); | |
const [recentContent, setRecentContent] = useState([]); | |
const [genres, setGenres] = useState([]); | |
const { toast } = useToast(); | |
useEffect(() => { | |
const fetchHomeData = async () => { | |
try { | |
setLoading(true); | |
// Fetch recent items for hero and recent content row | |
const recentItems = await getRecentItems(10); | |
if (recentItems && recentItems.length > 0) { | |
setHeroContent(recentItems[0]); | |
setRecentContent(recentItems); | |
} | |
// Fetch all genre categories (movies and shows together) | |
const genresRes = await getGenreCategories(); | |
console.log("Fetched genres:", genresRes); | |
const allGenres = Array.isArray(genresRes) | |
? genresRes.map((g) => g.name) | |
: genresRes.genres | |
? genresRes.genres.map((g) => g.name) | |
: []; | |
console.log("All genres:", allGenres); | |
setGenres(allGenres); | |
} catch (error) { | |
console.error("Error fetching home page data:", error); | |
toast({ | |
title: "Error loading content", | |
description: "Please try again later", | |
variant: "destructive" | |
}); | |
} finally { | |
setLoading(false); | |
} | |
}; | |
fetchHomeData(); | |
}, [toast]); | |
if (loading) { | |
return ( | |
<div className="flex items-center justify-center min-h-screen"> | |
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-netflix-red"></div> | |
</div> | |
); | |
} | |
return ( | |
<div> | |
{/* Hero Section */} | |
{heroContent && ( | |
<HeroSection | |
type={heroContent.type} | |
title={heroContent.title} | |
description={heroContent.description} | |
backdrop={heroContent.image} | |
genre={heroContent.genre} | |
year={heroContent.year} | |
/> | |
)} | |
<div className="mt-8"> | |
{/* Recent Content Row */} | |
<ContentRow title="Recent Additions" items={recentContent} /> | |
{/* Render genre rows dynamically */} | |
{genres.map((genre) => ( | |
<div key={genre}> | |
<GenreRow genre={genre} type="movie" /> | |
<GenreRow genre={genre} type="series" /> | |
</div> | |
))} | |
</div> | |
</div> | |
); | |
}; | |
export default HomePage; | |