پرتوپرتو

کارت کار (JobCard)

کارت canonical برای نمایش کار async — تحلیل، کمپین، ارزیابی، ایمپورت — با وضعیت، پیشرفت، متادیتا، آمار و خطا. Composition-based و status-aware.

معرفی

JobCard یک الگوی یکپارچه برای نمایش هر کار async در سیستم است — از تحلیل کامنت گرفته تا کمپین بوستر، ارزیابی پیج، ایمپورت اکسل، تولید گزارش. وضعیت (status) به‌صورت context cascade می‌شود تا badge/progress/color خودکار با lifecycle هم‌خوان بمانند.

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

  • نمایش لیست «کارهای من» / «تحلیل‌ها» / «کمپین‌ها» در داشبورد
  • ردیف‌های فعالیت اخیر با status + progress
  • کارت‌های موجود در صف اجرا یا در حال اجرا که کاربر باید وضعیت‌شان را ببیند
  • وقتی «کار» معنای domain دارد: entity‌ای با lifecycle (queued → running → completed/failed)

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

  • صرفاً نمایش content (پست، پروفایل، محتوا) — از PostCard یا Card استفاده کنید
  • متریک تک‌عدد با trend — از MetricCard استفاده کنید
  • کارت توضیحی بدون status — از Card سطح پایین استفاده کنید
  • وقتی lifecycle ۶ حالت پیش‌فرض (queued/running/paused/completed/failed/cancelled) برای دامنه کافی نیست — مدل سفارشی خودتان را بسازید

تحلیل کامنت‌های پست شماره ۱۲۳

۱٬۲۵۰ کامنت از اینستاگرام · برچسب‌زنی خودکار

در حال اجرا
در حال برچسب‌زنی کامنت‌ها۶۵٪
شروع
امروز، ۱۴:۳۲
مدت
۱۲ دقیقه
توسط
سارا احمدی
کامنت‌ها۱۲۵۰
برچسب‌خورده۸۲۰ / ۱۲۵۰
بدون برچسب۴۳۰

زمین بازی

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

زمین بازی

تحلیل گفت‌وگوهای کمپین

۲۴ ساعت گذشته

در حال اجرا
۶۴٪
پلتفرم
توییتر
پست‌ها
۱٬۲۰۰
بازه
۷ روز
تنظیمات
حالت
ظاهر
محتوا
داده
64
کد این نمونه به‌صورت خودکار قابل تولید نیست — برای کد آماده‌ی copy/paste به بخش «استفاده» در بالای صفحه مراجعه کنید.

استفاده

'use client'
import {
  JobCard,
  JobCardHeader,
  JobCardThumbnail,
  JobCardHeaderText,
  JobCardTitle,
  JobCardSubtitle,
  JobCardHeaderActions,
  JobCardStatusBadge,
  JobCardActions,
  JobCardProgress,
  JobCardMeta,
  JobCardMetaItem,
  JobCardStats,
  JobCardStat,
  Button,
} from '@parto-system-design/ui'
import { Brain, Calendar, Clock, MoreHorizontal } from 'lucide-react'

export function AnalysisJob({ job }) {
  return (
    <JobCard status={job.status}>
      <JobCardHeader>
        <JobCardThumbnail icon={Brain} />
        <JobCardHeaderText>
          <JobCardTitle>{job.title}</JobCardTitle>
          <JobCardSubtitle>{job.subtitle}</JobCardSubtitle>
        </JobCardHeaderText>
        <JobCardHeaderActions>
          <JobCardStatusBadge />
          <JobCardActions>
            <Button variant="ghost" size="xs" className="size-7 p-0">
              <MoreHorizontal className="size-4" />
            </Button>
          </JobCardActions>
        </JobCardHeaderActions>
      </JobCardHeader>

      <JobCardProgress value={job.progress} label={job.progressLabel} />

      <JobCardMeta>
        <JobCardMetaItem icon={Calendar} label="شروع" value={job.startedAt} />
        <JobCardMetaItem icon={Clock} label="مدت" value={job.duration} />
      </JobCardMeta>

      <JobCardStats>
        <JobCardStat label="کامنت‌ها" value={job.commentCount} />
        <JobCardStat label="برچسب‌خورده" value={job.labeled} total={job.commentCount} variant="positive" />
      </JobCardStats>
    </JobCard>
  )
}

