پرسشها با فهرستِ موضوع
پیشنمایش تمامصفحهپرسشهای پرتکرار با فهرستِ موضوع در ستونِ کنار (موضوعِ فعال روی کارتِ سفید با سایه، و شمارِ پرسشها) و آکاردئونِ همان موضوع.
۱. نصب کامپوننتها
این بلاک از ۵ آیتم دیگ استفاده میکند: 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.jsonpnpm 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.jsonyarn 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.jsonbunx --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>
)
}