import React, { useState, useMemo } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Ic } from '@/icons' import { Card, CardContent } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, } from '@/components/ui/dialog' import { Switch } from '@/components/ui/switch' import { Textarea } from '@/components/ui/textarea' import { toast } from '@/stores/toast' import { toFa } from '@/lib/utils' import { formatDualDate } from '@/lib/date-utils' import { useTranslation } from '@/i18n' import { fetchAdminBlogs, saveAdminArticle, deleteAdminArticle, toggleAdminArticleStatus, type BlogArticleItem, type SaveBlogArticlePayload, } from '../services/blogs-api' const LANGUAGE_CONFIG = [ { code: 'fa', label: 'فارسی', flag: '🇮🇷', dir: 'rtl' }, { code: 'ar', label: 'العربية', flag: '🇸🇦', dir: 'rtl' }, { code: 'en', label: 'English', flag: '🇬🇧', dir: 'ltr' }, { code: 'ru', label: 'Русский', flag: '🇷🇺', dir: 'ltr' }, { code: 'id', label: 'Indonesia', flag: '🇮🇩', dir: 'ltr' }, ] as const type SupportedLang = (typeof LANGUAGE_CONFIG)[number]['code'] const FILTER_LANGUAGE_OPTIONS = [ { code: 'ALL', label: 'همه زبان‌ها', flag: '🌐' }, ...LANGUAGE_CONFIG, ] interface FormTranslationState { title: string summary: string content: string tag_names: string[] } export function BlogsServiceView() { const { language } = useTranslation() const qc = useQueryClient() const [selectedFilterLang, setSelectedFilterLang] = useState('ALL') const [statusFilter, setStatusFilter] = useState('ALL') const [searchQuery, setSearchQuery] = useState('') // Dialog state const [isModalOpen, setIsModalOpen] = useState(false) const [editingArticleId, setEditingArticleId] = useState(null) const [formGroupKey, setFormGroupKey] = useState('') const [formBaseSlug, setFormBaseSlug] = useState('') const [formCover, setFormCover] = useState('') const [formStatus, setFormStatus] = useState(true) const [formIsTopList, setFormIsTopList] = useState(false) const [activeFormLang, setActiveFormLang] = useState('fa') const [previewMode, setPreviewMode] = useState(false) // Translations dictionary for the 5 languages const [formTranslations, setFormTranslations] = useState>({ fa: { title: '', summary: '', content: '', tag_names: ['زیارت', 'عقیله'] }, ar: { title: '', summary: '', content: '', tag_names: ['زيارة', 'عقيلة'] }, en: { title: '', summary: '', content: '', tag_names: ['Pilgrimage', 'Aqila'] }, ru: { title: '', summary: '', content: '', tag_names: ['Паломничество', 'Акила'] }, id: { title: '', summary: '', content: '', tag_names: ['Ziarah', 'Aqila'] }, }) // Query articles list const { data: articles = [], isLoading, refetch } = useQuery({ queryKey: ['admin-blogs', selectedFilterLang, statusFilter], queryFn: () => fetchAdminBlogs({ language_code: selectedFilterLang !== 'ALL' ? selectedFilterLang : undefined, status: statusFilter !== 'ALL' ? statusFilter : undefined, }), }) // Mutations const saveMutation = useMutation({ mutationFn: (payload: SaveBlogArticlePayload) => saveAdminArticle(payload), onSuccess: (_, variables) => { qc.invalidateQueries({ queryKey: ['admin-blogs'] }) setIsModalOpen(false) toast.success(variables.id ? 'مقاله و ترجمه‌های آن با موفقیت به‌روزرسانی شد.' : 'مقاله جدید با موفقیت در دیتابیس ثبت گردید.') }, onError: (err: any) => { toast.error(err?.message || 'خطا در ثبت یا به‌روزرسانی مقاله') }, }) const deleteMutation = useMutation({ mutationFn: (id: number) => deleteAdminArticle(id), onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-blogs'] }) toast.success('مقاله و کلیه ترجمه‌های آن حذف شدند.') }, onError: (err: any) => { toast.error(err?.message || 'خطا در حذف مقاله') }, }) const toggleStatusMutation = useMutation({ mutationFn: ({ id, status }: { id: number; status: boolean }) => toggleAdminArticleStatus(id, status), onSuccess: () => { qc.invalidateQueries({ queryKey: ['admin-blogs'] }) toast.success('وضعیت انتشار مقاله در کل زبان‌ها به‌روزرسانی شد.') }, onError: (err: any) => { toast.error(err?.message || 'خطا در تغییر وضعیت مقاله') }, }) // Filtered articles const filteredArticles = useMemo(() => { let list = articles if (searchQuery.trim()) { const q = searchQuery.toLowerCase().trim() list = list.filter((a) => { if (a.title?.toLowerCase().includes(q) || a.base_slug?.toLowerCase().includes(q)) { return true } // Search across all translations return Object.values(a.translations || {}).some( (t) => t.title?.toLowerCase().includes(q) || t.summary?.toLowerCase().includes(q) ) }) } return list }, [articles, searchQuery]) // Statistics const totalCount = articles.length const activeCount = articles.filter((a) => a.status).length const draftCount = articles.filter((a) => !a.status).length // Open Add Dialog const handleOpenAdd = () => { setEditingArticleId(null) setFormGroupKey('') setFormBaseSlug('') setFormCover('') setFormStatus(true) setFormIsTopList(false) setActiveFormLang('fa') setPreviewMode(false) setFormTranslations({ fa: { title: '', summary: '', content: '', tag_names: ['زیارت', 'عقیله'] }, ar: { title: '', summary: '', content: '', tag_names: ['زيارة', 'عقيلة'] }, en: { title: '', summary: '', content: '', tag_names: ['Pilgrimage', 'Aqila'] }, ru: { title: '', summary: '', content: '', tag_names: ['Паломничество', 'Акила'] }, id: { title: '', summary: '', content: '', tag_names: ['Ziarah', 'Aqila'] }, }) setIsModalOpen(true) } // Open Edit Dialog const handleOpenEdit = (article: BlogArticleItem) => { setEditingArticleId(article.id) setFormGroupKey(article.group_key || article.base_slug) setFormBaseSlug(article.base_slug || article.group_key) setFormCover(article.cover || '') setFormStatus(article.status) setFormIsTopList(article.is_top_list) setActiveFormLang( (article.available_languages?.[0] as SupportedLang) || 'fa' ) setPreviewMode(false) // Populate translations const newTrans: Record = { fa: { title: '', summary: '', content: '', tag_names: [] }, ar: { title: '', summary: '', content: '', tag_names: [] }, en: { title: '', summary: '', content: '', tag_names: [] }, ru: { title: '', summary: '', content: '', tag_names: [] }, id: { title: '', summary: '', content: '', tag_names: [] }, } LANGUAGE_CONFIG.forEach((cfg) => { const existing = article.translations?.[cfg.code] if (existing) { newTrans[cfg.code] = { title: existing.title || '', summary: existing.summary || '', content: existing.content || '', tag_names: existing.tag_names || existing.tags?.map((t) => t.name) || [], } } }) setFormTranslations(newTrans) setIsModalOpen(true) } // Save handler const handleSaveArticle = () => { // Validate that at least one language has a title const hasAnyTranslation = Object.values(formTranslations).some( (t) => t.title && t.title.trim().length > 0 ) if (!hasAnyTranslation) { toast.error('لطفاً عنوان مقاله را حداقل برای یک زبان وارد نمایید.') return } let finalSlug = formBaseSlug.trim() if (!finalSlug) { // Auto generate from first available title const firstTitle = Object.values(formTranslations).find((t) => t.title.trim())?.title || 'article' finalSlug = firstTitle .toLowerCase() .replace(/[^a-z0-9\u0600-\u06FF\s-]/g, '') .replace(/\s+/g, '-') .slice(0, 50) } const payload: SaveBlogArticlePayload = { id: editingArticleId || undefined, group_key: formGroupKey || finalSlug, base_slug: finalSlug, cover: formCover.trim() || undefined, status: formStatus, is_top_list: formIsTopList, translations: formTranslations, } saveMutation.mutate(payload) } const currentLangConfig = LANGUAGE_CONFIG.find((l) => l.code === activeFormLang) || LANGUAGE_CONFIG[0] const currentTranslation = formTranslations[activeFormLang] return (
{/* ۱. سربرگ یکپارچه بخش وبلاگ و آمار کلان */}

وبلاگ و مقالات دانشنامه

Articles & Encyclopedia

بانک مقالات تخصصی، دانشنامه اماکن متبرکه، راهنمای جامع زیارت و سفر به عتبات عالیات با مدیریت یکپارچه ترجمه در ۵ زبان

محتوا و سئو چندزبانه
{/* کارت‌های آماری */}

کل مقالات دانشنامه

{toFa(totalCount)}

مقالات منتشرشده (فعال)

{toFa(activeCount)}

پیش‌نویس / غیرفعال

{toFa(draftCount)}

پوشش زبان‌های عقیله

۵ زبان رسمی

{/* ۲. تب‌های فیلتر زبان و نوار جستجو */}
{/* فیلتر زبان */}
{FILTER_LANGUAGE_OPTIONS.map((lang) => ( ))}
{/* جستجو، وضعیت و دکمه بروزرسانی */}
setSearchQuery(e.target.value)} placeholder="جستجو در عنوان یا موضوع مقاله..." className="h-9 ps-9 text-xs bg-surface-base rounded-xl border-border-soft" />
{/* ۳. لیست و جدول مقالات یکپارچه */}
فهرست مقالات دانشنامه عقیله {toFa(filteredArticles.length)} مقاله مستقل
مدیریت ترجمه ۵ زبانه در یک ص
{isLoading ? (

در حال بارگذاری بانک مقالات دانشنامه...

) : filteredArticles.length === 0 ? (

هیچ مقاله‌ای با این فیلترها یافت نشد.

با کلیک بر روی «افزودن مقاله جدید» دانشنامه را کامل کنید.

) : (
{filteredArticles.map((article) => { const langs = article.available_languages || [] return ( {/* شناسه */} {/* کاور + عنوان (+ نشان ویژه) + نامک سئو */} {/* وضعیت زبان‌ها و ترجمه‌های مقاله */} {/* وضعیت انتشار عمومی کل مقاله */} {/* تاریخ ثبت */} {/* عملیات */} ) })}
شناسه عنوان و تصویر شاخص زبان‌های ترجمه‌شده وضعیت انتشار تاریخ ایجاد عملیات
#{article.id}
{article.cover ? ( {article.title} { ;(e.currentTarget as HTMLElement).style.display = 'none' }} /> ) : (
)}
{article.is_top_list && ( ویژه )}

/blog/{article.base_slug || article.group_key}

{LANGUAGE_CONFIG.map((lang) => { const hasTranslation = langs.includes(lang.code) return ( ) })}
{article.created_at ? formatDualDate(article.created_at, language as any) : '—'}
)}
{/* ۴. دیالوگ یکپارچه مدیریت ۵ زبانه مقاله (همگی در یک ص هندل میشن) */} { setIsModalOpen(open) }} > {/* Header */}
{editingArticleId ? 'ویرایش مقاله و مدیریت ترجمه‌های ۵ زبانه' : 'افزودن مقاله دانشنامه جدید (۵ زبانه)'} یک مقاله ساخته می‌شود؛ اگر در یک زبان ترجمه داشته باشد در وب‌سایت آن زبان نمایش داده می‌شود، در غیر این‌صورت نمایش داده نخواهد شد.
{formBaseSlug && ( /blog/{formBaseSlug} )}
{/* Body */} {/* ۱. بخش مشخصات مشترک بین همه زبان‌ها (Common settings) */}
{/* نامک اسلاگ پایه */}
setFormBaseSlug(e.target.value)} placeholder="najaf-holy-sites-guide" dir="ltr" className="bg-card border-border-soft rounded-xl text-xs font-mono h-9" />
{/* آدرس تصویر شاخص */}
setFormCover(e.target.value)} placeholder="https://... یا /media/blog/cover/..." dir="ltr" className="bg-card border-border-soft rounded-xl text-xs font-mono h-9 flex-1" /> {formCover && ( کاور )}
{/* وضعیت انتشار عمومی و مقاله ویژه */}

