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

فهرستِ همکاران

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

فهرستِ همکاران با جست‌وجو و سگمنتِ تیم، کارتِ عکس‌دار با نقش و وضعیتِ آنلاین، و شیتِ پروفایل با ایمیل، تلفن و تماس.

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

این بلاک از ۹ آیتم دیگ استفاده می‌کند: avatarbadgebuttoncontrol-sizeiconspersiansearch-fieldsheettoggle-group

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

۲. کد صفحه

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

team-directory.tsx
"use client"

import * as React from "react"

import { EnvelopeIcon, PhoneIcon } from "@/components/icons"
import { Avatar } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { ControlSizeProvider } from "@/components/ui/control-size"
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet"
import { SearchField } from "@/components/ui/search-field"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { toFaDigits, toSearchKey } from "@/lib/persian"
import { stockImages, type StockImageName } from "@/lib/stock-images"

type Person = { name: string; role: string; team: string; photo: StockImageName; email: string; phone: string; online?: boolean }
const PEOPLE: Person[] = [
  { name: "مهسا کریمی", role: "مدیرِ محصول", team: "محصول", photo: "personWoman1", email: "mahsa@avand.example", phone: "09121234567", online: true },
  { name: "امیر رضایی", role: "توسعه‌دهندهٔ ارشد", team: "فنی", photo: "personMan1", email: "amir@avand.example", phone: "09127654321", online: true },
  { name: "سارا محمدی", role: "طراحِ محصول", team: "محصول", photo: "personWoman2", email: "sara@avand.example", phone: "09131112233" },
  { name: "علی کاظمی", role: "مدیرِ فروش", team: "فروش", photo: "personMan2", email: "ali@avand.example", phone: "09351234567", online: true },
  { name: "نگار احمدی", role: "حسابدار", team: "مالی", photo: "personWoman3", email: "negar@avand.example", phone: "09191234567" },
]
const TEAMS = ["همه", "محصول", "فنی", "فروش", "مالی"]

/**
 * فهرستِ همکاران: جست‌وجو و فیلترِ تیم با سگمنت، کارتِ هر نفر با عکس، نقش و وضعیتِ آنلاین؛
 * لمسِ کارت شیتِ کناری پروفایل را با ایمیل و تلفن (چپ‌به‌راست) و اقدامِ تماس باز می‌کند.
 */
export default function TeamDirectoryBlock() {
  const [query, setQuery] = React.useState("")
  const [team, setTeam] = React.useState("همه")
  const [open, setOpen] = React.useState<Person | null>(null)
  const visible = PEOPLE.filter((person) => (team === "همه" || person.team === team) && toSearchKey(person.name + person.role).includes(toSearchKey(query)))

  return (
    <ControlSizeProvider size="xl" asChild>
      <main className="mx-auto flex w-full max-w-5xl 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(PEOPLE.length)} نفر در {toFaDigits(TEAMS.length - 1)} تیم</p>
        </header>
        <div className="flex flex-wrap items-center gap-3">
          <SearchField aria-label="جست‌وجوی همکار" placeholder="نام یا نقش" value={query} onValueChange={setQuery} className="w-full sm:max-w-xs" />
          <div className="max-w-full overflow-x-auto">
            <ToggleGroup type="single" variant="segmented" value={team} onValueChange={(value) => value && setTeam(value as string)} aria-label="تیم">
              {TEAMS.map((item) => (
                <ToggleGroupItem key={item} value={item} className="whitespace-nowrap">
                  {item}
                </ToggleGroupItem>
              ))}
            </ToggleGroup>
          </div>
        </div>
        <ul className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
          {visible.map((person) => (
            <li key={person.name}>
              <Button variant="outline" onClick={() => setOpen(person)} className="h-auto w-full justify-start gap-4 p-4 text-start font-normal">
                <span className="relative shrink-0">
                  <Avatar size="lg" alt="" src={stockImages[person.photo].src} />
                  {person.online && <span aria-label="آنلاین" className="absolute -end-0.5 -bottom-0.5 size-3.5 rounded-full bg-success ring-2 ring-background" />}
                </span>
                <span className="flex min-w-0 flex-col gap-1">
                  <span className="font-bold">{person.name}</span>
                  <span className="text-sm text-muted-foreground">{person.role}</span>
                  <Badge variant="soft" className="self-start">
                    {person.team}
                  </Badge>
                </span>
              </Button>
            </li>
          ))}
        </ul>
        {visible.length === 0 && <p className="rounded-xl bg-subtle p-8 text-center text-muted-foreground">همکاری با این نام پیدا نشد.</p>}

        <Sheet open={open !== null} onOpenChange={(value) => !value && setOpen(null)}>
          <ControlSizeProvider size="xl" asChild>
            <SheetContent side="end" className="flex w-full max-w-sm flex-col gap-6 p-6">
              {open && (
                <>
                  <SheetHeader className="items-center gap-3 p-0 text-center">
                    <Avatar alt="" src={stockImages[open.photo].src} className="size-24" />
                    <SheetTitle className="text-xl">{open.name}</SheetTitle>
                    <SheetDescription>
                      {open.role} · تیمِ {open.team}
                    </SheetDescription>
                  </SheetHeader>
                  <dl className="flex flex-col gap-4 rounded-xl bg-subtle p-4 text-sm">
                    <div className="flex items-center justify-between gap-3">
                      <dt className="text-muted-foreground">ایمیل</dt>
                      <dd>
                        <bdi dir="ltr">{open.email}</bdi>
                      </dd>
                    </div>
                    <div className="flex items-center justify-between gap-3">
                      <dt className="text-muted-foreground">موبایل</dt>
                      <dd>
                        <bdi dir="ltr">{toFaDigits(open.phone)}</bdi>
                      </dd>
                    </div>
                  </dl>
                  <div className="mt-auto flex gap-3">
                    <Button asChild className="flex-1">
                      <a href={`mailto:${open.email}`}>
                        <EnvelopeIcon />
                        ایمیل
                      </a>
                    </Button>
                    <Button asChild variant="outline" className="flex-1">
                      <a href={`tel:${open.phone}`}>
                        <PhoneIcon />
                        تماس
                      </a>
                    </Button>
                  </div>
                </>
              )}
            </SheetContent>
          </ControlSizeProvider>
        </Sheet>
      </main>
    </ControlSizeProvider>
  )
}