بلاک‌ها · حساب کاربری

دعوت از دوستان

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

کد و لینک دعوت با دکمهٔ کپی و آمار دعوت‌ها و اعتبار.

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

این بلاک از ۸ آیتم دیگ استفاده می‌کند: avatarbadgebuttoncardcontrol-sizeiconspersianprogress

پکیج‌منیجر پیش‌فرض Node.jsنصب سریع با لینک سخت و دیسک مشترکYarn نسخهٔ ۲ به بالا (Berry)رانتایم و پکیج‌منیجر Bun
npx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json
pnpm dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json
yarn dlx digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json
bunx --bun digdesign@latest add https://docs.digdesign.ir/r/avatar.json https://docs.digdesign.ir/r/badge.json https://docs.digdesign.ir/r/button.json https://docs.digdesign.ir/r/card.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/progress.json

۲. کد صفحه

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

referral.tsx
"use client"

import * as React from "react"

import { BaleLogo, CheckIcon, CopyIcon, ShareIcon, TelegramLogo } from "@/components/icons"
import { Avatar } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Progress } from "@/components/ui/progress"
import { formatCurrency, formatJalali, toFaDigits } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"

const CODE = "ARMITA-2405"
const LINK = "https://avand.example/invite/ARMITA-2405"
const REWARD = 200_000
const GOAL = 10

const STEPS = [
  { title: "لینک را بفرست", text: "از تلگرام، بله یا هر جای دیگر." },
  { title: "دوستت ثبت‌نام کند", text: "با لینک یا واردکردنِ کدِ تو." },
  { title: "هر دو هدیه بگیرید", text: `بعد از اولین خریدش، ${formatCurrency(REWARD)} برای هر کدام.` },
]

const FRIENDS: { name: string; image: StockImageName; date: string; status: "paid" | "joined" }[] = [
  { name: "سارا محمدی", image: "personWoman2", date: "2026-09-30", status: "paid" },
  { name: "امیر رضایی", image: "personMan1", date: "2026-09-21", status: "paid" },
  { name: "نگار احمدی", image: "personWoman3", date: "2026-09-14", status: "joined" },
  { name: "علی کاظمی", image: "personMan2", date: "2026-09-02", status: "paid" },
]

/** دکمهٔ کپی که نتیجه را همان‌جا روی خودش نشان می‌دهد، نه در اعلانی دور از چشم. */
function CopyButton({ value, label, className }: { value: string; label: string; className?: string }) {
  const [state, setState] = React.useState<"idle" | "copied" | "failed">("idle")

  async function copy() {
    try {
      await navigator.clipboard.writeText(value)
      setState("copied")
    } catch {
      setState("failed")
    }
    setTimeout(() => setState("idle"), 2000)
  }

  return (
    <Button onClick={copy} aria-label={state === "copied" ? `${label} کپی شد` : `کپی ${label}`} className={`min-w-28 ${className ?? ""}`}>
      {state === "copied" ? <CheckIcon /> : <CopyIcon />}
      {state === "copied" ? "کپی شد" : state === "failed" ? "کپی نشد" : "کپی لینک"}
      <span aria-live="polite" className="sr-only">
        {state === "copied" ? `${label} کپی شد` : ""}
      </span>
    </Button>
  )
}

/**
 * دعوتِ دوستان: هیروی گرادیانی با تیترِ هدیه، کوپنِ سفیدِ کد با کپی و اشتراک در پیام‌رسان‌ها؛
 * سه قدمِ «چطور کار می‌کند»، پیشرفت تا هدفِ ده دعوت، و فهرستِ دوستان با عکس و وضعیت.
 */
