مرکز اعلان (NotificationCenter)
مرکز اعلان مبتنی بر Popover با نشان شمارنده، severity رنگبندی، فیلترهای تب، و عملیات خواندهشده/بستن
معرفی
NotificationCenter یک دکمهی زنگ با نشان شمارندهی اعلانهای خواندهنشده است. کلیک روی آن یک Popover باز میکند که لیست اعلانها را با ترتیب زمانی و دستهبندی severity نشان میدهد. کاربر میتواند هر اعلان را خواندهشده علامت بزند، ببندد، یا با کلیک روی متن اعلان به مقصد هدایت شود.
چه زمانی استفاده کنیم:
- در App Shell (نزدیک منوی کاربر) برای آگاهسازی از هشدارها، بولتنهای جدید، تکمیل job، و خطاهای سیستمی
- برای صف هشدارهای افکارسنجی (مثل «گسترش ناگهانی پست بحرانی» یا «کاهش دقت کراولر»)
- همراه با
Toast(Sonner): توست برای اعلام لحظهای، NotificationCenter برای تاریخچه
چه زمانی استفاده نکنیم:
- برای نمایش یک پیام مهم دائمی در بالای صفحه — از
Bannerاستفاده کنید - برای پیام درونفرمی (خطای فیلد، توضیح context) — از
AlertیاCalloutاستفاده کنید - برای تأیید destructive — از
AlertDialog/ConfirmDialogاستفاده کنید
زمین بازی
با تغییر تنظیمات زیر، پیشنمایش زنده را مشاهده کنید.
استفاده
'use client'
import * as React from 'react'
import { NotificationCenter, type NotificationItem } from '@parto-system-design/ui'
function Header() {
const [items, setItems] = React.useState<NotificationItem[]>([
{
id: 'n1',
title: 'هشدار گسترش پست بحرانی',
body: 'پست خوشه «رونمایی محصول جدید» در ۲۰ دقیقه اخیر ۳۴۰۰ بازنشر گرفت.',
timestamp: new Date(Date.now() - 3 * 60 * 1000),
severity: 'critical',
read: false,
action: { label: 'باز کردن خوشه', onClick: () => router.push('/clusters/42') },
},
])
return (
<NotificationCenter
items={items}
onMarkRead={(id) => setItems((p) => p.map((i) => (i.id === id ? { ...i, read: true } : i)))}
onMarkAllRead={() => setItems((p) => p.map((i) => ({ ...i, read: true })))}
onDismiss={(id) => setItems((p) => p.filter((i) => i.id !== id))}
/>
)
}severity و رنگبندی
پنج نوع severity پشتیبانی میشود. هر severity یک رنگ خطحاشیهی شروع + رنگ نقطه/آیکون اختصاصی دارد:
| severity | توکن منبع | مورد استفاده |
|---|---|---|
info | --brand-default | پیشفرض — اطلاعرسانی عادی (گزارش آماده، کاربر جدید) |
success | --sentiment-positive | تکمیل موفقیتآمیز یک job (تحلیل کامنت تکمیل شد) |
warning | --warning-default | هشدارهای غیر-بحرانی (کاهش دقت، نزدیک شدن به quota) |
destructive | --destructive-default | خطای سیستم، از دست رفتن اتصال |
critical | --status-critical | بحران دامنهای افکارسنجی (گسترش ناگهانی پست، شکست تهدیدات) |
فیلترهای تب
پنل میتواند چند تب فیلتر داشته باشد که کاربر با کلیک بینشان جابهجا میشود:
<NotificationCenter
items={items}
filters={[
{ key: 'all', label: 'همه', predicate: () => true },
{ key: 'unread', label: 'خواندهنشده', predicate: (i) => !i.read },
{ key: 'critical', label: 'بحرانی', predicate: (i) => i.severity === 'critical' },
]}
defaultFilter="unread"
/>overflow نشان شمارنده
اگر تعداد خواندهنشدهها از ۹۹ عبور کند، نشان به +۹۹ (یا 99+ در انگلیسی) تبدیل میشود تا width ثابت بماند:
ادغام با Toast
الگوی رایج: اعلانهای لحظهای با toast() نمایش داده میشوند، و همزمان به state اپ اضافه میشوند تا در NotificationCenter بمانند.
import { toast } from 'sonner'
function emit(item: NotificationItem) {
setNotifications((p) => [item, ...p])
toast(item.title, { description: item.body })
}Props
NotificationItem
راهنمای استفاده
بکنید
- state آیتمها را در store مرکزی اپ نگه دارید (zustand/redux/context). NotificationCenter presentational است و داده
را نمیسازد - برای اعلانهای real-time،
toast()را برای لحظهای + افزودن به state برای تاریخچه همزمان اجرا کنید - در اپهای چندبخشی، ازfiltersبرای تفکیک منبع اعلان (سیستم / افکارسنجی / عملیات) استفاده کنید - برای اعلانهای پایدار که کاربر نباید حذفشان کند (مثل گزارش امضاءشده)،onDismissرا نپاس کنید
نکنید
- بیش از ۵۰ اعلان را یکجا render نکنید — اعلانهای قدیمی را به صفحهی «تاریخچه» ببرید - severity را به دلخواه استفاده نکنید — هر severity معنای مشخص دارد - NotificationCenter را درون مودال یا Drawer قرار ندهید — جایگاه قراردادی آن در Header است - برای نمایش وضعیت online/offline از severity استفاده نکنید — این یک پیام کوتاه، نه تاریخچهای است
دسترسیپذیری
- دکمهی زنگ
aria-labelدارد که در حالت وجود اعلان خواندهنشده شامل تعداد آنهاست - نشان شمارنده
aria-hidden="true"است چون اطلاعات آن در aria-label دکمه قرار دارد - تبهای فیلتر
role="tablist"+role="tab"+aria-selectedدارند - زمان هر اعلان با
<time datetime="...">markup شده تا assistive tech آن را درست بخواند - تمرکز صفحهکلید با Tab روی دکمهی زنگ → Enter برای باز کردن → Tab درون پنل
- Escape پنل را میبندد (از
Popoverprimitive)
کامپوننتهای مرتبط
- Banner — برای پیام مهم دائمی در بالای صفحه
- Alert — برای پیام درونصفحهای (context، خطای فرم)
- Sonner — برای اعلان لحظهای گذرا
- AlertDialog — برای تأیید destructive