import React, { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { getMediaById, // To get the parent media title getSeasonsByMediaId, createSeason, updateSeason, deleteSeason, getFileUrl, // To display season posters uploadFile, // To upload season posters deleteFile // To delete season posters } from '../../services/pocketbaseService'; import Modal from '../../components/common/Modal'; import DatePicker from 'react-datepicker'; // Assuming you have react-datepicker installed import "react-datepicker/dist/react-datepicker.css"; // Import styles import { FaEdit, FaTrashAlt, FaPlus } from 'react-icons/fa'; // Import icons // Season Form Component (can be reused for Add and Edit) const SeasonForm = ({ season, mediaId, onSubmit, onCancel, isSubmitting, error }) => { const { user } = useAuth(); // Get current user for created_by const [formData, setFormData] = useState({ season_number: '', title: '', overview: '', release_date: null, // poster: null, // File object for new upload - Handled by separate state is_published: false, // media_id and created_by will be added in onSubmit handler }); const [currentPosterUrl, setCurrentPosterUrl] = useState(null); // To display existing poster const [posterFile, setPosterFile] = useState(null); // State for the selected file input const [deleteExistingPoster, setDeleteExistingPoster] = useState(false); // State to track if existing poster should be deleted useEffect(() => { console.log('SeasonForm useEffect: season changed', season); if (season) { // Pre-fill form for editing setFormData({ season_number: season.season_number || '', title: season.title || '', overview: season.overview || '', release_date: season.release_date ? new Date(season.release_date) : null, // poster: null, // Clear file input state - Handled by separate state is_published: season.is_published ?? false, }); // Set current poster URL if exists setCurrentPosterUrl(season.poster ? getFileUrl(season, 'poster') : null); setPosterFile(null); // Clear file input setDeleteExistingPoster(false); // Reset delete flag } else { // Reset form for adding setFormData({ season_number: '', title: '', overview: '', release_date: null, // poster: null, is_published: false, }); setCurrentPosterUrl(null); setPosterFile(null); setDeleteExistingPoster(false); } }, [season]); const handleChange = (e) => { const { name, value, type, checked } = e.target; setFormData(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value })); }; const handleDateChange = (date) => { setFormData(prev => ({ ...prev, release_date: date })); }; const handleFileChange = (e) => { const file = e.target.files?.[0] || null; setPosterFile(file); // Store the file object // If a new file is selected, don't delete the existing one if (file) { setDeleteExistingPoster(false); } }; const handleRemoveExistingPoster = () => { setCurrentPosterUrl(null); // Hide the current poster preview setDeleteExistingPoster(true); // Mark for deletion on submit setPosterFile(null); // Ensure no new file is selected }; const handleSubmit = async (e) => { e.preventDefault(); // Basic validation if (!formData.season_number || isNaN(parseInt(formData.season_number)) || parseInt(formData.season_number) <= 0) { alert('Номер сезона обязателен и должен быть положительным числом.'); return; } // Create FormData for PocketBase const dataToSend = new FormData(); dataToSend.append('media_id', mediaId); // Link to parent media dataToSend.append('season_number', parseInt(formData.season_number)); // Ensure number type dataToSend.append('title', formData.title || ''); dataToSend.append('overview', formData.overview || ''); if (formData.release_date) { // PocketBase expects ISO string for datetime/date fields dataToSend.append('release_date', formData.release_date.toISOString()); } else { // Append empty string or null if date is optional and not set // PocketBase handles empty string for optional date fields dataToSend.append('release_date', ''); } dataToSend.append('is_published', formData.is_published); dataToSend.append('created_by', user.id); // Set creator to current user // Handle poster file upload or deletion if (posterFile) { // Append the new file dataToSend.append('poster', posterFile); } else if (deleteExistingPoster) { // If no new file and delete flag is true, signal deletion // For FormData, set the file field to an empty string or empty FileList // Empty string is simpler for single file fields dataToSend.append('poster', ''); // Signal deletion } // If no new file and not deleting, do not append 'poster' field at all // This prevents PocketBase from trying to update it if it's unchanged onSubmit(dataToSend); // Pass FormData to parent handler }; return (
); }; const AdminSeasonsPage = () => { const { mediaId } = useParams(); // Get mediaId from URL const navigate = useNavigate(); const { user, userProfile, loading: authLoading } = useAuth(); const [media, setMedia] = useState(null); // To store parent media details const [seasons, setSeasons] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [seasonToEdit, setSeasonToEdit] = useState(null); const [formError, setFormError] = useState(null); // Error for the form modal const [isSubmittingForm, setIsSubmittingForm] = useState(false); // Submitting state for form // Check if the current user is admin const isAdmin = userProfile?.role === 'admin'; // Use useCallback to memoize loadData const loadData = useCallback(async () => { if (!mediaId) { setError('ID медиа не указан.'); setLoading(false); return; } try { setLoading(true); setError(null); // Fetch parent media details const mediaData = await getMediaById(mediaId); if (!mediaData) { setError('Родительское медиа не найдено.'); setLoading(false); return; } setMedia(mediaData); // Fetch seasons for this media const seasonsData = await getSeasonsByMediaId(mediaId); // Sort seasons by season_number seasonsData.sort((a, b) => a.season_number - b.season_number); setSeasons(seasonsData || []); } catch (err) { console.error('Error loading seasons data:', err); setError('Не удалось загрузить данные сезонов.'); } finally { setLoading(false); } }, [mediaId]); // Depend on mediaId useEffect(() => { console.log('AdminSeasonsPage mounted, mediaId:', mediaId, 'user:', user); // Wait for auth to finish loading before checking user/profile if (authLoading) { console.log('AdminSeasonsPage: Auth loading...'); return; } // Redirect if not admin (AdminRoute should handle this, but double-check) if (!user || !isAdmin) { console.warn('AdminSeasonsPage: User is not admin, redirecting.'); navigate('/admin'); // Redirect to admin dashboard or login return; } // Load data if user is admin and mediaId is available if (user && isAdmin && mediaId) { loadData(); } }, [mediaId, user, userProfile, authLoading, navigate, isAdmin, loadData]); // Depend on auth states, navigate, loadData const handleAddSeason = () => { setSeasonToEdit(null); // Ensure we are adding, not editing setFormError(null); // Clear previous form errors setIsAddModalOpen(true); }; const handleEditSeason = (season) => { setSeasonToEdit(season); // Set season to edit setFormError(null); // Clear previous form errors setIsEditModalOpen(true); }; const handleDeleteSeason = async (seasonId) => { if (!window.confirm('Вы уверены, что хотите удалить этот сезон? Это также удалит все связанные с ним рецензии!')) { return; } try { setLoading(true); // Show main loading indicator setError(null); // Clear main error await deleteSeason(seasonId); loadData(); // Reload the list after deletion } catch (err) { console.error('Error deleting season:', err); setError('Не удалось удалить сезон.'); setLoading(false); // Hide loading on error } }; const handleFormSubmit = async (formData) => { setIsSubmittingForm(true); setFormError(null); // Clear previous form errors try { if (seasonToEdit) { // Update existing season await updateSeason(seasonToEdit.id, formData); console.log('Season updated successfully.'); } else { // Create new season await createSeason(formData); console.log('Season created successfully.'); } // Close modal and reload data setIsAddModalOpen(false); setIsEditModalOpen(false); setSeasonToEdit(null); loadData(); // Reload the list after add/edit } catch (err) { console.error('Error submitting season form:', err); // Set form-specific error setFormError(err.message || 'Произошла ошибка при сохранении сезона.'); } finally { setIsSubmittingForm(false); } }; const handleFormCancel = () => { setIsAddModalOpen(false); setIsEditModalOpen(false); setSeasonToEdit(null); setFormError(null); // Clear form error on cancel }; if (authLoading || loading) { return{/* Use flex-grow */} {season.overview}
)} {season.release_date && (Дата выхода: {new Date(season.release_date).toLocaleDateString()}
)} {/* Display season rating and review count */}