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

سرِ وسط‌چین با نویسنده و اشتراک، کاورِ پهن، متنِ خوانا با فهرستِ مطالبِ چسبان، نکته، نقل‌قول، گالری، برچسب‌ها، کارتِ نویسنده و مطالبِ مرتبط.

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

این بلاک از ۱۱ آیتم دیگ استفاده می‌کند: article-cardavatarbadgebreadcrumbbuttonchipcontrol-sizeiconspersiantoasttypography

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

۲. کد صفحه

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

article.tsx
"use client"

import * as React from "react"

import { AvatarGenderIcon, CopyIcon, HeartIcon, InfoIcon, ShareIcon } from "@/components/icons"
import {
  ArticleCard,
  ArticleCardBody,
  ArticleCardCategory,
  ArticleCardGrid,
  ArticleCardMedia,
  ArticleCardMeta,
  ArticleCardTitle,
} from "@/components/ui/article-card"
import { Avatar } from "@/components/ui/avatar"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Badge } from "@/components/ui/badge"
import {
  Breadcrumb,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Button } from "@/components/ui/button"
import { Chip, ChipLabel } from "@/components/ui/chip"
import { toast } from "@/components/ui/toast"
import { TypographyBlockquote, TypographyH2, TypographyList, TypographyP } from "@/components/ui/typography"
import { formatJalali, toFaDigits } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"

const TOC = [
  { id: "habit-1", title: "درگاه را خودتان باز کنید" },
  { id: "habit-2", title: "کارتِ جدا برای خرید آنلاین" },
  { id: "habit-3", title: "پیامکِ تراکنش روشن بماند" },
  { id: "when-hacked", title: "اگر کارت لو رفت" },
]

const RELATED: { category: string; title: string; minutes: number; cover: keyof typeof stockImages }[] = [
  { category: "مالی شخصی", title: "پس‌اندازِ اضطراری: چقدر کافی است؟", minutes: 5, cover: "coverMinimalRoom" },
  { category: "امنیت", title: "راهنمای کاملِ رمزِ پویا", minutes: 6, cover: "coverWorkspace" },
  { category: "اخبار", title: "کارمزدِ جدیدِ کارت‌به‌کارت از مهر", minutes: 3, cover: "coverCalculator" },
]

function ShareActions({ className }: { className?: string }) {
  const [saved, setSaved] = React.useState(false)
  return (
    <div className={className}>
      <Button
        variant="outline"
        size="icon"
        aria-label="کپی پیوند"
        onClick={() => {
          void navigator.clipboard?.writeText(window.location.href)
          toast.success("پیوندِ مقاله کپی شد.")
        }}
      >
        <CopyIcon />
      </Button>
      <Button variant="outline" size="icon" aria-label="اشتراک‌گذاری">
        <ShareIcon />
      </Button>
      <Button
        variant="outline"
        size="icon"
        className={saved ? "text-destructive hover:text-destructive" : undefined}
        aria-label={saved ? "حذف از علاقه‌مندی‌ها" : "افزودن به علاقه‌مندی‌ها"}
        aria-pressed={saved}
        onClick={() => setSaved((value) => !value)}
      >
        <HeartIcon filled={saved} />
      </Button>
    </div>
  )
}

/**
 * صفحهٔ مقاله: سرِ وسط‌چین با نویسنده، کاورِ پهن، ستونِ متنِ خوانا (حدود ۷۰ نویسه)
 * با فهرستِ مطالبِ چسبان در دسکتاپ، کارتِ نویسنده، برچسب‌ها و مطالبِ مرتبط.
 */
