import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { api } from '../lib/api'; import type { ApiResponse, PaginatedResponse } from '../lib/api'; import type { Blog } from '../types'; import { formatDate } from '../lib/utils'; import PageHeader from '../components/ui/PageHeader'; import DataTable, { Column } from '../components/ui/DataTable'; import Pagination from '../components/ui/Pagination'; import Modal from '../components/ui/Modal'; import ConfirmDialog from '../components/ui/ConfirmDialog'; const limit = 15; /** وضعیت بازبینی → رنگ نشان. مقاله‌های صف همه pending_review هستند. */ function reviewBadgeColor(status?: string | null): string { switch (status) { case 'approved': return 'green'; case 'rejected': return 'red'; case 'pending_review': return 'amber'; default: return 'gray'; } } function reviewBadgeLabel(status?: string | null): string { switch (status) { case 'approved': return 'تأییدشده'; case 'rejected': return 'ردشده'; case 'pending_review': return 'در انتظار بازبینی'; default: return status ?? '—'; } } export default function BlogReviewPage() { const qc = useQueryClient(); const [page, setPage] = useState(1); const [preview, setPreview] = useState(null); const [rejectTarget, setRejectTarget] = useState(null); const [rejectNote, setRejectNote] = useState(''); const { data, isLoading } = useQuery({ queryKey: ['blog-review-queue', page], queryFn: () => { const params = new URLSearchParams({ page: String(page), limit: String(limit) }); return api.get>(`/api/v1/admin/blog/review-queue?${params}`); }, }); const items = data?.data ?? []; const total = data?.meta?.totalRecords ?? 0; const approveMutation = useMutation({ mutationFn: (blog: Blog) => api.post>(`/api/v1/admin/blog/${blog.uuid}/review`, { decision: 'approved', publish: true, }), onSuccess: () => { toast.success('مقاله تأیید و منتشر شد'); setPreview(null); qc.invalidateQueries({ queryKey: ['blog-review-queue'] }); }, onError: (err: Error) => toast.error(err.message), }); const rejectMutation = useMutation({ mutationFn: (vars: { blog: Blog; note: string }) => api.post>(`/api/v1/admin/blog/${vars.blog.uuid}/review`, { decision: 'rejected', note: vars.note, }), onSuccess: () => { toast.success('مقاله رد شد'); setRejectTarget(null); setRejectNote(''); setPreview(null); qc.invalidateQueries({ queryKey: ['blog-review-queue'] }); }, onError: (err: Error) => toast.error(err.message), }); const columns: Column[] = [ { key: 'title', header: 'عنوان', render: (b) => {b.title} }, { key: 'topic_slug', header: 'موضوع', render: (b) => {b.topic_slug ?? '—'}, }, { key: 'review_status', header: 'وضعیت', render: (b) => ( {reviewBadgeLabel(b.review_status)} ), }, { key: 'created_at', header: 'تاریخ', render: (b) => formatDate(b.created_at) }, ]; return (
columns={columns} data={items} loading={isLoading} emptyMessage="مقاله‌ای در انتظار بازبینی نیست" actions={(b) => ( )} />
{/* پیش‌نمایش کامل + تصمیم */} setPreview(null)} footer={ preview ? (
) : undefined } > {preview && (
{preview.summary &&

{preview.summary}

}
{preview.sources && preview.sources.length > 0 && (

منابع

)}
)} {/* رد با درج دلیل الزامی */} { if (!rejectTarget) return; if (!rejectNote.trim()) { toast.error('درج دلیل الزامی است'); return; } rejectMutation.mutate({ blog: rejectTarget, note: rejectNote.trim() }); }} onCancel={() => { setRejectTarget(null); setRejectNote(''); }} >