فهرستِ همکاران
پیشنمایش تمامصفحهفهرستِ همکاران با جستوجو و سگمنتِ تیم، کارتِ عکسدار با نقش و وضعیتِ آنلاین، و شیتِ پروفایل با ایمیل، تلفن و تماس.
۱. نصب کامپوننتها
این بلاک از ۹ آیتم دیگ استفاده میکند: 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.jsonpnpm 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.jsonyarn 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.jsonbunx --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>
)
}