محصولِ دیجیتال
پیشنمایش تمامصفحهدوره، کتابِ الکترونیک یا اشتراک: کاور با پیشنمایش، مدرس، آمار، فهرستِ شاملها و کارتِ خریدِ چسبان با انتخابِ نوعِ دسترسی.
۱. نصب کامپوننتها
این بلاک از ۷ آیتم دیگ استفاده میکند: avatarbuttoncontrol-sizeiconslabelpersianradio-group
پکیجمنیجر پیشفرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیجمنیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/avatar.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/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.jsonpnpm dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.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/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.jsonyarn dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.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/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.jsonbunx --bun digdesign@latest add https://docs.digdesign.ir/r/avatar.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/label.json https://docs.digdesign.ir/r/persian.json https://docs.digdesign.ir/r/radio-group.json۲. کد صفحه
کد را در app/<route>/page.tsx بگذارید و متنها، داده و هندلرها را با محصول خودتان عوض کنید.
product-detail-digital.tsx
"use client"
import * as React from "react"
import { CheckIcon, ClockIcon, PlayIcon, StarIcon, UsersIcon } from "@/components/icons"
import { Avatar } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { formatCurrency } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"
const OPTIONS = [
{ value: "course", title: "دوره", note: "دسترسی همیشگی به ۲۴ درس", price: 1_900_000 },
{ value: "mentor", title: "دوره + منتور", note: "بههمراهِ ۴ جلسهٔ رفعِ اشکال", price: 3_400_000, best: true },
]
const INCLUDES = ["۲۴ درسِ ویدیویی، ۸ ساعت", "فایلِ تمرین و قالبِ اکسل", "گواهی پایانِ دوره", "بهروزرسانی رایگان"]
const optionCard =
"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"
/**
* جزئیاتِ محصولِ دیجیتال (دوره، کتابِ الکترونیک، اشتراک): کاور با پیشنمایشِ ویدیو، مدرس،
* آمار، «شاملِ چه چیزهایی»، و کارتِ خریدِ چسبان با انتخابِ نوعِ دسترسی و قیمت.
*/
export default function ProductDetailDigitalBlock() {
const [option, setOption] = React.useState("mentor")
const active = OPTIONS.find((item) => item.value === option) ?? OPTIONS[0]
return (
<ControlSizeProvider size="xl" asChild>
<main className="mx-auto grid w-full max-w-6xl grid-cols-1 items-start gap-10 p-4 sm:p-6 lg:grid-cols-[minmax(0,1fr)_22rem]">
<div className="flex flex-col gap-8">
<div className="relative overflow-hidden rounded-xl bg-inverse">
{/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
<img src={stockImages.studentStudy.src} alt="" className="aspect-video w-full object-cover" />
<div className="absolute inset-0 flex items-center justify-center">
<Button className="bg-[var(--dig-white)] text-[var(--dig-graphite-900)] hover:bg-[var(--dig-neutral-200)]">
<PlayIcon />
پیشنمایشِ رایگان
</Button>
</div>
</div>
<div className="flex flex-col gap-4">
<h1 className="text-3xl font-bold text-balance">حسابداری برای صاحبانِ کسبوکار</h1>
<p className="text-lg leading-8 text-muted-foreground">بدونِ پیشنیاز؛ از سند تا سود و زیان، با مثالهای واقعی فروشگاه و کافه.</p>
<ul className="flex flex-wrap gap-x-6 gap-y-2 text-sm text-muted-foreground">
<li className="flex items-center gap-1.5">
<StarIcon aria-hidden className="size-4 fill-[var(--dig-orange-400)] text-[var(--dig-orange-400)]" />
۴٫۹ (۶۴۰ نظر)
</li>
<li className="flex items-center gap-1.5">
<UsersIcon aria-hidden className="size-4" />
۳٬۴۰۰ دانشجو
</li>
<li className="flex items-center gap-1.5">
<ClockIcon aria-hidden className="size-4" />
۸ ساعت
</li>
</ul>
</div>
<div className="flex items-center gap-4 rounded-xl bg-subtle p-4">
<Avatar size="lg" alt="" src={stockImages.personWoman3.src} />
<span className="flex flex-col">
<span className="font-semibold">نگار احمدی</span>
<span className="text-sm text-muted-foreground">حسابدارِ رسمی، ۱۲ سال تدریس</span>
</span>
</div>
<section aria-labelledby="pdd-includes" className="flex flex-col gap-4">
<h2 id="pdd-includes" className="text-xl font-bold">
شاملِ اینهاست
</h2>
<ul className="grid grid-cols-1 gap-3 sm:grid-cols-2">
{INCLUDES.map((item) => (
<li key={item} className="flex items-center gap-3">
<CheckIcon aria-hidden className="size-5 text-success" />
{item}
</li>
))}
</ul>
</section>
</div>
<aside className="flex flex-col gap-5 rounded-xl border border-border bg-card p-5 lg:sticky lg:top-6">
<RadioGroup value={option} onValueChange={setOption} aria-label="نوعِ دسترسی" className="gap-3">
{OPTIONS.map((item) => (
<div key={item.value} className={optionCard}>
<RadioGroupItem id={`pdd-${item.value}`} value={item.value} />
<Label htmlFor={`pdd-${item.value}`} className="flex flex-1 flex-col items-start gap-1">
<span className="font-semibold">{item.title}</span>
<span className="text-xs font-normal text-muted-foreground">{item.note}</span>
</Label>
<span className="text-sm font-bold tabular-nums">{formatCurrency(item.price)}</span>
</div>
))}
</RadioGroup>
<div className="flex items-baseline justify-between border-t border-border pt-5">
<span className="text-muted-foreground">قیمت</span>
<span className="text-2xl font-black tabular-nums">{formatCurrency(active.price)}</span>
</div>
<Button fullWidth className="mt-2">
خرید و شروعِ دوره
</Button>
<p className="text-center text-xs text-muted-foreground">تا ۷ روز اگر راضی نبودی، کامل برمیگردانیم.</p>
</aside>
</main>
</ControlSizeProvider>
)
}