پرتوپرتو

بنر محدودیت نرخ (RateLimitBanner)

Banner برای هشدار فعال بودن rate-limit — شمارش معکوس live، دکمه retry اختیاری، و ترکیب با actionType برای context.

معرفی

RateLimitBanner به کاربر اعلام می‌کند که یک عملیات در حال حاضر توسط پلتفرم rate-limit شده و باید تا زمان مشخصی صبر کرد. شمارش معکوس live (هر ثانیه به‌روز می‌شود) + دکمه retry اختیاری + context از ActionTypeKey.

برخلاف QuotaProgressBar که «نزدیک شدن به سقف» را نشان می‌دهد، این banner بعد از برخورد با سقف می‌آید: «پلتفرم گفت صبر کن، X دقیقه دیگر تلاش کن.»

چه زمانی استفاده کنیم:

  • وقتی backend گزارش می‌دهد worker با rate-limit از سمت Instagram مواجه شده
  • در داشبورد وقتی یک عملیات متوقف شده و resumeAt مشخص است
  • بالای کارت worker یا در top-of-page برای هشدار جدی
  • در admin panel برای نمایش rate-limit سمت API

چه زمانی استفاده نکنیم:

  • برای هشدار فقط «نزدیک به سقف» — از QuotaProgressBar استفاده کنید
  • برای خطاهای عمومی (نه rate-limit) — از Banner یا Alert استفاده کنید
  • برای toast موقت — از sonner استفاده کنید

زمین بازی

با تغییر تنظیمات زیر، پیش‌نمایش زنده را مشاهده کنید.

زمین بازی
تنظیمات
محتوا
کد این نمونه به‌صورت خودکار قابل تولید نیست — برای کد آماده‌ی copy/paste به بخش «استفاده» در بالای صفحه مراجعه کنید.

استفاده

'use client'
import { RateLimitBanner } from '@parto-system-design/ui'

export function WorkerFeed({ worker }) {
  if (!worker.rateLimit?.isActive) return null
  return (
    <RateLimitBanner
      resumeAt={worker.rateLimit.resumeAt}
      actionType="like"
      reason="پلتفرم نرخ پسند این حساب را موقتاً کاهش داده است."
      onRetryNow={() => retryWorker(worker.id)}
    />
  )
}

شمارش معکوس live

countdown هر ثانیه به‌روز می‌شود:

با live={true} (پیش‌فرض)، یک setInterval هر ثانیه state را re-render می‌کند و formatTimeRemaining دوباره محاسبه می‌کند. در صفحاتی که banner طولانی مدت باز می‌ماند، این اطلاعات دقیق به کاربر می‌دهد.

اگر نیاز به performance بالا دارید یا در تست‌ها، live={false} کنید.

ترکیب با actionType

با actionType، عنوان به «محدودیت نرخ فعال — پسندیدن» (مثلاً) تغییر می‌کند و data-action-type روی root می‌آید — برای CSS یا telemetry.

variant warning (کمتر جدی)

برای سناریوهایی که rate-limit موقت و کم‌خطر است (مثلاً backend gateway با ۴۰ req/h)، variant="warning" رنگ کهربایی می‌دهد به جای قرمز.

با دکمه retry

کلیک روی «تلاش مجدد» تعداد را افزایش می‌دهد: 0

وقتی onRetryNow تنظیم شود، دکمه «تلاش مجدد» در سمت end banner ظاهر می‌شود. کاربر می‌تواند override کند و سعی کند قبل از پایان cooldown.

Props

Prop

Type

helper formatTimeRemaining

در کنار کامپوننت، تابع formatTimeRemaining(target, locale) هم export می‌شود — اگر خودتان می‌خواهید countdown سفارشی بسازید:

import { formatTimeRemaining } from '@parto-system-design/ui'

formatTimeRemaining(new Date('2026-04-25T10:00:00Z'), 'fa')
// → "۲ ساعت و ۳۰ دقیقه دیگر"
formatTimeRemaining(Date.now() + 45_000, 'en')
// → "in 45s"

راهنمای استفاده

بکنید

  • وقتی پلتفرم/backend صراحتاً rate-limit برگرداند و resumeAt مشخص است این banner را نمایش دهید — کاربر منتظر چه زمانی می‌ماند را دقیق می‌فهمد - برای rate-limit جدی (block پلتفرم) از variant="destructive" و برای gateway موقت از variant="warning" استفاده کنید - وقتی retry override واقعاً شدنی است (مثل proxy جدید) onRetryNow بدهید؛ در غیر این صورت دکمه را پاس ندهید تا کاربر صبر کند - با actionType context را غنی کنید — کاربر می‌فهمد محدودیت روی like است نه comment

نکنید

  • برای خطاهای transient (network blip, 500) از این banner استفاده نکنید — از Sonner toast استفاده کنید - برای هشدار «نزدیک به سقف» (نه برخورد به سقف) از این استفاده نکنید — از QuotaProgressBar مکمل استفاده کنید - چندین RateLimitBanner را در یک صفحه انباشت نکنید — اولویت‌بندی کنید و فقط جدی‌ترین rate-limit را نمایش دهید - live={true} را در صفحاتی که چند banner هم‌زمان دارند روی همه فعال نکنید — هر کدام setInterval جداگانه می‌سازد

دسترسی‌پذیری

  • Banner از <Banner> زیرین استفاده می‌کند که role="banner" دارد — landmark در landmark tree ساخته می‌شود.
  • عنوان + دلیل + countdown همگی متن هستند؛ screen reader ترتیب خوانایی درستی دارد.
  • دکمه retry یک <button type="button"> استاندارد با focus ring است.
  • در حالت dismissible, دکمه × یک button با aria-label="Dismiss" است.
  • countdown با data-slot="rate-limit-countdown" markup شده — وقتی live={true} هر ثانیه به‌روز می‌شود ولی screen reader re-announce نمی‌کند (هنوز live region نیست؛ برای جلوگیری از اسپم). اگر نیاز به اعلان هر دقیقه داشتید، aria-live="polite" را به عنوان override اضافه کنید.
  • motion-safe — countdown با react state هست، نه CSS animation.

کامپوننت‌های مرتبط

  • QuotaProgressBar — هشدار «نزدیک به سقف»؛ این banner مکمل است برای «بعد از سقف»
  • Banner — banner عمومی؛ این کامپوننت روی آن بنا شده
  • Alert — alert سطح component (نه صفحه) برای خطاهای غیر-rate-limit
  • ActionTypeChip — نمایش نوع عملیات به‌صورت chip جداگانه