بلاک‌ها · پنل مدیریت

گزارش‌ها و نمودارها

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

کارت‌های آمارِ hi-fi با بازهٔ ۷/۳۰/۹۰ روز: نمودار خطی، نوار تا هدف، سهم‌ها، دونات کانال‌ها، قیف خرید و عقربهٔ اعتبار.

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

این بلاک از ۵ آیتم دیگ استفاده می‌کند: control-sizeiconspersianstat-tiletoggle-group

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

۲. کد صفحه

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

analytics.tsx
"use client"

import * as React from "react"

import { UsersIcon, WalletIcon } from "@/components/icons"
import {
  StatTile,
  StatTileAction,
  StatTileBar,
  StatTileChart,
  StatTileContent,
  StatTileDescription,
  StatTileGauge,
  StatTileHeader,
  StatTileIcon,
  StatTileLabel,
  StatTileList,
  StatTileMeter,
  StatTileRing,
  StatTileSegments,
  StatTileSparkline,
  StatTileTrend,
  StatTileUnit,
  StatTileValue,
} from "@/components/ui/stat-tile"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { formatNumber } from "@/lib/persian"

/** دادهٔ نمونه برای هر بازه؛ در پروژهٔ واقعی همین‌ها پاسخِ API هستند. */
const DATA = {
  "7": { visits: [42, 48, 45, 52, 61, 58, 66], revenue: 186, orders: 820, conversion: 3.4 },
  "30": { visits: [38, 41, 47, 44, 52, 49, 58, 55, 63, 60, 68, 72], revenue: 824, orders: 3_285, conversion: 3.9 },
  "90": { visits: [30, 34, 33, 41, 39, 45, 50, 47, 55, 58, 61, 66, 70], revenue: 2_410, orders: 9_870, conversion: 4.2 },
} as const

const CHANNELS = [
  { label: "فروشگاه آنلاین", value: 45, color: "var(--chart-1)" },
  { label: "اپلیکیشن", value: 35, color: "var(--chart-2)" },
  { label: "فروش حضوری", value: 20, color: "var(--chart-4)" },
]

