Files
aramesh-backend/frontend/src/pages/Blog.tsx
T

138 lines
5.9 KiB
TypeScript

import React from 'react'
import { Link } from 'react-router-dom'
import { ArrowLeftIcon, ClockIcon } from 'lucide-react'
import { PageHero } from '../components/PageHero'
import { Reveal } from '../components/Reveal'
import { ArticleCard } from '../components/ArticleCard'
import { Newsletter } from '../components/home/Newsletter'
import { useSite } from '../api'
import { useLang } from '../i18n/lang'
import { EditableItem } from '../admin/Editable'
import { useAdmin } from '../admin/AdminContext'
import { formatJalali } from '../utils/date'
import { toPersianDigits } from '../utils/digits'
import { PlusIcon } from 'lucide-react'
function DraftBadge({ className = '' }: { className?: string }) {
const { t } = useLang()
return (
<span
className={`border border-amber-400/60 bg-ink/90 px-2 py-1 text-xs font-bold text-amber-300 ${className}`}
>
{t('draft')}
</span>
)
}
export function Blog() {
const { articles } = useSite()
const { t, href, lang } = useLang()
const { isEditMode } = useAdmin()
// دکمه «نوشتن مقاله جدید» فقط در حالت ویرایش ادمین دیده می‌شود
const writeButton = isEditMode ? (
<Link
to="/admin/write"
data-admin-link
className="inline-flex items-center gap-2 bg-copper px-6 py-3 text-sm font-bold text-char transition-transform duration-200 hover:scale-105"
>
<PlusIcon className="h-4 w-4" />
{t('writeNewArticle')}
</Link>
) : null
// حالت خالی — وبلاگ بدون مقاله نباید کرش کند
if (articles.length === 0) {
return (
<>
<PageHero pageKey="blog" />
<section className="bg-ink py-24 lg:py-32" aria-label={t('articlesAria')}>
<div className="mx-auto max-w-site px-6 text-center lg:px-10">
<h2 className="text-3xl font-bold text-ivory">{t('blogEmpty')}</h2>
<p className="mt-4 text-sm font-light text-ivory/60">
{t('blogEmptyText')}
</p>
<div className="mt-8 flex justify-center">{writeButton}</div>
</div>
</section>
<Newsletter />
</>
)
}
const [featured, ...rest] = articles
return (
<>
<PageHero pageKey="blog" />
<section className="bg-ink py-24 lg:py-32" aria-label={t('articlesAria')}>
<div className="mx-auto max-w-site px-6 lg:px-10">
<div className="mb-10 flex justify-start">{writeButton}</div>
<Reveal>
<EditableItem resource="articles" item={featured} editHref={`/admin/write/${featured.id}`}>
<Link
to={href(`/blog/${featured.slug}`)}
className="group grid items-stretch border border-ivory/10 bg-char transition-[transform,box-shadow,border-color] duration-300 ease-lux hover:-translate-y-2 hover:border-copper/50 hover:shadow-lift lg:grid-cols-2"
aria-label={`${t('readArticlePrefix')} ${featured.title}`}
>
<div className="overflow-hidden">
<img
src={featured.image}
alt={`${t('articleImagePrefix')} ${featured.title}`}
className="h-full min-h-[20rem] w-full object-cover transition-transform duration-500 ease-lux group-hover:scale-105" fetchPriority="high" decoding="async"
/>
</div>
<div className="flex flex-col justify-center p-8 lg:p-12">
<p className="text-xs font-light text-copper">
{t('featuredArticle')} — {featured.category}
{featured.status === 'draft' && (
<DraftBadge className="inline-block align-middle ltr:ml-3 rtl:mr-3" />
)}
</p>
<h2 className="mt-6 text-2xl font-bold leading-[2.2] md:leading-[2.2] text-ivory transition-colors duration-200 ease-lux group-hover:text-copper md:text-3xl">
{featured.title}
</h2>
<p className="mt-6 text-sm font-light leading-10 text-ivory/65">{featured.excerpt}</p>
<div className="mt-10 flex items-center justify-between border-t border-ivory/10 pt-8 text-xs font-light text-ivory/50">
<span className="flex items-center gap-4">
<span>{lang === 'en'
? new Intl.DateTimeFormat('en-GB', { day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(featured.date))
: formatJalali(featured.date)}</span>
<span className="flex items-center gap-1.5">
<ClockIcon className="h-3.5 w-3.5" />
{lang === 'en' ? featured.read : toPersianDigits(featured.read)}
</span>
</span>
<span className="flex items-center gap-2 text-copper">
{t('readMore')}
<ArrowLeftIcon className="h-3.5 w-3.5 ltr:-scale-x-100 transition-transform duration-200 ease-lux rtl:group-hover:-translate-x-1 ltr:group-hover:translate-x-1" />
</span>
</div>
</div>
</Link>
</EditableItem>
</Reveal>
<div className="mt-12 grid gap-6 lg:grid-cols-3">
{rest.map((article, i) => (
<Reveal key={article.id} delay={0.05 * i} className="h-full">
<EditableItem resource="articles" item={article} className="h-full" editHref={`/admin/write/${article.id}`}>
<div className="relative h-full">
{article.status === 'draft' && (
<DraftBadge className="absolute top-3 ltr:left-3 rtl:right-3 z-10" />
)}
<ArticleCard article={article} />
</div>
</EditableItem>
</Reveal>
))}
</div>
</div>
</section>
<Newsletter />
</>
)
}