پرتوپرتو

بنر محدودیت نرخ (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 '@partodata/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 طولانی مدت باز می‌ماند، این اطلاعات دقیق به کاربر می‌دهد. اگر کاربر prefers-reduced-motion: reduce فعال کرده باشد، این tick به‌جای هر ثانیه به هر ۳۰ ثانیه کند می‌شود تا از churn بصری کم شود بدون اینکه شمارش معکوس از دست برود.

اگر نیاز به 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 '@partodata/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="بستن" است (این مقدار در Banner زیرین hardcode شده و locale-aware نیست).
  • 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؛ و در prefers-reduced-motion: reduce نرخ به‌روزرسانی از هر ۱ ثانیه به هر ۳۰ ثانیه کند می‌شود.

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

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