frontend/src/App.tsxtypescript · @kibot
20/9/2026, 17:19:47import React, { useState, useEffect } from 'react'
import VideoPlayer from './components/VideoPlayer'
import type { Movie, MovieDetailsResponse } from './types/api'
const API_BASE_URL = 'http://localhost:8000'
const App: React.FC = () => {
const [selectedMovie, setSelectedMovie] = useState<Movie | null>(null)
const [movies, setMovies] = useState<Movie[]>([])
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
// Cargar películas iniciales desde el backend
useEffect(() => {
fetchMovies()
}, [])
const fetchMovies = async () => {
try {
setLoading(true)
setError(null)
const response = await fetch(`${API_BASE_URL}/movies`)
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`)
}
const data: Movie[] = await response.json()
setMovies(data)
} catch (err) {
console.error('Error cargando películas:', err)
setError('No se pudieron cargar las películas. Asegúrate de que el backend esté corriendo.')
} finally {
setLoading(false)
}
}
const handleMovieSelect = async (movie: Movie) => {
try {
// Obtener detalles completos de la película
const detailsResponse = await fetch(`${API_BASE_URL}/movies/${movie.id}`)
if (!detailsResponse.ok) {
throw new Error('Película no encontrada')
}
const movieDetails: MovieDetailsResponse = await detailsResponse.json()
setSelectedMovie(movieDetails)
} catch (err) {
console.error('Error obteniendo detalles de la película:', err)
setError('No se pudo obtener los detalles de la película')
}
}
const handleRefresh = () => {
fetchMovies()
}
return (
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif', minHeight: '100vh', backgroundColor: '#141414' }}>
<h1 style={{ color: '#e50914', marginBottom: '20px' }}>NetFlux - Tu propio Netflix Casero</h1>
{error && (
<div style={{
backgroundColor: '#d32f2f',
color: 'white',
padding: '12px',
borderRadius: '4px',
marginBottom: '20px'
}}>
{error}
</div>
)}
<button
onClick={handleRefresh}
style={{
backgroundColor: '#333',
color: 'white',
border: 'none',
padding: '8px 16px',
borderRadius: '4px',
cursor: 'pointer',
marginBottom: '20px'
}}
>
🔄 Recargar películas
</button>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '16px' }}>
{loading ? (
<div style={{ gridColumn: '1/-1', textAlign: 'center', padding: '40px' }}>
Cargando películas...
</div>
) : movies.length === 0 ? (
<div style={{ gridColumn: '1/-1', textAlign: 'center', padding: '40px', color: '#888' }}>
No hay películas disponibles. Asegúrate de que el backend esté corriendo.
</div>
) : (
movies.map(movie => (
<div
key={movie.id}
onClick={() => handleMovieSelect(movie)}
style={{
cursor: 'pointer',
padding: '12px',
border: '1px solid #333',
borderRadius: '8px',
textAlign: 'center',
backgroundColor: '#1f1f1f',
transition: 'transform 0.2s, box-shadow 0.2s',
transform: 'scale(1)',
boxShadow: 'none'
}}
onMouseEnter={(e) => {
e.currentTarget.style.transform = 'scale(1.05)'
e.currentTarget.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)'
e.currentTarget.style.boxShadow = 'none'
}}
>
<img
src={movie.poster_path || '/placeholder.jpg'}
alt={movie.title}
style={{ width: '100%', height: '300px', objectFit: 'cover', borderRadius: '4px' }}
/>
<h3 style={{ margin: '8px 0 4px', color: '#fff' }}>{movie.title}</h3>
</div>
))
)}
</div>
{selectedMovie && (
<div style={{ marginTop: '24px' }}>
<VideoPlayer movie={selectedMovie} />
</div>
)}
</div>
)
}
export default App