export default function ArticleBlock() {
  return (
    <ControlSizeProvider size="xl" asChild>
    <main className="mx-auto flex w-full max-w-6xl flex-col gap-12 px-4 py-10 sm:px-6">
      <Breadcrumb>
        <BreadcrumbList>
          <BreadcrumbItem>
            <BreadcrumbLink href="#">خانه</BreadcrumbLink>
          </BreadcrumbItem>
          <BreadcrumbSeparator />
          <BreadcrumbItem>
            <BreadcrumbLink href="#">مجله</BreadcrumbLink>
          </BreadcrumbItem>
          <BreadcrumbSeparator />
          <BreadcrumbItem>
            <BreadcrumbPage>امنیت کارت بانکی</BreadcrumbPage>
          </BreadcrumbItem>
        </BreadcrumbList>
      </Breadcrumb>

      <article className="flex flex-col gap-12">
        {/* سر: دسته، تیتر و خلاصه یک گروهِ نزدیک. */}
        <header className="flex max-w-3xl flex-col gap-4">
          <Badge variant="soft" className="self-start">
            امنیت · بانکداری
          </Badge>
          <h1 className="text-3xl leading-snug font-bold text-balance sm:text-4xl sm:leading-snug">پنج عادتِ ساده برای امنیتِ کارتِ بانکی</h1>
          <p className="text-lg leading-8 text-muted-foreground">بیشترِ کلاهبرداری‌ها با چند عادتِ ساده جلوگیری می‌شود؛ این‌جا مهم‌ترینشان را مرور کرده‌ایم.</p>
        </header>

        {/* کاور و زیرش نویسنده و اشتراک در یک ردیف. */}
        <div className="flex flex-col gap-6">
          <div className="overflow-hidden rounded-xl bg-muted">
            {/* eslint-disable-next-line @next/next/no-img-element -- کاورِ تزئینی ثابت */}
            <img src={stockImages.coverNotebook.src} alt="" className="aspect-[2/1] w-full object-cover sm:aspect-[21/9]" />
          </div>
          <div className="flex flex-wrap items-center justify-between gap-4">
            <div className="flex items-center gap-3">
              <Avatar alt="آرمیتا رستگار" src={stockImages.personWoman3.src} icon={<AvatarGenderIcon gender="female" />} />
              <div className="flex flex-col text-sm">
                <span className="font-semibold">آرمیتا رستگار</span>
                <span className="text-muted-foreground">
                  {formatJalali(new Date("2026-09-25"), "long")} · {toFaDigits(5)} دقیقه مطالعه
                </span>
              </div>
            </div>
            <ShareActions className="flex gap-2" />
          </div>
        </div>

        {/* متن در ستونِ پهن؛ فهرستِ مطالب در دسکتاپ کنارش می‌چسبد. */}
        <div className="grid grid-cols-1 gap-12 lg:grid-cols-[13rem_minmax(0,52rem)] lg:justify-center">
          <nav aria-label="فهرست مطالب" className="hidden lg:block">
            <div className="sticky top-24 flex flex-col gap-3 border-s border-border ps-4 text-sm">
              <span className="font-semibold">در این مقاله</span>
              {TOC.map((item) => (
                <a key={item.id} href={`#${item.id}`} className="leading-6 text-muted-foreground transition-colors hover:text-foreground">
                  {item.title}
                </a>
              ))}
            </div>
          </nav>

          <div className="flex min-w-0 flex-col gap-6 text-base leading-8">
            <TypographyP className="text-lg leading-9">
              هر روز ده‌ها پیامکِ جعلی با نامِ بانک‌ها فرستاده می‌شود. خبرِ خوب این است که با چند عادتِ ساده، تقریباً همهٔ این
              دام‌ها بی‌اثر می‌شوند؛ بدونِ این‌که خریدِ آنلاین سخت شود.
            </TypographyP>

            <TypographyH2 id="habit-1" className="mt-6 scroll-mt-24 border-b-0 pb-0 text-2xl">
              ۱. درگاه را خودتان باز کنید
            </TypographyH2>
            <TypographyP>
              رمزِ پویا را فقط در درگاهی وارد کنید که خودتان از سایتِ فروشگاه به آن رسیده‌اید. هیچ بانکی رمزِ پویا را تلفنی یا
              پیامکی از شما نمی‌خواهد.
            </TypographyP>

            {/* نکته: کادرِ ته‌رنگ با آیکون؛ از متنِ اصلی جدا ولی هم‌سطحِ آن. */}
            <aside className="flex gap-3 rounded-xl bg-[color-mix(in_oklab,var(--brand-secondary)_8%,var(--background))] p-5 text-sm leading-7">
              <InfoIcon aria-hidden className="mt-1 size-5 shrink-0 text-brand-secondary" />
              <p>
                <strong className="font-semibold">نشانی درگاه را نگاه کنید:</strong> درگاه‌های رسمی همیشه با{" "}
                <bdi dir="ltr">shaparak.ir</bdi> تمام می‌شوند.
              </p>
            </aside>

            <TypographyH2 id="habit-2" className="mt-6 scroll-mt-24 border-b-0 pb-0 text-2xl">
              ۲. کارتِ جدا برای خرید آنلاین
            </TypographyH2>
            <TypographyList>
              <li>یک کارت با موجودی کم فقط برای خریدهای اینترنتی نگه دارید.</li>
              <li>قبل از هر خریدِ بزرگ، فقط همان مبلغ را به آن منتقل کنید.</li>
              <li>سقفِ خریدِ روزانهٔ کارت را از اپِ بانک پایین بیاورید.</li>
            </TypographyList>

            <TypographyBlockquote className="my-4 border-s-4 border-brand-secondary text-xl leading-9 font-semibold not-italic">
              «رمزِ دوم مثلِ کلیدِ خانه است؛ آن را به هیچ‌کس نمی‌دهید، حتی اگر خودش را مأمورِ بانک معرفی کند.»
            </TypographyBlockquote>

            <figure className="flex flex-col gap-3">
              <div className="grid grid-cols-3 gap-3">
                {(["coverCalculator", "coverFinanceDesk", "coverOfficeSupplies"] as const).map((cover) => (
                  // eslint-disable-next-line @next/next/no-img-element -- تصویرِ تزئینی ثابت
                  <img key={cover} src={stockImages[cover].src} alt="" className="aspect-square w-full rounded-xl object-cover" />
                ))}
              </div>
              <figcaption className="text-center text-sm text-muted-foreground">سه نشانهٔ پیامکِ جعلی: پیوندِ کوتاه، فوریت و درخواستِ رمز.</figcaption>
            </figure>

            <TypographyH2 id="habit-3" className="mt-6 scroll-mt-24 border-b-0 pb-0 text-2xl">
              ۳. پیامکِ تراکنش روشن بماند
            </TypographyH2>
            <TypographyP>پیامکِ هر برداشت، زودترین هشدار است. اگر قطعش کرده‌اید، همین امروز از اپِ بانک دوباره فعالش کنید.</TypographyP>

            <TypographyH2 id="when-hacked" className="mt-6 scroll-mt-24 border-b-0 pb-0 text-2xl">
              اگر کارت لو رفت
            </TypographyH2>
            <TypographyP>همان لحظه کارت را از اپلیکیشنِ بانک مسدود کنید، رمزها را عوض کنید و با شمارهٔ رسمی بانک تماس بگیرید.</TypographyP>

            {/* پایانِ متن: برچسب‌ها و اشتراک در یک ردیف. */}
            <div className="mt-6 flex flex-wrap items-center justify-between gap-4 border-y border-border py-6">
              <div className="flex flex-wrap gap-2">
                {["امنیت", "کارت بانکی", "رمز پویا", "کلاهبرداری"].map((tag) => (
                  <Chip key={tag}>
                    <ChipLabel>{tag}</ChipLabel>
                  </Chip>
                ))}
              </div>
              <ShareActions className="flex gap-2" />
            </div>

            {/* کارتِ نویسنده */}
            <section aria-label="دربارهٔ نویسنده" className="flex flex-col gap-4 rounded-xl bg-muted p-6 sm:flex-row sm:items-center">
              <Avatar size="lg" alt="آرمیتا رستگار" src={stockImages.personWoman3.src} icon={<AvatarGenderIcon gender="female" />} />
              <div className="flex flex-1 flex-col gap-1">
                <span className="font-semibold">آرمیتا رستگار</span>
                <span className="text-sm text-muted-foreground">نویسندهٔ حوزهٔ امنیتِ بانکی؛ ده سال کار در تیمِ پیشگیری از تقلب.</span>
              </div>
              <Button variant="outline" className="bg-background">
                دنبال کردن
              </Button>
            </section>
          </div>
        </div>
      </article>

      <section aria-labelledby="related-title" className="flex flex-col gap-8 border-t border-border pt-16">
        <h2 id="related-title" className="text-xl font-bold">
          مطالبِ مرتبط
        </h2>
        <ArticleCardGrid>
          {RELATED.map((post) => (
            <ArticleCard key={post.title}>
              <ArticleCardMedia>
                {/* eslint-disable-next-line @next/next/no-img-element -- کاورِ تزئینی ثابت */}
                <img src={stockImages[post.cover].src} alt="" loading="lazy" />
              </ArticleCardMedia>
              <ArticleCardBody>
                <ArticleCardCategory>{post.category}</ArticleCardCategory>
                <ArticleCardTitle href="#">{post.title}</ArticleCardTitle>
                <ArticleCardMeta className="justify-start">{toFaDigits(post.minutes)} دقیقه مطالعه</ArticleCardMeta>
              </ArticleCardBody>
            </ArticleCard>
          ))}
        </ArticleCardGrid>
      </section>
    </main>
    </ControlSizeProvider>
  )
}