export default function AnalyticsBlock() {
  const [period, setPeriod] = React.useState<keyof typeof DATA>("30")
  const data = DATA[period]

  return (
    <ControlSizeProvider size="xl" asChild>
    <main className="mx-auto flex w-full min-w-0 max-w-6xl flex-col gap-6 bg-muted p-4 sm:p-6">
      <header className="flex flex-wrap items-end justify-between gap-x-4 gap-y-8">
        <div className="flex flex-col gap-1">
          <h1 className="text-2xl font-bold">گزارش‌ها</h1>
          <p className="text-sm text-muted-foreground">نمای کلی فروش، بازدید و مشتریان در بازهٔ انتخابی.</p>
        </div>
        <ToggleGroup type="single" variant="segmented" value={period} onValueChange={(value) => value && setPeriod(value as keyof typeof DATA)} aria-label="بازهٔ زمانی">
          <ToggleGroupItem value="7" className="px-4">۷ روز</ToggleGroupItem>
          <ToggleGroupItem value="30" className="px-4">۳۰ روز</ToggleGroupItem>
          <ToggleGroupItem value="90" className="px-4">۹۰ روز</ToggleGroupItem>
        </ToggleGroup>
      </header>

      {/* ردیفِ اول: چهار شاخص، هر کدام یک نوع نمایشِ سبک. */}
      <section aria-label="شاخص‌های کلیدی" className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
        <StatTile className="gap-3">
          <StatTileHeader>
            <StatTileLabel>درآمد</StatTileLabel>
            <StatTileAction aria-label="جزئیات درآمد" />
          </StatTileHeader>
          <StatTileContent>
            <StatTileValue className="text-2xl">
              {formatNumber(data.revenue)}
              <StatTileUnit>میلیون تومان</StatTileUnit>
            </StatTileValue>
            <StatTileTrend value={12.5} formatValue={() => "+۱۲٫۵٪"} />
          </StatTileContent>
          <StatTileSparkline values={[...data.visits]} />
        </StatTile>

        <StatTile className="gap-3">
          <StatTileHeader>
            <StatTileLabel>سفارش‌ها</StatTileLabel>
            <StatTileAction aria-label="جزئیات سفارش‌ها" />
          </StatTileHeader>
          <StatTileContent>
            <StatTileValue className="text-2xl">{formatNumber(data.orders)}</StatTileValue>
            <span className="ms-auto text-sm text-muted-foreground">
              <strong className="font-semibold text-foreground">٪۴۲</strong> تا هدف
            </span>
          </StatTileContent>
          <StatTileMeter value={42} />
        </StatTile>

        <StatTile className="gap-3">
          <StatTileHeader>
            <StatTileLabel>مشتریان تازه</StatTileLabel>
            <StatTileAction aria-label="جزئیات مشتریان" />
          </StatTileHeader>
          <div className="grid grid-cols-[62fr_38fr] gap-1.5">
            <div className="flex flex-col gap-1.5 border-s-2 border-info ps-3">
              <span className="text-2xl font-bold tabular-nums">{formatNumber(302)}</span>
              <span className="text-xs text-muted-foreground">خرید مستقیم</span>
            </div>
            <div className="flex flex-col gap-1.5 border-s-2 border-warning ps-3">
              <span className="text-2xl font-bold tabular-nums">{formatNumber(184)}</span>
              <span className="text-xs text-muted-foreground">معرفی</span>
            </div>
          </div>
          <StatTileSegments
            segments={[
              { value: 302, color: "var(--info)" },
              { value: 184, color: "var(--warning)" },
            ]}
          />
        </StatTile>

        <StatTile className="gap-3">
          <StatTileHeader>
            <StatTileLabel>نرخ تبدیل</StatTileLabel>
            <StatTileAction aria-label="جزئیات نرخ تبدیل" />
          </StatTileHeader>
          <div className="flex items-end justify-between gap-3">
            <div className="flex flex-col gap-1.5">
              <StatTileValue className="text-2xl">٪{formatNumber(data.conversion)}</StatTileValue>
              <StatTileTrend value={9.2} formatValue={() => "+۹٫۲٪"} />
            </div>
            <StatTileChart
              className="h-20 min-w-0 max-w-44 flex-1"
              color="var(--success)"
              data={[
                { label: "تیر", value: 40 },
                { label: "مرداد", value: 36 },
                { label: "شهریور", value: 33 },
                { label: "مهر", value: 45 },
              ]}
            />
          </div>
        </StatTile>
      </section>

      {/* ردیفِ دوم: نمودارهای بزرگ‌تر، کنارِ هم با ارتفاعِ برابر. */}
      <section aria-label="جزئیات" className="grid grid-cols-1 gap-4 lg:grid-cols-3">
        <StatTile>
          <StatTileHeader>
            <StatTileLabel className="text-base text-foreground">سهمِ کانال‌های فروش</StatTileLabel>
            <StatTileAction aria-label="جزئیات کانال‌ها" />
          </StatTileHeader>
          <StatTileRing segments={CHANNELS}>
            <span className="text-2xl font-bold tabular-nums">{formatNumber(1_560)}</span>
            <span className="text-xs text-muted-foreground">سفارش</span>
          </StatTileRing>
          <ul className="flex flex-col gap-2.5 text-sm">
            {CHANNELS.map((item) => (
              <li key={item.label} className="flex items-center gap-2">
                <span aria-hidden className="size-2.5 rounded-full" style={{ background: item.color }} />
                <span className="text-muted-foreground">{item.label}</span>
                <span className="ms-auto font-semibold tabular-nums">٪{formatNumber(item.value)}</span>
              </li>
            ))}
          </ul>
        </StatTile>

        <StatTile>
          <StatTileHeader>
            <StatTileLabel className="text-base text-foreground">قیفِ خرید</StatTileLabel>
            <StatTileAction aria-label="جزئیات قیف" />
          </StatTileHeader>
          <StatTileContent>
            <StatTileValue className="text-3xl">٪۶۲</StatTileValue>
            <StatTileDescription>به سبد خرید رسیده‌اند</StatTileDescription>
          </StatTileContent>
          <StatTileList
            items={[
              { label: "بازدید", value: 100, display: "٪۱۰۰" },
              { label: "دیدنِ محصول", value: 74, display: "٪۷۴" },
              { label: "افزودن به سبد", value: 62, display: "٪۶۲" },
              { label: "شروعِ پرداخت", value: 38, display: "٪۳۸" },
              { label: "خریدِ موفق", value: 21, display: "٪۲۱" },
            ]}
          />
        </StatTile>

        <StatTile>
          <StatTileHeader>
            <StatTileLabel className="text-base text-foreground">اعتبارِ پیامک</StatTileLabel>
            <StatTileAction aria-label="خرید اعتبار پیامک" />
          </StatTileHeader>
          <StatTileGauge value={62} className="mt-2">
            <span className="text-3xl font-bold tabular-nums">{formatNumber(750)}</span>
            <span className="text-xs text-muted-foreground">پیامک باقی‌مانده</span>
          </StatTileGauge>
          <StatTileDescription className="text-center">
            با این روند، اعتبار تا <strong>۱۲ روز</strong> دیگر کافی است.
          </StatTileDescription>
        </StatTile>
      </section>

      {/* ردیفِ سوم: شاخص‌های عملیاتی با آیکون و نوارِ پیشرفت. */}
      <section aria-label="عملیات" className="grid grid-cols-1 gap-4 sm:grid-cols-2">
        <StatTile>
          <StatTileHeader>
            <StatTileIcon tone="success">
              <UsersIcon />
            </StatTileIcon>
            <StatTileLabel>تیکت‌های پاسخ‌داده‌شده</StatTileLabel>
          </StatTileHeader>
          <StatTileContent>
            <StatTileValue>٪۹۲</StatTileValue>
            <StatTileTrend value={12} formatValue={() => "+۱۲٪"} />
          </StatTileContent>
          <StatTileBar value={92} />
          <StatTileDescription>تقریباً همهٔ تیکت‌ها زیرِ یک ساعت پاسخ گرفته‌اند.</StatTileDescription>
        </StatTile>
        <StatTile>
          <StatTileHeader>
            <StatTileIcon tone="accent">
              <WalletIcon />
            </StatTileIcon>
            <StatTileLabel>میانگینِ سبد خرید</StatTileLabel>
          </StatTileHeader>
          <StatTileContent>
            <StatTileValue>
              {formatNumber(562_000)}
              <StatTileUnit>تومان</StatTileUnit>
            </StatTileValue>
            <StatTileTrend value={-3.1} formatValue={() => "−۳٫۱٪"} />
          </StatTileContent>
          <StatTileSparkline values={[6, 5.6, 5.9, 5.2, 5.4, 4.9, 5.1, 4.8]} color="var(--destructive)" />
        </StatTile>
      </section>
    </main>
    </ControlSizeProvider>
  )
}