Spaces:
Sleeping
Sleeping
| 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; | |