پرتوپرتو

ویزارد کار (JobWizard)

wizard چندمرحله‌ای canonical برای راه‌اندازی کارهای async — تحلیل، کمپین، ارزیابی، ایمپورت — با per-step validation، draft persistence، و finish state (submitting/success/error).

معرفی

JobWizard canonical برای ساختن فرم‌های چندمرحله‌ای است که در پایان یک کار async را شروع می‌کنند — تحلیل کامنت‌ها، راه‌اندازی کمپین Booster، یا ارزیابی پورتفوی. ترکیب:

  1. useJobWizard (هوک) — مغز state: قدم فعلی، داده‌ها، اعتبارسنجی per-step، navigation، draft، finish lifecycle.
  2. <JobWizard> composition — لایه‌ی presentation با stepper (StatusFlow) + header + body + footer + success panel.

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

  • ایجاد تحلیل جدید (کامنت‌سنج: منبع → پیکربندی → بازبینی)
  • راه‌اندازی کمپین Booster (پست هدف → worker → scheduling)
  • افزودن پیج به پورتفوی ارزیابی (جستجو → دسته → اهداف)
  • هر فرمی که state بین چند صفحه حفظ شود و در انتها یک job با احتمال خطا/تأخیر شروع کند

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

  • فرم‌های تک‌مرحله‌ای (از Form ساده استفاده کنید)
  • فلوی navigation محور کاربر بدون ورودی form (از Stepper ساده استفاده کنید — JobWizard فرم می‌خواهد)
  • نمایش وضعیت یک job که از قبل شروع شده (از StatusFlow + JobCard استفاده کنید)

ایجاد تحلیل جدید

با این wizard یک تحلیل کامنت جدید ایجاد کنید

  1. منبع داده

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

  2. پیکربندی تحلیل

    ماژول‌های فعال

  3. بازبینی و شروع

    تأیید نهایی و ایجاد جاب

زمین بازی

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

زمین بازی

منبع

انتخاب پلتفرم و بازه

  1. منبع

    انتخاب پلتفرم و بازه

  2. فیلترها

    هشتگ، زبان، منطقه

  3. تحلیل

    انتخاب مدل و کیفیت

  4. مرور

    بررسی نهایی و ارسال

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

الگوی مصرف

'use client'
import {
  useJobWizard,
  JobWizard,
  JobWizardHeader,
  JobWizardStepper,
  JobWizardBody,
  JobWizardError,
  JobWizardFooter,
  JobWizardBack,
  JobWizardNext,
  JobWizardCancel,
  JobWizardSpacer,
  JobWizardSuccess,
} from '@parto-system-design/ui'

interface FormData extends Record<string, unknown> {
  source: 'instagram' | 'excel'
  postUrl?: string
  name: string
}

export function NewAnalysisWizard({ onClose }: { onClose: () => void }) {
  const wizard = useJobWizard<FormData>({
    steps: [
      { id: 'source', title: 'منبع', description: 'پست یا فایل' },
      { id: 'config', title: 'پیکربندی' },
      { id: 'review', title: 'بازبینی' },
    ],
    initialData: { source: 'instagram', name: '' },
    validateStep: (stepId, data) => {
      if (stepId === 'source' && data.source === 'instagram' && !data.postUrl?.trim())
        return { postUrl: 'URL پست الزامی است' } as Record<string, string>
      if (stepId === 'review' && !data.name?.trim()) return 'نام تحلیل الزامی است'
      return null
    },
    onSubmit: async (data) => {
      const job = await fetch('/api/analyses', { method: 'POST', body: JSON.stringify(data) }).then((r) => r.json())
      return job
    },
    onCancel: onClose,
    draftKey: 'analyses:new', // persist draft
  })

  return (
    <JobWizard wizard={wizard}>
      <JobWizardHeader title="ایجاد تحلیل جدید" description="یک تحلیل کامنت جدید بسازید" />
      <JobWizardStepper size="sm" />

      {wizard.submitState === 'success' ? (
        <JobWizardSuccess title="تحلیل شروع شد" description={`شناسه: ${wizard.submitResult?.id}`} />
      ) : (
        <>
          <JobWizardBody>{/* step content */}</JobWizardBody>
          <JobWizardError />
          <JobWizardFooter>
            <JobWizardBack />
            <JobWizardSpacer />
            <JobWizardCancel />
            <JobWizardNext submitLabel="شروع تحلیل" />
          </JobWizardFooter>
        </>
      )}
    </JobWizard>
  )
}

Layout عمودی (sidebar stepper)

پلتفرم

محتوای مرحله‌ی «پلتفرم»

برای wizardهای طولانی یا صفحات اختصاصی، از stepper عمودی در sidebar استفاده کنید. کافی است <JobWizardStepper orientation="vertical" /> را در یک <aside> و بقیه را در ستون کنارش بگذارید.

چرخه finish — idle / submitting / success / error

wizard.submitState چهار حالت دارد:

  • idle — حالت پیش‌فرض. کاربر در حال پر کردن.
  • submittingonSubmit در حال اجراست. دکمه Next disabled + spinner.
  • successonSubmit با موفقیت resolve شد. <JobWizardSuccess> به‌صورت خودکار نمایش داده می‌شود.
  • erroronSubmit throw کرد. <JobWizardError> پیام خطای wizard.submitError.message را نشان می‌دهد. کاربر می‌تواند دوباره Next بزند.

