بلاک‌ها · پشتیبانی و محتوا

ثبت تیکت با بخش، پیام، توضیح صوتی و پیوست، و فهرست تیکت‌های قبلی.

۱. نصب کامپوننت‌ها

این بلاک از ۱۲ آیتم دیگ استفاده می‌کند: badgebuttoncontrol-sizefile-uploadiconslabelpersianradio-grouptext-fieldtextareatoastvoice-recorder

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/textarea.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/voice-recorder.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/textarea.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/voice-recorder.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/textarea.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/voice-recorder.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/control-size.json https://docs.digdesign.ir/r/file-upload.json https://docs.digdesign.ir/r/icons.json https://docs.digdesign.ir/r/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.json https://docs.digdesign.ir/r/text-field.json https://docs.digdesign.ir/r/textarea.json https://docs.digdesign.ir/r/toast.json https://docs.digdesign.ir/r/voice-recorder.json

۲. کد صفحه

کد را در app/<route>/page.tsx بگذارید و متن‌ها، داده و هندلرها را با محصول خودتان عوض کنید.

support-ticket.tsx
"use client"

import * as React from "react"

import { ChevronLeftIcon, ClockIcon, CreditCardIcon, SettingsIcon, TruckIcon } from "@/components/icons"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { FileUpload } from "@/components/ui/file-upload"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { TextField } from "@/components/ui/text-field"
import { Textarea } from "@/components/ui/textarea"
import { toast } from "@/components/ui/toast"
import { VoiceRecorder } from "@/components/ui/voice-recorder"
import { formatRelativeTime, toFaDigits } from "@/lib/persian"

type Status = "open" | "answered" | "closed"
const STATUS: Record<Status, { text: string; color: "warning" | "success" | "default" }> = {
  open: { text: "در انتظار پاسخ", color: "warning" },
  answered: { text: "پاسخ داده شد", color: "success" },
  closed: { text: "بسته", color: "default" },
}
const TICKETS: { id: string; subject: string; date: string; status: Status }[] = [
  { id: "5021", subject: "کسر وجه بدون ثبت سفارش", date: "2026-09-29T14:00:00", status: "answered" },
  { id: "5003", subject: "فاکتورِ رسمی برای شرکت", date: "2026-09-26T10:15:00", status: "open" },
  { id: "4988", subject: "تغییر آدرس تحویل", date: "2026-09-24T09:30:00", status: "closed" },
]
const DEPARTMENTS = [
  { value: "financial", label: "مالی و پرداخت", icon: <CreditCardIcon /> },
  { value: "orders", label: "سفارش و ارسال", icon: <TruckIcon /> },
  { value: "technical", label: "فنی", icon: <SettingsIcon /> },
]

const optionCard =
  "group flex items-center gap-3 rounded-xl border border-border p-4 transition-colors has-[[data-state=checked]]:border-primary has-[[data-state=checked]]:bg-primary-subtle has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring"

/**
 * درخواستِ پشتیبانی: انتخابِ بخش با کارت‌های آیکون‌دار، موضوع و شرح، پیامِ صوتی و پیوست؛
 * ستونِ کنار زمانِ پاسخ و درخواست‌های قبلی را به‌صورتِ ردیف‌های قابلِ کلیک نشان می‌دهد.
 */
export default function SupportTicketBlock() {
  const [sending, setSending] = React.useState(false)
  const [department, setDepartment] = React.useState("financial")

  async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setSending(true)
    await new Promise((resolve) => setTimeout(resolve, 800))
    setSending(false)
    toast.success("تیکت ثبت شد.", { description: "کد پیگیری ۵۰۲۲" })
  }

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto grid grid-cols-1 w-full max-w-5xl items-start gap-8 p-4 sm:p-6 lg:grid-cols-[minmax(0,1fr)_20rem]">
        <form onSubmit={onSubmit} className="flex flex-col gap-8 rounded-xl border border-border bg-card p-6 sm:p-8">
          <div className="flex flex-col gap-1">
            <h1 className="text-2xl font-bold">درخواستِ پشتیبانی</h1>
            <p className="text-muted-foreground">هر چه دقیق‌تر بنویسید، سریع‌تر پاسخ می‌گیرید.</p>
          </div>

          <fieldset className="flex flex-col gap-3">
            <legend className="mb-3 text-sm font-medium">بخش</legend>
            <RadioGroup name="department" value={department} onValueChange={setDepartment} className="grid grid-cols-1 gap-3 sm:grid-cols-3">
              {DEPARTMENTS.map((item) => (
                <div key={item.value} className={optionCard}>
                  <RadioGroupItem id={`dep-${item.value}`} value={item.value} className="sr-only" />
                  <span aria-hidden className="flex size-10 shrink-0 items-center justify-center rounded-lg bg-subtle group-has-[[data-state=checked]]:bg-background [&_svg]:size-5">
                    {item.icon}
                  </span>
                  <Label htmlFor={`dep-${item.value}`} className="flex-1">
                    {item.label}
                  </Label>
                </div>
              ))}
            </RadioGroup>
          </fieldset>

          <div className="flex flex-col gap-5">
            <TextField label="موضوع" name="subject" required />
            <div className="flex flex-col gap-2">
              <Label htmlFor="message">شرحِ درخواست</Label>
              <Textarea id="message" name="message" rows={5} placeholder="چه اتفاقی افتاد و انتظار داشتید چه شود؟" required />
            </div>
            <div className="flex flex-col gap-2">
              <Label>توضیحِ صوتی</Label>
              <VoiceRecorder name="voice" maxDuration={60} />
            </div>
            <FileUpload name="attachments" multiple maxFiles={3} accept="image/*,.pdf" hint="تصویر یا PDF، حداکثر ۳ فایل" />
          </div>

          <div className="flex justify-end border-t border-border pt-8">
            <Button type="submit" loading={sending}>
              ثبتِ درخواست
            </Button>
          </div>
        </form>

        <aside className="flex flex-col gap-6">
          <div className="flex items-center gap-3 rounded-xl bg-subtle p-5">
            <span aria-hidden className="flex size-10 items-center justify-center rounded-lg bg-success-subtle text-success [&_svg]:size-5">
              <ClockIcon />
            </span>
            <div className="flex flex-col">
              <span className="font-semibold">پاسخ در کمتر از ۴ ساعت</span>
              <span className="text-sm text-muted-foreground">شنبه تا پنجشنبه، ۸ تا ۲۰</span>
            </div>
          </div>

          <section aria-labelledby="tickets-title" className="flex flex-col gap-3">
            <h2 id="tickets-title" className="font-semibold">
              درخواست‌های قبلی
            </h2>
            <ul className="flex flex-col gap-2">
              {TICKETS.map((ticket) => (
                <li key={ticket.id}>
                  <Button variant="outline" className="h-auto w-full justify-between gap-3 py-3 text-start font-normal">
                    <span className="flex flex-col items-start gap-1">
                      <span className="font-medium">{ticket.subject}</span>
                      <span className="text-xs text-muted-foreground">
                        #{toFaDigits(ticket.id)} · {formatRelativeTime(ticket.date)}
                      </span>
                      <Badge variant="soft" color={STATUS[ticket.status].color}>
                        {STATUS[ticket.status].text}
                      </Badge>
                    </span>
                    <ChevronLeftIcon aria-hidden />
                  </Button>
                </li>
              ))}
            </ul>
          </section>
        </aside>
      </main>
    </ControlSizeProvider>
  )
}