نظرِ برجسته
پیشنمایش تمامصفحهبخشِ نظرِ مشتری با یک نقلقولِ بزرگ روی سطحِ تیره با پترنِ شبکه، عکسِ گوینده و عددِ نتیجه؛ با دکمهٔ قبلی و بعدی بین داستانها.
۱. نصب کامپوننتها
این بلاک از ۵ آیتم دیگ استفاده میکند: background-patternbuttoncontrol-sizeiconspersian
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/background-pattern.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/background-pattern.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/background-pattern.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/background-pattern.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 بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
testimonial-spotlight.tsx
"use client"
import * as React from "react"
import { ChevronLeftIcon, ChevronRightIcon, QuoteIcon } from "@/components/icons"
import { GridPattern } from "@/components/ui/background-pattern"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"
const QUOTES: { text: string; name: string; role: string; photo: StockImageName; metric: string; metricLabel: string }[] = [
{ text: "آوند حسابوکتابِ چهار شعبه را یکجا کرد؛ حالا هر صبح با یک نگاه میدانم دیروز چه گذشت.", name: "سارا محمدی", role: "مدیرِ کافه رست", photo: "personWoman2", metric: "۲۰٪", metricLabel: "کاهشِ هزینه در شش ماه" },
{ text: "قبل از آوند، پایانِ ماه یعنی دو شب بیداری. الان حقوق و بیمه را در یک ساعت میبندیم.", name: "علی کاظمی", role: "مدیرِ منابعِ انسانی", photo: "personMan2", metric: "۹ ساعت", metricLabel: "صرفهجویی در هفته" },
]
/**
* نظرِ برجستهٔ مشتری: یک نقلقولِ بزرگ روی سطحِ تیره با پترنِ شبکه، عکسِ تمامقدِ گوینده
* و عددِ نتیجه؛ با دکمههای قبلی/بعدی بین داستانها جابهجا میشود.
*/
export default function TestimonialSpotlightBlock() {
const [index, setIndex] = React.useState(0)
const quote = QUOTES[index]
const go = (step: number) => setIndex((value) => (value + step + QUOTES.length) % QUOTES.length)
return (
<ControlSizeProvider size="xl" asChild>
<section aria-roledescription="نمایشگر" aria-label="داستانِ مشتریها" className="mx-auto w-full max-w-6xl px-4 py-24 sm:px-6">
<div className="relative grid grid-cols-1 overflow-hidden rounded-xl bg-inverse text-[var(--dig-white)] [--border:var(--inverse-border)] [--muted-foreground:var(--dig-neutral-400)] md:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)]">
<GridPattern size={40} />
<div className="relative flex flex-col gap-8 p-8 sm:p-12" aria-live="polite">
<QuoteIcon aria-hidden className="size-10 text-[var(--dig-violet-400)]" />
<blockquote className="text-2xl leading-relaxed font-bold text-balance sm:text-3xl sm:leading-relaxed">{quote.text}</blockquote>
<div className="flex flex-wrap items-end justify-between gap-6">
<div className="flex flex-col">
<span className="font-semibold">{quote.name}</span>
<span className="text-sm text-muted-foreground">{quote.role}</span>
</div>
<div className="flex flex-col items-end">
<span className="text-3xl font-black tabular-nums text-[var(--dig-green-400)]">{quote.metric}</span>
<span className="text-sm text-muted-foreground">{quote.metricLabel}</span>
</div>
</div>
<div className="flex items-center gap-3">
<Button variant="outline" size="icon" aria-label="داستانِ قبلی" onClick={() => go(-1)} className="bg-inverse text-[var(--dig-white)] hover:bg-inverse-raised">
<ChevronRightIcon className="ltr:rotate-180" />
</Button>
<Button variant="outline" size="icon" aria-label="داستانِ بعدی" onClick={() => go(1)} className="bg-inverse text-[var(--dig-white)] hover:bg-inverse-raised">
<ChevronLeftIcon className="ltr:rotate-180" />
</Button>
<span className="text-sm text-muted-foreground tabular-nums">
{toFaDigits(index + 1)} از {toFaDigits(QUOTES.length)}
</span>
</div>
</div>
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img key={quote.photo} src={stockImages[quote.photo].src} alt={quote.name} className="relative aspect-[4/3] size-full object-cover md:aspect-auto" />
</div>
</section>
</ControlSizeProvider>
)
}