Validation

validateStep(stepId, data) دو نوع خروجی می‌تواند برگرداند:

  • string — خطای سطح-مرحله (step-level). <JobWizardError> آن را در قالب یک بنر قرمز نشان می‌دهد.
  • Record<string, string> — map از field → error. مصرف‌کننده خودش ارور هر field را زیرش می‌چسباند (wizard.error را object کنترل می‌کند).

اگر null برگردد، navigation رد می‌شود. validator می‌تواند async باشد (Promise برگرداند).

Draft persistence

  • با ست کردن draftKey، محتوای data به localStorage ذخیره می‌شود (debounce پیش‌فرض 300ms).
  • در mount بعدی، اگر draft باشد، جایگزین initialData می‌شود.
  • پس از submit موفق، draft خودکار پاک می‌شود.
  • با wizard.clearDraft() یا wizard.reset() هم قابل پاک کردن است.

Props و APIs

useJobWizard (هوک)

Prop

Type

JobWizardApi (return value)

مهم‌ترین فیلدها:

type JobWizardApi<TData> = {
  currentStep: WizardStep
  currentIndex: number
  isFirst: boolean
  isLast: boolean
  steps: WizardStep[]
  data: TData
  setData: (data: TData) => void
  update: (patch: Partial<TData>) => void
  error: string | Record<string, string> | null
  clearError: () => void
  submitState: 'idle' | 'submitting' | 'success' | 'error'
  submitResult: unknown
  submitError: unknown
  next: () => void
  back: () => void
  goToStep: (id: string) => void
  submit: () => void
  cancel: () => void
  reset: () => void
  clearDraft: () => void
  isStepVisited: (id: string) => boolean
  stageStatus: (step: WizardStep, index: number) => StageStatusKey
}

JobWizard (component)

Prop

Type

دکمه‌های نویگیشن

هر چهار دکمه (JobWizardBack, JobWizardNext, JobWizardCancel, JobWizardSkip) از variant/size/className همان Button پایه پشتیبانی می‌کنند:

  • JobWizardBackwizard.back(). در اولین مرحله disabled.
  • JobWizardNextwizard.next() یا در آخرین مرحله wizard.submit(). submitLabel برای متن سفارشی دکمه‌ی finish. هنگام submitting spinner می‌شود.
  • JobWizardCancelwizard.cancel().
  • JobWizardSkip — فقط در stepهایی که optional: true دارند ظاهر می‌شود؛ بدون validation به مرحله‌ی بعد می‌پرد.
  • JobWizardSpacer — div transparent با flex: 1 برای push کردن گروه بعدی به end.

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

بکنید

  • برای برنچینگ (مثلاً skipping یک مرحله بر اساس داده)، یک steps array محاسبه‌شده از React.useMemo پاس دهید و با تغییر data تغییرش دهید — wizard خودکار سازگار می‌شود
  • draftKey را برای wizardهای long-running حتماً ست کنید تا کاربر با reload صفحه progress از دست ندهد
  • در <JobWizardSuccess> حداقل یک action primary («مشاهده وضعیت» / «رفتن به داشبورد») بدهید تا کاربر در بن‌بست نماند
  • برای خطای فیلد، wizard.error را به‌صورت object بازگردانید و زیر هر Input مقدار مناسب را نمایش دهید + aria-invalid

نکنید

  • از wizard.next() به صورت مستقیم همراه با wizard.submit() در یک handler استفاده نکنید — <JobWizardNext> خودش بر اساس isLast سوئیچ می‌کند
  • برای فرم‌های یک‌مرحله‌ای از JobWizard استفاده نکنید — overkill است
  • در onSubmit از دست دادن data اصلی را در state والد قبول نکنید — wizard حق ensure می‌دهد ولی برای منطق رفتاری (مثل «continue in background»)، داده را خودتان cache کنید
  • validateStep را async بدون loading state نگذارید — کاربر کلیک می‌کند و نمی‌فهمد چرا چیزی نمی‌شود. حداقل در هنگام validation خیلی سریع باشید یا از submitting استفاده کنید

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

  • Stepper از StatusFlow می‌آید: root <ol> با aria-current="step" روی مرحله‌ی فعال + aria-label ترکیبی "{title} — {status}" روی هر مرحله
  • مراحل بازدید‌شده (با allowJump) به <button> تبدیل می‌شوند؛ مراحل بعدی کلیک‌پذیر نیستند
  • <JobWizardError> با role="alert" — screen reader بلافاصله announce می‌کند
  • <JobWizardSuccess> با role="status" — screen reader موفقیت را همزمان با focus trap می‌شنود
  • دکمه Next در submitting spinner دارد با motion-safe:animate-spin (reduced-motion safe)
  • هنگام submitting/success، دکمه‌های Back/Cancel disabled می‌شوند تا کاربر عقب‌گرد نکند
  • flow کاملاً keyboard-accessible — Tab بین فیلدها + دکمه‌های نویگیشن

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

  • StatusFlow — زیرین برای stepper؛ JobWizard فقط یک integration است
  • JobCard — بعد از submit موفق، نمایش job ایجاد‌شده در لیست «کارهای من»
  • Stepper — برای wizardهای بدون ایجاد job (مثل onboarding سبک) — state کمتر، بدون submit
  • Form — برای فرم‌های تک‌مرحله‌ای
  • FilterPanel — الگوی مشابه composition-based root