بلاک‌ها · بخش‌های صفحه

بخشِ نظرِ مشتری با یک نقل‌قولِ بزرگ روی سطحِ تیره با پترنِ شبکه، عکسِ گوینده و عددِ نتیجه؛ با دکمهٔ قبلی و بعدی بین داستان‌ها.

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

این بلاک از ۵ آیتم دیگ استفاده می‌کند: 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.json
pnpm 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.json
yarn 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.json
bunx --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>
  )
}