fix: add news link to admin panel, prevent empty news section flash

- Add News item to marketing group in AdminPanel with NewspaperIcon
- Add news nav/panel translations for all 4 locales (ru, en, zh, fa)
- Don't render NewsSection until query confirms articles exist
- Remove skeleton that caused brief flash when news list is empty
This commit is contained in:
Fringg
2026-03-23 11:21:25 +03:00
parent 74e6d52fee
commit 38b0f4be9a
6 changed files with 37 additions and 30 deletions

View File

@@ -427,8 +427,9 @@ export default function NewsSection() {
if (featuredSlug) handleCardClick(featuredSlug); if (featuredSlug) handleCardClick(featuredSlug);
}, [featuredSlug, handleCardClick]); }, [featuredSlug, handleCardClick]);
// Don't render if no news and not loading // Don't render until we know there are news items.
if (!isLoading && items.length === 0) { // This prevents the skeleton from briefly flashing when there are no articles.
if (items.length === 0) {
return null; return null;
} }
@@ -463,27 +464,8 @@ export default function NewsSection() {
)} )}
</motion.div> </motion.div>
{/* Loading skeleton */}
{isLoading && (
<div className="grid gap-4 sm:grid-cols-2">
{Array.from({ length: 3 }).map((_, i) => (
<div
key={i}
className={cn(
'animate-pulse rounded-2xl bg-dark-900 p-7',
i === 0 && 'col-span-full',
)}
>
<div className="mb-4 h-4 w-24 rounded bg-dark-800" />
<div className="mb-3 h-6 w-3/4 rounded bg-dark-800" />
<div className="h-4 w-1/2 rounded bg-dark-800" />
</div>
))}
</div>
)}
{/* Grid */} {/* Grid */}
{!isLoading && items.length > 0 && ( {items.length > 0 && (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
{featured && <FeaturedCard item={featured} onClick={handleFeaturedClick} />} {featured && <FeaturedCard item={featured} onClick={handleFeaturedClick} />}
{regular.map((item, i) => ( {regular.map((item, i) => (

View File

@@ -1110,7 +1110,8 @@
"auditLog": "Audit Log", "auditLog": "Audit Log",
"salesStats": "Sales Statistics", "salesStats": "Sales Statistics",
"landings": "Landings", "landings": "Landings",
"referralNetwork": "Referral Network" "referralNetwork": "Referral Network",
"news": "News"
}, },
"panel": { "panel": {
"title": "Admin Panel", "title": "Admin Panel",
@@ -1145,7 +1146,8 @@
"auditLogDesc": "Review system activity and access history", "auditLogDesc": "Review system activity and access history",
"salesStatsDesc": "Sales analytics and trends", "salesStatsDesc": "Sales analytics and trends",
"landingsDesc": "Quick purchase landing pages", "landingsDesc": "Quick purchase landing pages",
"referralNetworkDesc": "Referral graph and campaign visualization" "referralNetworkDesc": "Referral graph and campaign visualization",
"newsDesc": "Manage news articles and publications"
}, },
"salesStats": { "salesStats": {
"title": "Sales Statistics", "title": "Sales Statistics",

View File

@@ -944,7 +944,8 @@
"auditLog": "گزارش بازرسی", "auditLog": "گزارش بازرسی",
"salesStats": "آمار فروش", "salesStats": "آمار فروش",
"landings": "صفحات فرود", "landings": "صفحات فرود",
"referralNetwork": "شبکه ارجاع" "referralNetwork": "شبکه ارجاع",
"news": "اخبار"
}, },
"panel": { "panel": {
"title": "پنل مدیریت", "title": "پنل مدیریت",
@@ -979,7 +980,8 @@
"auditLogDesc": "بررسی فعالیت سیستم و تاریخچه دسترسی", "auditLogDesc": "بررسی فعالیت سیستم و تاریخچه دسترسی",
"salesStatsDesc": "تحلیل فروش و روندها", "salesStatsDesc": "تحلیل فروش و روندها",
"landingsDesc": "صفحات خرید سریع", "landingsDesc": "صفحات خرید سریع",
"referralNetworkDesc": "نمایش بصری گراف ارجاعات و کمپین‌ها" "referralNetworkDesc": "نمایش بصری گراف ارجاعات و کمپین‌ها",
"newsDesc": "مدیریت مقالات خبری و انتشارات"
}, },
"salesStats": { "salesStats": {
"title": "آمار فروش", "title": "آمار فروش",

View File

@@ -1131,7 +1131,8 @@
"auditLog": "Журнал аудита", "auditLog": "Журнал аудита",
"salesStats": "Статистика продаж", "salesStats": "Статистика продаж",
"landings": "Лендинги", "landings": "Лендинги",
"referralNetwork": "Реферальная сеть" "referralNetwork": "Реферальная сеть",
"news": "Новости"
}, },
"panel": { "panel": {
"title": "Панель администратора", "title": "Панель администратора",
@@ -1166,7 +1167,8 @@
"auditLogDesc": "Просмотр активности системы и истории доступа", "auditLogDesc": "Просмотр активности системы и истории доступа",
"salesStatsDesc": "Аналитика продаж и тренды", "salesStatsDesc": "Аналитика продаж и тренды",
"landingsDesc": "Страницы быстрой покупки", "landingsDesc": "Страницы быстрой покупки",
"referralNetworkDesc": "Визуализация графа рефералов и РК" "referralNetworkDesc": "Визуализация графа рефералов и РК",
"newsDesc": "Управление новостями и статьями"
}, },
"salesStats": { "salesStats": {
"title": "Статистика продаж", "title": "Статистика продаж",

View File

@@ -944,7 +944,8 @@
"auditLog": "审计日志", "auditLog": "审计日志",
"salesStats": "销售统计", "salesStats": "销售统计",
"landings": "落地页", "landings": "落地页",
"referralNetwork": "推荐网络" "referralNetwork": "推荐网络",
"news": "新闻"
}, },
"panel": { "panel": {
"title": "管理面板", "title": "管理面板",
@@ -979,7 +980,8 @@
"auditLogDesc": "查看系统活动和访问历史", "auditLogDesc": "查看系统活动和访问历史",
"salesStatsDesc": "销售分析与趋势", "salesStatsDesc": "销售分析与趋势",
"landingsDesc": "快速购买落地页", "landingsDesc": "快速购买落地页",
"referralNetworkDesc": "推荐图表和广告活动可视化" "referralNetworkDesc": "推荐图表和广告活动可视化",
"newsDesc": "管理新闻文章和出版物"
}, },
"salesStats": { "salesStats": {
"title": "销售统计", "title": "销售统计",

View File

@@ -259,6 +259,16 @@ const NetworkGraphIcon = () => (
</svg> </svg>
); );
const NewspaperIcon = () => (
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 7.5h1.5m-1.5 3h1.5m-7.5 3h7.5m-7.5 3h7.5m3-9h3.375c.621 0 1.125.504 1.125 1.125V18a2.25 2.25 0 01-2.25 2.25M16.5 7.5V18a2.25 2.25 0 002.25 2.25M16.5 7.5V4.875c0-.621-.504-1.125-1.125-1.125H4.125C3.504 3.75 3 4.254 3 4.875V18a2.25 2.25 0 002.25 2.25h13.5M6 7.5h3v3H6v-3z"
/>
</svg>
);
const ChevronRightIcon = () => ( const ChevronRightIcon = () => (
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" /> <path strokeLinecap="round" strokeLinejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
@@ -538,6 +548,13 @@ export default function AdminPanel() {
iconBg: 'bg-error-500/20', iconBg: 'bg-error-500/20',
iconColor: 'text-error-400', iconColor: 'text-error-400',
items: [ items: [
{
to: '/admin/news',
icon: <NewspaperIcon />,
title: t('admin.nav.news'),
description: t('admin.panel.newsDesc'),
permission: 'news:read',
},
{ {
to: '/admin/campaigns', to: '/admin/campaigns',
icon: <MegaphoneIcon />, icon: <MegaphoneIcon />,