import React, { useEffect, useState } from 'react'; import { TipTapEditor } from './Editor/TipTapEditor'; import { CoverImageUpload } from './ImageUpload/CoverImageUpload'; import { ImageUploader } from './ImageUpload/ImageUploader'; import { GalleryManager } from './GalleryManager'; import { useGallery } from '../hooks/useGallery'; import { ArticleData, Author, CategoryTitles, CityTitles, GalleryImage } from '../types'; import { useAuthStore } from '../stores/authStore'; import ConfirmModal from './ConfirmModal'; interface FormState { title: string; excerpt: string; categoryId: number; cityId: number; coverImage: string; readTime: number; content: string; authorId: string; galleryImages: GalleryImage[]; } interface ArticleFormProps { editingId: string | null; articleId: string; initialFormState: FormState | null; onSubmit: (articleData: ArticleData, closeForm: boolean) => Promise; onCancel: () => void; authors: Author[]; availableCategoryIds: number[]; availableCityIds: number[]; } export function ArticleForm({ editingId, articleId, initialFormState, onSubmit, onCancel, authors, availableCategoryIds, availableCityIds, }: ArticleFormProps) { const { user } = useAuthStore(); const isAdmin = user?.permissions.isAdmin || false; const [title, setTitle] = useState(''); const [excerpt, setExcerpt] = useState(''); const [categoryId, setCategoryId] = useState(availableCategoryIds[0] || 1); const [cityId, setCityId] = useState(availableCityIds[0] || 1); const [coverImage, setCoverImage] = useState('/images/cover-placeholder.webp'); const [readTime, setReadTime] = useState(5); const [content, setContent] = useState(''); const [authorId, setAuthorId] = useState(authors[0]?.id || ''); const [displayedImages, setDisplayedImages] = useState([]); const [formNewImageUrl, setFormNewImageUrl] = useState(''); const [error, setError] = useState(null); const [hasChanges, setHasChanges] = useState(false); const [isInitialLoad, setIsInitialLoad] = useState(true); const [isSubmitting, setIsSubmitting] = useState(false); // Добавляем флаг для отслеживания отправки const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false); // Состояние для модального окна const { images: galleryImages, loading: galleryLoading, error: galleryError, addImage: addGalleryImage, updateImage: updateGalleryImage, deleteImage: deleteGalleryImage, reorderImages: reorderGalleryImages } = useGallery(editingId || ''); // Добавляем обработку ошибок useEffect(() => { const handleUnhandledRejection = (event: PromiseRejectionEvent) => { console.error('Unhandled promise rejection in ArticleForm:', event.reason); event.preventDefault(); // Предотвращаем "всплытие" ошибки }; const handleError = (event: ErrorEvent) => { console.error('Unhandled error in ArticleForm:', event.error); event.preventDefault(); // Предотвращаем "всплытие" ошибки }; window.addEventListener('unhandledrejection', handleUnhandledRejection); window.addEventListener('error', handleError); return () => { window.removeEventListener('unhandledrejection', handleUnhandledRejection); window.removeEventListener('error', handleError); }; }, []); useEffect(() => { if (editingId) { setDisplayedImages(galleryImages); } else { setDisplayedImages([]); } }, [editingId, galleryImages]); useEffect(() => { if (initialFormState) { setTitle(initialFormState.title); setExcerpt(initialFormState.excerpt); setCategoryId(initialFormState.categoryId); setCityId(initialFormState.cityId); setCoverImage(initialFormState.coverImage); setReadTime(initialFormState.readTime); setContent(initialFormState.content); setAuthorId(initialFormState.authorId); setDisplayedImages(initialFormState.galleryImages || []); console.log('Содержимое статьи при загрузке:', initialFormState.content); } }, [initialFormState]); useEffect(() => { if (!initialFormState) return; const currentState: FormState = { title, excerpt, categoryId, cityId, coverImage, readTime, content, authorId, galleryImages: displayedImages }; const areRequiredFieldsFilled = title.trim() !== '' && excerpt.trim() !== ''; const hasFormChanges = Object.keys(initialFormState).some(key => { if (key === 'galleryImages') { const isDifferent = JSON.stringify(currentState[key]) !== JSON.stringify(initialFormState[key]); if (isInitialLoad && isDifferent) return false; return isDifferent; } if (key === 'content') { return JSON.stringify(currentState[key]) !== JSON.stringify(initialFormState[key]); } const currentValue = typeof currentState[key as keyof FormState] === 'number' ? String(currentState[key as keyof FormState]) : currentState[key as keyof FormState]; const initialValue = typeof initialFormState[key as keyof FormState] === 'number' ? String(initialFormState[key as keyof FormState]) : initialFormState[key as keyof FormState]; return currentValue !== initialValue; }); setHasChanges(hasFormChanges && areRequiredFieldsFilled); if (isInitialLoad) { setIsInitialLoad(false); } }, [title, excerpt, categoryId, cityId, coverImage, readTime, content, authorId, displayedImages, initialFormState, isInitialLoad]); const handleSubmit = async (e: React.FormEvent, closeForm: boolean = true) => { e.preventDefault(); console.log('Вызов handleSubmit:', { closeForm }); console.log('Содержимое статьи перед сохранением:', content); if (isSubmitting) { console.log('Форма уже отправляется, игнорируем повторную отправку'); return; } if (!title.trim() || !excerpt.trim()) { setError('Пожалуйста, заполните обязательные поля: Заголовок и Краткое описание.'); return; } if (!hasChanges) return; const selectedAuthor = editingId && isAdmin ? authors.find(a => a.id === authorId) || authors[0] : user; // Проверяем, что selectedAuthor существует и соответствует типу Author if (!selectedAuthor) { setError('Пожалуйста, выберите автора или войдите в систему.'); return; } const articleData: ArticleData = { title, excerpt, categoryId, cityId, coverImage, readTime, gallery: displayedImages, content: content || '', importId: 0, isActive: false, author: selectedAuthor, }; try { setIsSubmitting(true); await onSubmit(articleData, closeForm); } catch (error) { console.error('Ошибка при сохранении статьи:', error); setError('Не удалось сохранить статью. Пожалуйста, попробуйте снова.'); } finally { setIsSubmitting(false); } }; const handleApply = (e: React.FormEvent) => { handleSubmit(e, false); }; const handleCancel = () => { if (hasChanges) { setIsConfirmModalOpen(true); // Открываем модальное окно } else { onCancel(); } }; const handleConfirmCancel = () => { setIsConfirmModalOpen(false); onCancel(); }; const handleCloseModal = () => { setIsConfirmModalOpen(false); }; if (editingId && galleryLoading) { return (
); } return (

{editingId ? 'Редактировать статью' : 'Создать новую статью'}

{(error || galleryError) && (
{error || galleryError}
)}
handleSubmit(e, true)} className="space-y-6">
setTitle(e.target.value)} className="px-2 py-1 mt-1 block w-full rounded-lg border border-gray-300 shadow-sm hover:border-blue-300 focus:border-blue-500 focus:ring-4 focus:ring-blue-100 transition-all duration-200" required />