import React, { useState, useEffect } from 'react'; import { Plus, X, CheckCircle, XCircle, Calendar, Pencil, Trash2, Eye, FileText } from 'lucide-react'; import { backendApi } from '../../services/apiClient'; import type { PostTopic } from '../../types'; interface PostTopicsManagerProps { onCreatePostFromTopic?: (topic: PostTopic) => void; } export const PostTopicsManager: React.FC = ({ onCreatePostFromTopic }) => { const [list, setList] = useState([]); const [loading, setLoading] = useState(true); const [selectedMonth, setSelectedMonth] = useState(() => { const now = new Date(); return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; }); const [modalOpen, setModalOpen] = useState(false); const [viewModal, setViewModal] = useState(null); const [editing, setEditing] = useState(null); const [form, setForm] = useState({ title: '', description: '', scheduledDate: '', month: selectedMonth }); const [saving, setSaving] = useState(false); const [actionLoading, setActionLoading] = useState(false); const loadList = () => { setLoading(true); backendApi.getPostTopics({ month: selectedMonth, limit: 100 }) .then((data) => setList(Array.isArray(data) ? data : [])) .catch(() => setList([])) .finally(() => setLoading(false)); }; useEffect(() => { loadList(); }, [selectedMonth]); const openCreate = () => { setEditing(null); const firstDayOfMonth = `${selectedMonth}-01`; setForm({ title: '', description: '', scheduledDate: firstDayOfMonth, month: selectedMonth }); setModalOpen(true); }; const openEdit = (topic: PostTopic) => { setEditing(topic); setForm({ title: topic.title, description: topic.description ?? '', scheduledDate: topic.scheduledDate ? topic.scheduledDate.slice(0, 10) : `${topic.month}-01`, month: topic.month }); setModalOpen(true); }; const handleSave = () => { const title = form.title.trim(); if (!title || !form.scheduledDate) return; setSaving(true); const monthVal = form.month || form.scheduledDate.slice(0, 7); if (editing) { backendApi.updatePostTopic(editing.id, { title, description: form.description.trim() || undefined, scheduledDate: form.scheduledDate }) .then(() => { setModalOpen(false); loadList(); }) .finally(() => setSaving(false)); } else { backendApi.createPostTopic({ title, description: form.description.trim() || undefined, scheduledDate: form.scheduledDate, month: monthVal }) .then(() => { setModalOpen(false); loadList(); }) .finally(() => setSaving(false)); } }; const handleApprove = (id: number) => { if (!confirm('Одобрить тему графика публикации?')) return; setActionLoading(true); backendApi.approvePostTopic(id) .then(() => { setViewModal(null); loadList(); }) .finally(() => setActionLoading(false)); }; const handleReject = (id: number) => { const reason = prompt('Укажите причину отклонения:'); if (!reason) return; setActionLoading(true); backendApi.rejectPostTopic(id, { rejectionReason: reason }) .then(() => { setViewModal(null); loadList(); }) .finally(() => setActionLoading(false)); }; const handleSendToApproval = (id: number) => { setActionLoading(true); backendApi.updatePostTopic(id, { status: 'pending_approval' }) .then(() => { setModalOpen(false); loadList(); }) .finally(() => setActionLoading(false)); }; const handleDelete = (id: number) => { if (!confirm('Удалить тему из графика?')) return; backendApi.deletePostTopic(id).then(() => loadList()); }; const getStatusColor = (status: PostTopic['status']) => { switch (status) { case 'approved': return 'text-emerald-600 bg-emerald-50'; case 'pending_approval': return 'text-amber-600 bg-amber-50'; case 'rejected': return 'text-red-600 bg-red-50'; default: return 'text-slate-500 bg-slate-50'; } }; const getStatusLabel = (status: PostTopic['status']) => { switch (status) { case 'draft': return 'Черновик'; case 'pending_approval': return 'На согласовании'; case 'approved': return 'Одобрено'; case 'rejected': return 'Отклонено'; default: return status; } }; const pendingCount = list.filter(t => t.status === 'pending_approval').length; const approvedCount = list.filter(t => t.status === 'approved').length; const approvedTopics = list.filter(t => t.status === 'approved'); // Группировка по датам для календарного вида const topicsByDate: Record = {}; list.forEach(topic => { const date = topic.scheduledDate ? topic.scheduledDate.slice(0, 10) : `${topic.month}-01`; if (!topicsByDate[date]) topicsByDate[date] = []; topicsByDate[date].push(topic); }); return (

График публикации

План публикаций на месяц (без контента). По этим темам создаются посты с контентом для одобрения.

setSelectedMonth(e.target.value)} className="px-4 py-2 border border-slate-200 rounded-xl text-sm" />
{(pendingCount > 0 || approvedCount > 0) && (
{pendingCount > 0 && (

На согласовании

{pendingCount}

)} {approvedCount > 0 && (

Одобрено

{approvedCount}

)}
)} {loading ? (
Загрузка...
) : list.length === 0 ? (

Нет записей в графике для выбранного месяца

Создайте график публикаций на месяц, затем по этим темам создавайте посты с контентом

) : (
{/* Календарное отображение */} {Object.keys(topicsByDate).sort().map(date => (
{new Date(date).toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', weekday: 'short' })}
{topicsByDate[date].map((topic) => (
{topic.title} {getStatusLabel(topic.status)}
{topic.description &&

{topic.description}

}
{topic.status === 'approved' && onCreatePostFromTopic && ( )} {topic.status === 'draft' && ( <> )} {topic.status === 'pending_approval' && ( <> )}
))}
))}
)} {/* Create/Edit Modal */} {modalOpen && (
!saving && setModalOpen(false)}>
e.stopPropagation()}>

{editing ? 'Редактировать тему графика' : 'Добавить в график публикации'}

setForm((f) => ({ ...f, title: e.target.value }))} placeholder="Напр.: Отчёт о работе за месяц" className="w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm" />