138 lines
5.9 KiB
TypeScript
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 />
|
|
</>
|
|
)
|
|
}
|