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

فراخوانِ عضویت

پیش‌نمایش تمام‌صفحه

بخشِ فراخوان با فرمِ ایمیل: کارتِ دوتکه با عکسِ واقعی، خطا زیرِ فیلد و پیامِ موفقیت در همان جا.

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

این بلاک از ۵ آیتم دیگ استفاده می‌کند: buttoncontrol-sizepersiantext-fieldtoast

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

۲. کد صفحه

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

cta-newsletter.tsx
"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { TextField } from "@/components/ui/text-field"
import { toast } from "@/components/ui/toast"
import { isValidEmail } from "@/lib/persian"
import { stockImages } from "@/lib/stock-images"

/**
 * فراخوانِ عضویت: کارتِ دوتکه با عکسِ واقعی و فرمِ ایمیل؛ خطا زیرِ همان فیلد و پیامِ
 * موفقیت در همان جای فرم، تا کاربر از صفحه بیرون نرود.
 */
export default function CtaNewsletterBlock() {
  const [done, setDone] = React.useState(false)

  return (
    <ControlSizeProvider size="xl" asChild>
      <section className="mx-auto w-full max-w-6xl px-4 py-24 sm:px-6">
        <div className="grid grid-cols-1 overflow-hidden rounded-xl border border-border bg-card md:grid-cols-2">
          <div className="flex flex-col justify-center gap-6 p-8 sm:p-12">
            <div className="flex flex-col gap-3">
              <h2 className="text-3xl leading-snug font-black text-balance">هر پنجشنبه، یک نکتهٔ مالی</h2>
              <p className="leading-8 text-muted-foreground">نامه‌ای کوتاه با بهترین مقاله‌های هفته؛ بدونِ تبلیغ، هر وقت خواستی لغو کن.</p>
            </div>
            {done ? (
              <p role="status" className="rounded-lg bg-success-subtle p-4 font-medium text-success-subtle-foreground">
                عضو شدی؛ اولین نامه پنجشنبه می‌رسد.
              </p>
            ) : (
              <form
                className="flex flex-col gap-3 sm:flex-row sm:items-start"
                onSubmit={(event) => {
                  event.preventDefault()
                  setDone(true)
                  toast.success("عضویت ثبت شد.")
                }}
              >
                <TextField
                  aria-label="ایمیل"
                  name="email"
                  type="email"
                  dir="ltr"
                  placeholder="name@example.com"
                  autoComplete="email"
                  required
                  validate={(value) => (isValidEmail(value) ? undefined : "ایمیل معتبر نیست؛ مثل name@example.com")}
                  className="flex-1"
                />
                <Button type="submit">عضویت</Button>
              </form>
            )}
          </div>
          {/* eslint-disable-next-line @next/next/no-img-element -- عکس از کتابخانهٔ تصویرِ دیگ */}
          <img src={stockImages.coverNotebook.src} alt={stockImages.coverNotebook.alt} loading="lazy" className="order-first aspect-[16/9] size-full object-cover md:order-none md:aspect-auto" />
        </div>
      </section>
    </ControlSizeProvider>
  )
}