Sub-componentها با context به status والد دسترسی دارند — JobCardStatusBadge و JobCardProgress رنگ/آیکون/انیمیشن را خودکار از وضعیت می‌گیرند.

شش وضعیت canonical

JobStatusKey: queued | running | paused | completed | failed | cancelled

ایمپورت نظرات از فایل اکسل

۳٬۴۰۰ ردیف · در انتظار worker

در صف
ایجاد
۲ دقیقه پیش
توسط
علی رضایی

ارزیابی ۱۲ پیج پورتفوی ورزشی

به‌دلیل rate-limit اینستاگرام متوقف شد

متوقف
در حال واکشی پروفایل‌ها۴۲٪
مدت اجرا
۸ دقیقه
ادامه در
۱۸ دقیقه

تحلیل کامنت‌های پست شماره ۱۲۲

۸۷۰ کامنت تحلیل شد · گزارش آماده است

تکمیل شد
کل۸۷۰
مثبت۳۱۲
منفی۱۹۸
خنثی۳۶۰

بوستر: ۴۵۰ لایک روی پست #۸۹۲

۱۲ worker · در دقیقه ۳ ناموفق شد

ناموفق
پیشرفت در لحظه‌ی توقف۱۸٪
  • queued — هنوز شروع نشده. progress پنهان است. رنگ خنثی.
  • running — در حال اجرا. spinner روی badge، dot نبضی. Progress اگر تنظیم شود نمایش داده می‌شود.
  • paused — به دلیل rate-limit یا دستی. Progress با رنگ warning باقی می‌ماند.
  • completed — تمام شد. معمولاً Progress حذف می‌شود و Stats نتیجه را نشان می‌دهد.
  • failed — خطا. Badge قرمز، JobCardError معمولاً زیر آن می‌آید.
  • cancelled — کاربر لغو کرد. Progress پنهان است، رنگ خاموش.

کمک‌تابع isActiveJobStatus(status) برای فیلتر کردن کارهای فعال (غیر terminal) صادر می‌شود.

حالت compact برای لیست

تحلیل کامنت‌های کانال اخبار

۳٬۲۰۰ کامنت

ارزیابی ۸ پیج تبلیغاتی

در صف اجرا

بوستر: فالو روی ۲۵۰ پیج

همه workerها سالم

ایمپورت اکسل ۵٬۰۰۰ ردیفی

خطا در ستون دوم

  • compact — ردیف افقی یک‌سطری مناسب لیست/inbox
  • interactive — کل کارت قابل کلیک با focus ring و Space/Enter keyboard
  • در حالت compact سعی کنید Header را تنها سیتم اصلی بگذارید؛ Progress/Stats را حذف کنید تا فشرده بماند

progress نامعین (indeterminate)

واکشی پروفایل از اینستاگرام

زمان تخمینی نامشخص — منتظر پاسخ API

در حال اجرا
در حال اتصال…

وقتی کار در حال انجام است ولی درصد پیشرفت مشخص نیست (مثل «منتظر پاسخ API»)، از indeterminate استفاده کنید. shimmer از start به end حرکت می‌کند و با prefers-reduced-motion ساکن می‌شود.

thumbnail با تصویر

پست اینستاگرام

تحلیل کامنت‌های پست کافه لاکچری

@cafe_lux · ۵٬۳۰۰ کامنت

تکمیل شد
موافق کمپین۳٫۴۲K
مخالف۶۸۰
خنثی۱٫۲K
  • src + alt برای تصویر (مثل preview پست)
  • icon برای آیکون Lucide
  • fallback برای حروف اول / emoji
  • پیش‌فرض sizing خودکار بر اساس compact

