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

پرسش‌ها با فهرستِ موضوع

پیش‌نمایش تمام‌صفحه

پرسش‌های پرتکرار با فهرستِ موضوع در ستونِ کنار (موضوعِ فعال روی کارتِ سفید با سایه، و شمارِ پرسش‌ها) و آکاردئونِ همان موضوع.

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

این بلاک از ۵ آیتم دیگ استفاده می‌کند: accordionbuttoncontrol-sizeiconspersian

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

۲. کد صفحه

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

faq-sidebar.tsx
"use client"

import * as React from "react"

import { CreditCardIcon, ShieldCheckIcon, TruckIcon, UserIcon } from "@/components/icons"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toFaDigits } from "@/lib/persian"

const TOPICS = [
  { value: "account", label: "حساب کاربری", icon: <UserIcon />, items: [
    { q: "چطور رمزم را عوض کنم؟", a: "از «حسابِ من ← امنیت» رمزِ فعلی و رمزِ تازه را وارد کن؛ بعد از ذخیره از دستگاه‌های دیگر خارج می‌شوی." },
    { q: "می‌توانم شمارهٔ موبایلم را تغییر دهم؟", a: "بله؛ کدِ تأیید به شمارهٔ تازه فرستاده می‌شود و بعد از تأیید جایگزین می‌شود." },
  ] },
  { value: "payment", label: "پرداخت", icon: <CreditCardIcon />, items: [
    { q: "پول کم شد ولی سفارش ثبت نشد", a: "مبلغ تا ۷۲ ساعت خودکار به کارت برمی‌گردد. اگر برنگشت، با کدِ پیگیری تیکت بزن." },
    { q: "فاکتورِ رسمی می‌دهید؟", a: "بله؛ از صفحهٔ سفارش، «درخواستِ فاکتورِ رسمی» را بزن و اطلاعاتِ شرکت را وارد کن." },
  ] },
  { value: "shipping", label: "ارسال", icon: <TruckIcon />, items: [
    { q: "ارسال چقدر طول می‌کشد؟", a: "تهران همان روز با پیک؛ شهرهای دیگر ۳ تا ۵ روزِ کاری با پست." },
  ] },
  { value: "security", label: "امنیت", icon: <ShieldCheckIcon />, items: [
    { q: "اطلاعاتم کجا نگه داشته می‌شود؟", a: "روی سرورهای داخلِ کشور، رمزگذاری‌شده، با نسخهٔ پشتیبانِ روزانه." },
  ] },
]

/**
 * پرسش‌های پرتکرار با فهرستِ موضوع در ستونِ کنار: موضوعِ فعال روی کارتِ سفید با سایه (جدا از سطحِ subtle)، شمارِ
 * پرسش‌های هر موضوع، و آکاردئونِ همان موضوع در ستونِ اصلی؛ در موبایل موضوع‌ها بالای پرسش‌ها.
 */
export default function FaqSidebarBlock() {
  const [topic, setTopic] = React.useState(TOPICS[0].value)
  const active = TOPICS.find((item) => item.value === topic) ?? TOPICS[0]

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-5xl flex-col gap-10 px-4 py-16 sm:px-6">
        <header className="flex flex-col gap-2">
          <h1 className="text-3xl font-black">پرسش‌های پرتکرار</h1>
          <p className="text-muted-foreground">موضوع را انتخاب کن؛ جوابِ بیشترِ سؤال‌ها همین‌جاست.</p>
        </header>
        <div className="grid grid-cols-1 items-start gap-8 md:grid-cols-[16rem_minmax(0,1fr)]">
          <nav aria-label="موضوع‌ها" className="flex flex-col gap-1 rounded-xl bg-muted p-1.5">
            {TOPICS.map((item) => (
              <Button
                key={item.value}
                variant="ghost"
                aria-current={item.value === topic ? "true" : undefined}
                onClick={() => setTopic(item.value)}
                className={`justify-start gap-3 font-normal ${item.value === topic ? "bg-card font-semibold shadow-control hover:bg-card" : "hover:bg-card"}`}
              >
                <span aria-hidden className="text-muted-foreground [&>svg]:size-5">
                  {item.icon}
                </span>
                <span className="flex-1 text-start">{item.label}</span>
                <span className="text-xs text-muted-foreground tabular-nums">{toFaDigits(item.items.length)}</span>
              </Button>
            ))}
          </nav>
          <section aria-label={active.label}>
            <Accordion key={topic} type="single" collapsible variant="soft" indicator="plus" defaultValue={active.items[0].q}>
              {active.items.map((item) => (
                <AccordionItem key={item.q} value={item.q}>
                  <AccordionTrigger>{item.q}</AccordionTrigger>
                  <AccordionContent className="leading-8 text-muted-foreground">{item.a}</AccordionContent>
                </AccordionItem>
              ))}
            </Accordion>
          </section>
        </div>
      </main>
    </ControlSizeProvider>
  )
}