import React, { useState, useEffect, useRef, useCallback } from 'react'; import 'quill/dist/quill.snow.css'; import '../App.css'; import DOMPurify from 'dompurify'; function Editor() { const [isLoggedIn, setIsLoggedIn] = useState(false); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [loginError, setLoginError] = useState(''); const [activeTab, setActiveTab] = useState('info'); const [blocks, setBlocks] = useState([]); const [showEditModal, setShowEditModal] = useState(false); const [editingBlockId, setEditingBlockId] = useState(null); const [editContent, setEditContent] = useState(''); const [editPreviewContent, setEditPreviewContent] = useState(''); const [showConfirmModal, setShowConfirmModal] = useState(false); //const [csrfToken, setCsrfToken] = useState(''); const [notify, setNotify] = useState({ message: '', type: '', show: false }); const editorRef = useRef(null); const quillRef = useRef(null); const originalContentRef = useRef(''); // Notify helper const showNotify = useCallback((message, type = 'success') => { setNotify({ message, type, show: true }); setTimeout(() => { setNotify({ message: '', type: '', show: false }); }, 2200); }, []); // Fetch CSRF token //useEffect(() => { // fetch('/moders/csrf-token') // .then(res => res.json()) // .then(data => { // setCsrfToken(data.csrfToken); //}) //.catch(() => {}); //}, []); // Check session on mount useEffect(() => { fetch('/moders/check-session', { credentials: 'include' }) .then(res => { if (res.ok) { setIsLoggedIn(true); loadBlocks(activeTab); } else { setIsLoggedIn(false); } }) .catch(() => setIsLoggedIn(false)); }, []); // eslint-disable-line react-hooks/exhaustive-deps const authenticatedFetch = useCallback((url, options = {}) => { return fetch(url, options); }, []); const loadBlocks = useCallback(async (tab) => { try { const res = await fetch(`/moders/content/${tab}`); const data = await res.json(); setBlocks(Array.isArray(data) ? data : []); } catch (e) { setBlocks([]); } }, []); const handleLogin = async (e) => { e.preventDefault(); setLoginError(''); try { const res = await fetch('/moders/moder-login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (res.ok) { setIsLoggedIn(true); setLoginError(''); loadBlocks(activeTab); } else { const data = await res.json(); setLoginError(data.message || 'Ошибка входа'); } } catch (err) { setLoginError('Ошибка сервера'); } }; const handleLogout = async () => { try { await authenticatedFetch('/moders/moder-logout'); } catch (e) { // ignore } setIsLoggedIn(false); setUsername(''); setPassword(''); showNotify('Вы вышли из системы', 'success'); }; const handleTabChange = (tab) => { setActiveTab(tab); if (isLoggedIn) { loadBlocks(tab); } }; const handleAddBlock = () => { setEditingBlockId(null); setEditContent('
Новый блок
'); setEditPreviewContent(DOMPurify.sanitize('Новый блок
')); originalContentRef.current = 'Новый блок
'; setShowEditModal(true); }; const handleEditBlock = (block) => { setEditingBlockId(block.id); setEditContent(block.content); setEditPreviewContent(DOMPurify.sanitize(block.content)); originalContentRef.current = block.content; setShowEditModal(true); }; const handleDeleteBlock = async (blockId) => { if (!window.confirm('Удалить блок?')) return; try { await authenticatedFetch(`/moders/content/${activeTab}/${blockId}`, { method: 'DELETE' }); showNotify('Блок удалён', 'success'); loadBlocks(activeTab); } catch (e) { showNotify('Ошибка удаления', 'error'); } }; const handleSaveBlock = async (e) => { e.preventDefault(); const content = DOMPurify.sanitize(editContent.trim()); if (!content || content === 'Нет блоков
} {blocks.map(block => (Вы уверены, что хотите закрыть окно?
Несохранённые изменения будут потеряны!