export default function ReferralBlock() {
  const paid = FRIENDS.filter((friend) => friend.status === "paid").length

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-4xl flex-col gap-8 p-4 sm:p-6">
        {/* هیروی طراحی‌شده: گرادیانِ بنفش به نیلی با درخششِ شعاعی، پترنِ نقطه‌ای و حلقه‌های تزئینی؛
            کد روی یک «کوپنِ» سفید با لبهٔ خط‌چین، نه فیلدی هم‌رنگِ زمینه. */}
        <section className="relative isolate grid grid-cols-1 gap-8 overflow-hidden rounded-xl bg-[linear-gradient(135deg,var(--dig-violet-700),var(--dig-indigo-800))] p-5 text-[var(--dig-white)] [--muted-foreground:var(--dig-violet-100)] sm:p-10 lg:grid-cols-[minmax(0,1fr)_20rem] lg:items-center">
          <span aria-hidden className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(circle_at_90%_0%,var(--dig-violet-400),transparent_45%),radial-gradient(circle_at_0%_100%,var(--dig-blue-500),transparent_40%)]" />
          <span aria-hidden className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(color-mix(in_oklab,var(--dig-white)_35%,transparent)_1.2px,transparent_1.3px)] [background-size:18px_18px] [mask-image:linear-gradient(to_left,black,transparent_60%)]" />
          <span aria-hidden className="pointer-events-none absolute -end-16 -bottom-24 -z-10 size-72 rounded-full border-[28px] border-[color-mix(in_oklab,var(--dig-white)_10%,transparent)]" />
          <span aria-hidden className="pointer-events-none absolute end-24 -top-10 -z-10 size-28 rounded-full border-[14px] border-[color-mix(in_oklab,var(--dig-white)_8%,transparent)]" />

          <div className="flex flex-col gap-5">
            <span className="flex w-fit items-center gap-1.5 rounded-full bg-[var(--dig-white)] px-3 py-1 text-sm font-bold text-[var(--dig-violet-700)]">
              دعوتِ دوستان
            </span>
            <div className="flex flex-col gap-2">
              <h1 className="text-2xl leading-snug font-black text-balance sm:text-4xl">
                دوستت را بیاور، هر دو {formatCurrency(REWARD)} هدیه بگیرید
              </h1>
              <p className="text-sm text-muted-foreground sm:text-base">بعد از اولین خریدِ دوستت، هدیه به کیفِ پولِ هر دو می‌نشیند.</p>
            </div>
            <div className="grid grid-cols-3 gap-2 sm:flex sm:flex-wrap">
              {[
                { label: "تلگرام", icon: <TelegramLogo /> },
                { label: "بله", icon: <BaleLogo /> },
                { label: "اشتراک", icon: <ShareIcon /> },
              ].map((item) => (
                <Button key={item.label} className="text-[var(--dig-white)] bg-[color-mix(in_oklab,var(--dig-white)_14%,var(--dig-violet-800))] hover:bg-[color-mix(in_oklab,var(--dig-white)_22%,var(--dig-violet-800))]" aria-label={`فرستادن با ${item.label}`}>
                  {item.icon}
                  {item.label}
                </Button>
              ))}
            </div>
          </div>

          {/* کوپن: سفید، با کد درشت و لبهٔ خط‌چین؛ کپی همان‌جاست. */}
          <div className="flex flex-col gap-4 rounded-xl bg-[var(--dig-white)] p-5 text-[var(--dig-slate-900)] shadow-card">
            <div className="flex items-center justify-between gap-3">
              <span className="text-sm font-medium text-[var(--dig-slate-500)]">کدِ دعوتِ تو</span>
              <span className="text-xs font-bold text-[var(--dig-violet-700)]">{toFaDigits(paid)} دعوتِ موفق</span>
            </div>
            <bdi dir="ltr" className="rounded-lg border-2 border-dashed border-[var(--dig-violet-300)] bg-[var(--dig-violet-50)] py-3 text-center font-mono text-xl font-black tracking-widest text-[var(--dig-violet-800)]">
              {CODE}
            </bdi>
            <CopyButton value={LINK} label="لینک دعوت" className="w-full bg-[var(--dig-violet-700)] text-[var(--dig-white)] hover:bg-[var(--dig-violet-800)]" />
          </div>
        </section>

        <section aria-labelledby="referral-how" className="flex flex-col gap-5">
          <h2 id="referral-how" className="text-lg font-bold">
            چطور کار می‌کند؟
          </h2>
          {/* در موبایل هر قدم یک ردیفِ کوتاه است (عدد کنارِ متن)، نه کارتی بلند. */}
          <ol className="grid grid-cols-1 gap-2 sm:grid-cols-3 sm:gap-4">
            {STEPS.map((step, index) => (
              <li key={step.title} className="flex items-start gap-3 rounded-xl bg-subtle p-4 sm:flex-col sm:gap-2 sm:p-5">
                <span className="flex size-8 shrink-0 items-center justify-center rounded-full bg-[var(--dig-violet-100)] text-sm font-bold text-[var(--dig-violet-800)]">
                  {toFaDigits(index + 1)}
                </span>
                <span className="flex flex-col gap-1">
                  <span className="font-semibold">{step.title}</span>
                  <span className="text-sm leading-6 text-muted-foreground">{step.text}</span>
                </span>
              </li>
            ))}
          </ol>
        </section>

        <div className="grid grid-cols-1 items-start gap-6 lg:grid-cols-[minmax(0,1fr)_18rem]">
          <Card>
            <CardHeader>
              <CardTitle className="text-base">دوستانِ دعوت‌شده</CardTitle>
            </CardHeader>
            <CardContent>
              <ul className="flex flex-col divide-y divide-border">
                {FRIENDS.map((friend) => (
                  <li key={friend.name} className="flex items-center gap-3 py-3 first:pt-0 last:pb-0">
                    <Avatar alt={friend.name} src={stockImages[friend.image].src} />
                    <div className="flex flex-1 flex-col">
                      <span className="font-medium">{friend.name}</span>
                      <span className="text-xs text-muted-foreground">{formatJalali(new Date(friend.date))}</span>
                    </div>
                    {friend.status === "paid" ? (
                      <Badge variant="soft" color="success">
                        +{formatCurrency(REWARD)}
                      </Badge>
                    ) : (
                      <Badge variant="soft">منتظرِ اولین خرید</Badge>
                    )}
                  </li>
                ))}
              </ul>
            </CardContent>
          </Card>

          <Card>
            <CardHeader>
              <CardTitle className="text-base">تا هدیهٔ ویژه</CardTitle>
            </CardHeader>
            <CardContent className="flex flex-col gap-4">
              <span className="text-3xl font-black tabular-nums">{formatCurrency(paid * REWARD)}</span>
              <div className="flex flex-col gap-2">
                <Progress value={(FRIENDS.length / GOAL) * 100} aria-label="پیشرفت تا ده دعوت" />
                <span className="text-sm text-muted-foreground">
                  {toFaDigits(FRIENDS.length)} از {toFaDigits(GOAL)} دعوت؛ با دهمی یک ماه اشتراکِ رایگان.
                </span>
              </div>
            </CardContent>
          </Card>
        </div>
      </main>
    </ControlSizeProvider>
  )
}