import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import { EyeIcon, PencilIcon, TrashIcon, PlusIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline'; import { toast } from 'sonner'; import { api } from '../lib/api'; import { useUrlState, pageOf } from '../hooks/useUrlState'; import type { ApiResponse, PaginatedResponse } from '../lib/api'; import type { Blog } from '../types'; import { formatDate } from '../lib/utils'; import DataTable, { Column } from '../components/ui/DataTable'; import Pagination from '../components/ui/Pagination'; import ConfirmDialog from '../components/ui/ConfirmDialog'; const STATUS_FILTERS = [ { value: '', label: 'همه' }, { value: 'draft', label: 'پیش‌نویس' }, { value: 'published', label: 'منتشرشده' }, { value: 'archived', label: 'آرشیو' }, ]; export default function BlogsPage() { const navigate = useNavigate(); const qc = useQueryClient(); // وضعیت لیست در URL می‌ماند تا «بازگشت» از صفحهٔ جزئیات، همین فیلترها و صفحه را برگرداند. const [urlState, setUrlState] = useUrlState({ page: '1', search: '', status: '' }); const page = pageOf(urlState.page); const search = urlState.search; const statusFilter = urlState.status; const setPage = (p: number) => setUrlState({ page: String(p) }); const setSearch = (v: string) => setUrlState({ search: v, page: '1' }); const setStatusFilter = (v: string) => setUrlState({ status: v, page: '1' }); const [deleteTarget, setDeleteTarget] = useState(null); const limit = 15; const { data, isLoading } = useQuery({ queryKey: ['blogs', page, search, statusFilter], queryFn: () => { const params = new URLSearchParams({ page: String(page), limit: String(limit) }); if (search) params.set('search', search); if (statusFilter) params.set('status', statusFilter); // اندپوینت ادمین همهٔ وضعیت‌ها را برمی‌گرداند (اندپوینت عمومی فقط منتشرشده). return api.get>(`/api/v1/admin/blogs?${params}`); }, }); const deleteMutation = useMutation({ mutationFn: (b: Blog) => api.delete>(`/api/v1/blog/${b.uuid}`), onSuccess: () => { toast.success('مقاله حذف شد'); setDeleteTarget(null); qc.invalidateQueries({ queryKey: ['blogs'] }); }, onError: (err: Error) => toast.error(err.message), }); const columns: Column[] = [ { key: 'title', header: 'عنوان', render: (b) => (
{b.image_url ? ( ) : (
)}
{b.title}
), }, { key: 'author', header: 'نویسنده', render: (b) => b.author?.name ?? '—' }, { key: 'status', header: 'وضعیت', render: (b) => ( {b.status === 'published' ? 'منتشرشده' : 'پیش‌نویس'} ), }, { key: 'tags', header: 'تگ‌ها', render: (b) => (
{b.tags?.slice(0, 3).map((tag, i) => ( {tag} ))}
), }, { key: 'created_at', header: 'تاریخ', render: (b) => formatDate(b.created_at) }, ]; const items = data?.data ?? []; const total = data?.meta?.totalRecords ?? 0; return (

بلاگ

{total} مقاله
{ setSearch(e.target.value); setPage(1); }} placeholder="جستجو بر اساس عنوان..." />
{STATUS_FILTERS.map((f) => ( ))}
columns={columns} data={items} loading={isLoading} emptyMessage="هیچ مقاله‌ای یافت نشد" emptyAction={ } actions={(blog) => ( <> )} />
deleteTarget && deleteMutation.mutate(deleteTarget)} onCancel={() => setDeleteTarget(null)} />
); }