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

اعلان‌های دسته‌بندی‌شده

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

هر منبع یک کارت با تازه‌ترین اعلان و «چند اعلانِ دیگر» بازشونده؛ برای کاهشِ شلوغیِ اعلان‌ها.

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

این بلاک از ۴ آیتم دیگ استفاده می‌کند: buttoncontrol-sizeiconspersian

پکیج‌منیجر پیش‌فرض 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/icons.json https://docs.digdesign.ir/r/persian.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/icons.json https://docs.digdesign.ir/r/persian.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/icons.json https://docs.digdesign.ir/r/persian.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/icons.json https://docs.digdesign.ir/r/persian.json

۲. کد صفحه

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

notifications-grouped.tsx
"use client"

import * as React from "react"

import { ChevronDownIcon, ReceiptIcon, TruckIcon, UsersIcon } from "@/components/icons"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { toFaDigits } from "@/lib/persian"

const GROUPS = [
  { key: "orders", title: "سفارش‌ها", icon: <TruckIcon />, items: ["سفارشِ ۸۸۲۱۰۴۲ ارسال شد", "سفارشِ ۸۸۲۱۰۴۱ تحویل شد", "سفارشِ ۸۸۲۱۰۴۰ آماده‌سازی شد", "سفارشِ ۸۸۲۱۰۳۹ مرجوع شد"], time: "۱۰ دقیقه پیش" },
  { key: "team", title: "تیم", icon: <UsersIcon />, items: ["سارا محمدی به تیمِ فروش پیوست", "امیر رضایی نقشِ مدیر گرفت"], time: "۲ ساعت پیش" },
  { key: "finance", title: "مالی", icon: <ReceiptIcon />, items: ["پرداختِ فاکتورِ ۱۴۰۵-۰۲۳۱ انجام شد"], time: "دیروز" },
]

/**
 * اعلان‌های دسته‌بندی‌شده برای کاهشِ شلوغی: هر منبع یک کارت با تازه‌ترین اعلان و «۳ اعلانِ
 * دیگر» که باز و بسته می‌شود (aria-expanded)؛ کاربر به‌جای ده ردیف، سه خبرِ اصلی می‌بیند.
 */
export default function NotificationsGroupedBlock() {
  const [open, setOpen] = React.useState<string[]>([])

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-2xl flex-col gap-8 p-4 sm:p-6">
        <header className="flex flex-col gap-1">
          <h1 className="text-2xl font-bold">اعلان‌ها</h1>
          <p className="text-muted-foreground">{toFaDigits(GROUPS.reduce((sum, group) => sum + group.items.length, 0))} اعلان از {toFaDigits(GROUPS.length)} بخش</p>
        </header>
        <ul className="flex flex-col gap-3">
          {GROUPS.map((group) => {
            const expanded = open.includes(group.key)
            const rest = group.items.length - 1
            return (
              <li key={group.key} className="flex flex-col rounded-xl border border-border bg-card">
                <div className="flex items-start gap-3 p-4">
                  <span aria-hidden className="flex size-10 shrink-0 items-center justify-center rounded-full bg-subtle text-brand-secondary [&>svg]:size-5">
                    {group.icon}
                  </span>
                  <div className="flex flex-1 flex-col gap-1">
                    <span className="flex items-center justify-between gap-2">
                      <span className="text-sm font-semibold text-muted-foreground">{group.title}</span>
                      <span className="text-xs text-muted-foreground">{group.time}</span>
                    </span>
                    <span className="font-medium">{group.items[0]}</span>
                  </div>
                </div>
                {rest > 0 && (
                  <>
                    {expanded && (
                      <ul id={`ng-${group.key}`} className="flex flex-col gap-2 border-t border-border px-4 py-3 ps-[4.25rem]">
                        {group.items.slice(1).map((item) => (
                          <li key={item} className="text-sm text-muted-foreground">
                            {item}
                          </li>
                        ))}
                      </ul>
                    )}
                    <Button
                      variant="ghost"
                      aria-expanded={expanded}
                      aria-controls={`ng-${group.key}`}
                      onClick={() => setOpen((list) => (expanded ? list.filter((key) => key !== group.key) : [...list, group.key]))}
                      className="justify-center gap-2 rounded-t-none border-t border-border"
                    >
                      {expanded ? "بستن" : `${toFaDigits(rest)} اعلانِ دیگر`}
                      <ChevronDownIcon className={expanded ? "rotate-180" : ""} />
                    </Button>
                  </>
                )}
              </li>
            )
          })}
        </ul>
      </main>
    </ControlSizeProvider>
  )
}