کارت کار (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) برای دامنه کافی نیست — مدل سفارشی خودتان را بسازید
تحلیل کامنتهای پست شماره ۱۲۳
۱٬۲۵۰ کامنت از اینستاگرام · برچسبزنی خودکار
- شروع
- امروز، ۱۴:۳۲
- مدت
- ۱۲ دقیقه
- توسط
- سارا احمدی
زمین بازی
با تغییر تنظیمات زیر، پیشنمایش زنده را مشاهده کنید.
تحلیل گفتوگوهای کمپین
۲۴ ساعت گذشته
- پلتفرم
- توییتر
- پستها
- ۱٬۲۰۰
- بازه
- ۷ روز
استفاده
'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 · در دقیقه ۳ ناموفق شد
challenged بازگشتند. رمز یا نشستها را بررسی کنید.- 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— ردیف افقی یکسطری مناسب لیست/inboxinteractive— کل کارت قابل کلیک با focus ring و Space/Enter keyboard- در حالت compact سعی کنید Header را تنها سیتم اصلی بگذارید؛ Progress/Stats را حذف کنید تا فشرده بماند
progress نامعین (indeterminate)
واکشی پروفایل از اینستاگرام
زمان تخمینی نامشخص — منتظر پاسخ API
وقتی کار در حال انجام است ولی درصد پیشرفت مشخص نیست (مثل «منتظر پاسخ API»)، از indeterminate استفاده کنید. shimmer از start به end حرکت میکند و با prefers-reduced-motion ساکن میشود.
thumbnail با تصویر
تحلیل کامنتهای پست کافه لاکچری
@cafe_lux · ۵٬۳۰۰ کامنت
src+altبرای تصویر (مثل preview پست)iconبرای آیکون Lucidefallbackبرای حروف اول / 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
JobCardThumbnail
JobCardStatusBadge
JobCardProgress
JobCardStat
JobCardMetaItem
راهنمای استفاده
بکنید
- وضعیتهای sixگانه را به دامنهی خودتان map کنید: CommentSanj
pending_review→runningبا 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است؛ در حالت indeterminatearia-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 چندمرحلهای داخل کار