ترکیب با FilterPanel برای صفحه Jobs

در صفحه‌ی مدیریت کارها (مثل «تحلیل‌های من»)، الگوی کامل معمولاً این شکل است:

<div className="flex gap-6">
  <aside className="w-72">
    <FilterPanel>
      <FilterPanelHeader>
        <FilterPanelTitle activeCount={activeCount} />
      </FilterPanelHeader>
      <FilterPanelBody>
        <FilterSection title="وضعیت">{/* checkboxes for JobStatusKey */}</FilterSection>
        <FilterSection title="بازه زمانی">{/* DateRangePicker */}</FilterSection>
      </FilterPanelBody>
    </FilterPanel>
  </aside>
  <main className="flex-1 flex flex-col gap-3">
    {jobs.map((job) => (
      <JobCard key={job.id} status={job.status} interactive onClick={() => open(job.id)}>
        {/* ... */}
      </JobCard>
    ))}
  </main>
</div>

Props

JobCard

Prop

Type

JobCardThumbnail

Prop

Type

JobCardStatusBadge

Prop

Type

JobCardProgress

Prop

Type

JobCardStat

Prop

Type

JobCardMetaItem

Prop

Type

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

بکنید

  • وضعیت‌های six‌گانه را به دامنه‌ی خودتان map کنید: CommentSanj pending_reviewrunning با label اختصاصی، completed همان completed. lifecycle خود را با این six state مدل کنید نه برعکس - در حالت completed ارز کاربردی را روی JobCardStats بگذارید، نه progress - در حالت failed همیشه JobCardError بیافزایید تا کاربر دلیل را بداند و action بعدی بعد واضح باشد - برای لیست‌های طولانی compact interactive استفاده کنید و detail را در صفحه جدا یا Drawer باز کنید - JobCardStat.total را برای نمایش «x از y» استفاده کنید — خیلی خواناتر از دو عدد جدا

نکنید

  • وضعیت‌های سفارشی به JobStatusKey اضافه نکنید — این مجموعه canonical است. برای sub-statusها از label روی Progress یا Subtitle استفاده کنید
  • در حالت running بدون مقدار value و بدون indeterminate نگذارید — کاربر فکر می‌کند کار گیر کرده
  • <JobCardStatusBadge> را بیرون از <JobCard> نگذارید — context نخواهد بود و error می‌دهد
  • برای نمایش progress چندمرحله‌ای (pipeline) از این استفاده نکنید — کامپوننت اختصاصی StatusFlow برای آن می‌آید (دسته A، مرحله ۲)

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

  • JobCard با interactive به role="button" + tabIndex=0 + هندلر keyboard (Enter/Space) تبدیل می‌شود
  • JobCardStatusBadge در حالت iconOnly هنوز aria-label مناسب دارد
  • JobCardProgress یک role="progressbar" با aria-valuenow/min/max است؛ در حالت indeterminate aria-valuenow حذف می‌شود
  • JobCardError دارای role="alert" — screen reader بلافاصله announce می‌کند
  • JobCardMetaItem از <dt>/<dd> استفاده می‌کند و label را sr-only می‌کند تا فقط value بصری باشد ولی screen reader هر دو را بخواند
  • spinner روی status badge با motion-safe:animate-spin ست شده — وقتی prefers-reduced-motion فعال است ساکن می‌ماند

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

  • PostCard — کارت محتوایی (پست شبکه اجتماعی)؛ از این برای محتوا استفاده کنید، از JobCard برای کار
  • MetricCard — کارت متریک تک‌عدد با trend؛ برای KPI ها
  • Card — کارت سطح پایین بدون semantic ویژه
  • StatusBadge — badge وضعیت operational (critical/warning/middle/low) — مستقل از lifecycle کار
  • FilterPanel — برای فیلتر کردن لیست کارها
  • StatusFlow (به زودی) — برای نمایش pipeline چندمرحله‌ای داخل کار