← Back to application blocks
team-01
A team management card listing members with avatars and per-member role selects, plus an invite-by-email row. Use in settings or workspace admin pages.
Code
import * as React from "react"
import { cn } from "@/lib/utils"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
const members = [
{
name: "Sofia Davis",
email: "sofia@example.com",
role: "owner",
avatar: "https://github.com/shadcn.png",
initials: "SD",
},
{
name: "Jackson Lee",
email: "jackson@example.com",
role: "member",
avatar: "",
initials: "JL",
},
{
name: "Isabella Nguyen",
email: "isabella@example.com",
role: "member",
avatar: "",
initials: "IN",
},
]
const roles = [
{ value: "owner", label: "Owner" },
{ value: "member", label: "Member" },
{ value: "viewer", label: "Viewer" },
]
export function TeamMembers({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
className={cn(
"mx-auto flex w-full max-w-xl flex-col px-6 py-16",
className
)}
{...props}
>
<Card>
<CardHeader>
<CardTitle>Team members</CardTitle>
<CardDescription>
Invite your team members to collaborate.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
{members.map((member) => (
<div
key={member.email}
className="flex items-center justify-between gap-4"
>
<div className="flex items-center gap-3">
<Avatar>
{member.avatar ? (
<AvatarImage src={member.avatar} alt={member.name} />
) : null}
<AvatarFallback>{member.initials}</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium leading-none">
{member.name}
</p>
<p className="text-sm text-muted-foreground">
{member.email}
</p>
</div>
</div>
<Select defaultValue={member.role}>
<SelectTrigger
className="w-[110px]"
aria-label={`Role for ${member.name}`}
>
<SelectValue />
</SelectTrigger>
<SelectContent>
{roles.map((role) => (
<SelectItem key={role.value} value={role.value}>
{role.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
))}
<Separator />
<div className="grid gap-2">
<p className="text-sm font-medium">Invite by email</p>
<div className="flex gap-2">
<Input type="email" placeholder="email@example.com" />
<Button>Invite</Button>
</div>
</div>
</CardContent>
<CardFooter className="text-sm text-muted-foreground">
Members can view and edit shared projects.
</CardFooter>
</Card>
</div>
)
}