Files
neomovies/src/app/favorites/page.tsx

122 lines
4.3 KiB
TypeScript
Raw Normal View History

'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
2025-08-10 22:20:58 +00:00
import { neoApi, getImageUrl } from '@/lib/neoApi';
2025-07-08 00:15:55 +03:00
import { Loader2, HeartCrack } from 'lucide-react';
interface Favorite {
id: number;
mediaId: string;
mediaType: 'movie' | 'tv';
title: string;
posterPath: string;
}
export default function FavoritesPage() {
const [favorites, setFavorites] = useState<Favorite[]>([]);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
const fetchFavorites = async () => {
const token = localStorage.getItem('token');
if (!token) {
2025-08-10 22:20:58 +00:00
router.replace('/login');
return;
}
try {
2025-08-10 22:20:58 +00:00
const response = await neoApi.get('/api/v1/favorites');
2025-08-11 18:41:57 +00:00
setFavorites(response.data);
2025-08-10 22:20:58 +00:00
} catch (error: any) {
console.error('Failed to fetch favorites:', error);
2025-08-10 22:20:58 +00:00
// Редиректим только при явном 401
if (error?.response?.status === 401) {
localStorage.removeItem('token');
localStorage.removeItem('userName');
localStorage.removeItem('userEmail');
router.replace('/login');
}
} finally {
setLoading(false);
}
};
fetchFavorites();
}, [router]);
if (loading) {
return (
2025-07-08 00:15:55 +03:00
<div className="min-h-screen bg-background flex items-center justify-center">
<Loader2 className="h-16 w-16 animate-spin text-accent" />
</div>
);
}
if (favorites.length === 0) {
return (
2025-07-08 00:15:55 +03:00
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<HeartCrack size={80} className="mx-auto mb-6 text-gray-400" />
<h1 className="text-3xl font-bold text-foreground mb-4">Избранное пусто</h1>
<p className="text-lg text-gray-600 dark:text-gray-400 mb-8">
У вас пока нет избранных фильмов и сериалов
</p>
<Link
href="/"
className="inline-flex items-center justify-center px-6 py-3 text-base font-medium text-white bg-accent rounded-lg hover:bg-accent/90 transition-colors"
>
Найти фильмы
</Link>
</div>
</div>
);
}
return (
2025-07-08 00:15:55 +03:00
<div className="min-h-screen bg-background">
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-foreground mb-4">Избранное</h1>
<p className="text-lg text-gray-600 dark:text-gray-400">
Ваша коллекция любимых фильмов и сериалов
</p>
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-6">
{favorites.map(favorite => (
<Link
key={`${favorite.mediaType}-${favorite.mediaId}`}
href={`/${favorite.mediaType === 'movie' ? 'movie' : 'tv'}/${favorite.mediaId}`}
className="group"
>
<div className="overflow-hidden rounded-xl bg-gray-100 dark:bg-gray-800 shadow-sm transition-all duration-300 group-hover:shadow-lg group-hover:-translate-y-1">
<div className="relative aspect-[2/3] w-full">
<Image
src={favorite.posterPath ? getImageUrl(favorite.posterPath) : '/images/placeholder.jpg'}
alt={favorite.title}
fill
sizes="(max-width: 640px) 50vw, (max-width: 768px) 33vw, (max-width: 1024px) 25vw, (max-width: 1280px) 20vw, 16vw"
className="object-cover"
unoptimized
/>
</div>
</div>
<div className="mt-3 px-1">
<h3 className="font-semibold text-base text-foreground truncate group-hover:text-accent transition-colors">
{favorite.title}
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
{favorite.mediaType === 'movie' ? 'Фильм' : 'Сериал'}
</p>
</div>
</Link>
))}
</div>
</div>
</div>
);
2025-08-10 22:20:58 +00:00
}