انتشار عمومی در وب‌سایت

وضعیت فعال در کل زبان‌ها

نمایش در مقالات ویژه (Top List)

برجسته کردن در بالای وبلاگ

{/* ۲. تب‌های انتخاب زبان ترجمه (۵ زبانه) */}
دارای محتوا فاقد ترجمه (عدم نمایش در وب این زبان)
{LANGUAGE_CONFIG.map((lang) => { const isSelected = activeFormLang === lang.code const hasData = Boolean(formTranslations[lang.code]?.title?.trim()) return ( ) })}
{/* ۳. فرم ورود محتوا برای زبان فعال */}
{currentLangConfig.flag} محتوای مقاله به زبان {currentLangConfig.label} {currentLangConfig.dir.toUpperCase()}
{!currentTranslation.title.trim() && ( (در صورت خالی ماندن، مقاله در نسخه {currentLangConfig.label} نمایش داده نمی‌شود) )}
{/* عنوان مقاله در این زبان */}
setFormTranslations({ ...formTranslations, [activeFormLang]: { ...currentTranslation, title: e.target.value }, }) } dir={currentLangConfig.dir} placeholder={`عنوان مقاله به زبان ${currentLangConfig.label}...`} className="bg-surface-base border-border-soft rounded-xl text-sm" />
{/* خلاصه متای سئو */}
{toFa(currentTranslation.summary?.length || 0)} / ۱۶۰ کاراکتر