185 lines
6.7 KiB
JavaScript
185 lines
6.7 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
import { Link } from 'react-router-dom';
|
||
import { pb } from '../../services/pocketbaseService';
|
||
import { getFileUrl } from '../../services/pocketbaseService';
|
||
import { FaStar, FaFilter } from 'react-icons/fa';
|
||
import SearchBar from '../../components/ui/SearchBar';
|
||
|
||
const MobileCatalog = () => {
|
||
const [media, setMedia] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [filters, setFilters] = useState({
|
||
type: '',
|
||
genre: '',
|
||
year: '',
|
||
sort: '-created'
|
||
});
|
||
const [showFilters, setShowFilters] = useState(false);
|
||
|
||
useEffect(() => {
|
||
fetchMedia();
|
||
}, [filters]);
|
||
|
||
const fetchMedia = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const filter = [];
|
||
|
||
if (filters.type) {
|
||
filter.push(`type = "${filters.type}"`);
|
||
}
|
||
if (filters.genre) {
|
||
filter.push(`genres ?~ "${filters.genre}"`);
|
||
}
|
||
if (filters.year) {
|
||
filter.push(`year = ${filters.year}`);
|
||
}
|
||
|
||
const records = await pb.collection('media').getList(1, 50, {
|
||
sort: filters.sort,
|
||
filter: filter.join(' && ')
|
||
});
|
||
|
||
setMedia(records.items);
|
||
} catch (error) {
|
||
console.error('Error fetching media:', error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleFilterChange = (key, value) => {
|
||
setFilters(prev => ({
|
||
...prev,
|
||
[key]: value
|
||
}));
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="flex items-center justify-center min-h-screen">
|
||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-campfire-amber"></div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-campfire-dark">
|
||
<div className="px-4 pt-4">
|
||
<SearchBar mobileMode={true} />
|
||
</div>
|
||
{/* Фильтры */}
|
||
<div className="px-4 py-4">
|
||
<div className="mb-6">
|
||
<button
|
||
onClick={() => setShowFilters(!showFilters)}
|
||
className="w-full flex items-center justify-center space-x-2 bg-campfire-dark/50 border border-campfire-ash/30 rounded-lg p-3 text-campfire-light"
|
||
>
|
||
<FaFilter />
|
||
<span>Фильтры</span>
|
||
</button>
|
||
|
||
{showFilters && (
|
||
<div className="mt-4 space-y-4 bg-campfire-dark/50 border border-campfire-ash/30 rounded-lg p-4">
|
||
<div>
|
||
<label className="block text-campfire-light mb-2">Тип</label>
|
||
<select
|
||
value={filters.type}
|
||
onChange={(e) => handleFilterChange('type', e.target.value)}
|
||
className="w-full bg-campfire-dark border border-campfire-ash/30 rounded-lg p-2 text-campfire-light"
|
||
>
|
||
<option value="">Все</option>
|
||
<option value="movie">Фильмы</option>
|
||
<option value="series">Сериалы</option>
|
||
<option value="game">Игры</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-campfire-light mb-2">Жанр</label>
|
||
<select
|
||
value={filters.genre}
|
||
onChange={(e) => handleFilterChange('genre', e.target.value)}
|
||
className="w-full bg-campfire-dark border border-campfire-ash/30 rounded-lg p-2 text-campfire-light"
|
||
>
|
||
<option value="">Все</option>
|
||
<option value="action">Боевик</option>
|
||
<option value="comedy">Комедия</option>
|
||
<option value="drama">Драма</option>
|
||
<option value="horror">Ужасы</option>
|
||
<option value="sci-fi">Фантастика</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-campfire-light mb-2">Год</label>
|
||
<select
|
||
value={filters.year}
|
||
onChange={(e) => handleFilterChange('year', e.target.value)}
|
||
className="w-full bg-campfire-dark border border-campfire-ash/30 rounded-lg p-2 text-campfire-light"
|
||
>
|
||
<option value="">Все</option>
|
||
{Array.from({ length: 24 }, (_, i) => 2024 - i).map(year => (
|
||
<option key={year} value={year}>{year}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-campfire-light mb-2">Сортировка</label>
|
||
<select
|
||
value={filters.sort}
|
||
onChange={(e) => handleFilterChange('sort', e.target.value)}
|
||
className="w-full bg-campfire-dark border border-campfire-ash/30 rounded-lg p-2 text-campfire-light"
|
||
>
|
||
<option value="-created">Сначала новые</option>
|
||
<option value="created">Сначала старые</option>
|
||
<option value="-average_rating">По рейтингу</option>
|
||
<option value="title">По названию</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Список медиа */}
|
||
<div className="space-y-4">
|
||
{media.map((item) => (
|
||
<Link
|
||
key={item.id}
|
||
to={`/media/${item.path}`}
|
||
className="block bg-campfire-dark/50 border border-campfire-ash/30 rounded-lg p-4"
|
||
>
|
||
<div className="flex items-center space-x-4">
|
||
<img
|
||
src={getFileUrl(item, 'poster')}
|
||
alt={item.title}
|
||
className="w-16 h-24 rounded-lg object-cover"
|
||
/>
|
||
<div className="flex-1">
|
||
<h3 className="text-campfire-light font-semibold mb-1">
|
||
{item.title}
|
||
</h3>
|
||
<div className="flex items-center space-x-2 text-sm">
|
||
<div className="flex items-center text-campfire-amber">
|
||
<FaStar className="mr-1" />
|
||
<span>{item.average_rating?.toFixed(1) || '0.0'}</span>
|
||
</div>
|
||
<span className="text-campfire-ash">
|
||
{item.review_count || 0} обзоров
|
||
</span>
|
||
</div>
|
||
<p className="text-campfire-ash text-sm mt-1">
|
||
{item.type === 'movie' ? 'Фильм' : item.type === 'series' ? 'Сериал' : 'Игра'} • {item.year}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default MobileCatalog;
|