Files
reui-blocks/blocks/settings-members5.tsx
T

353 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import {
Clock,
MoreVertical,
Send,
UserCheck,
UserPlus,
X,
} from "lucide-react";
import { useState } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
interface ActiveMember {
id: string;
name: string;
email: string;
role: string;
avatar?: string;
lastActive: string;
}
interface PendingInvite {
id: string;
email: string;
role: string;
sentAt: string;
expiresIn: string;
}
interface SettingsMembers5Props {
heading?: string;
description?: string;
activeMembers?: ActiveMember[];
pendingInvites?: PendingInvite[];
className?: string;
}
const SettingsMembers5 = ({
heading = "مدیریت تیم",
description = "اعضای فعال فضای کاری را ببینید و دعوت‌های باز را پیگیری کنید.",
activeMembers = [
{
id: "1",
name: "پریا شمیرانی",
email: "priya@designlab.io",
role: "مالک",
avatar:
"/block-media/shadcnblocks/block/avatar/avatar11.jpg",
lastActive: "الان",
},
{
id: "2",
name: "کاوه ابریشمی",
email: "kevin@designlab.io",
role: "مدیر عملیات",
avatar:
"/block-media/shadcnblocks/block/avatar/avatar12.jpg",
lastActive: "۲ ساعت پیش",
},
{
id: "3",
name: "یگانه تنکابنی",
email: "yuki@designlab.io",
role: "فروشنده",
avatar:
"/block-media/shadcnblocks/block/avatar/avatar13.jpg",
lastActive: "دیروز",
},
{
id: "4",
name: "کیان منتظری",
email: "carlos@designlab.io",
role: "فروشنده",
avatar:
"/block-media/shadcnblocks/block/avatar/avatar14.jpg",
lastActive: "۳ روز پیش",
},
],
pendingInvites = [
{
id: "p1",
email: "maya.wilson@gmail.com",
role: "فروشنده",
sentAt: "۱۵ ژانویه ۲۰۲۶",
expiresIn: "۵ روز",
},
{
id: "p2",
email: "alex.foster@outlook.com",
role: "فقط مشاهده",
sentAt: "۲۰ ژانویه ۲۰۲۶",
expiresIn: "۱۰ روز",
},
],
className,
}: SettingsMembers5Props) => {
const [isInviteOpen, setIsInviteOpen] = useState(false);
return (
<section dir="rtl" lang="fa" className={cn("w-full py-32", className)}>
<div className="w-full px-4 sm:px-6 lg:px-8">
<div className="flex w-full flex-col gap-6">
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div className="space-y-1">
<h2 className="text-xl font-semibold tracking-tight">
{heading}
</h2>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
<Dialog open={isInviteOpen} onOpenChange={setIsInviteOpen}>
<DialogTrigger asChild>
<Button>
<UserPlus className="size-4" />
دعوت عضو
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>دعوت عضو جدید</DialogTitle>
<DialogDescription>
دعوتنامه ایمیلی برای پیوستن به فضای کاری ارسال کنید.
</DialogDescription>
</DialogHeader>
<form
onSubmit={(e) => {
e.preventDefault();
setIsInviteOpen(false);
}}
className="space-y-4"
>
<div className="space-y-2">
<Label htmlFor="new-email">ایمیل سازمانی</Label>
<Input
id="new-email"
type="email"
placeholder="colleague@example.com"
/>
</div>
<div className="space-y-2">
<Label htmlFor="new-role">نقش دسترسی</Label>
<Select defaultValue="editor">
<SelectTrigger id="new-role">
<SelectValue placeholder="انتخاب نقش" />
</SelectTrigger>
<SelectContent>
<SelectItem value="admin">مدیر عملیات</SelectItem>
<SelectItem value="editor">فروشنده</SelectItem>
<SelectItem value="viewer">فقط مشاهده</SelectItem>
</SelectContent>
</Select>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => setIsInviteOpen(false)}
>
انصراف
</Button>
<Button type="submit">
<Send className="size-4" />
ارسال دعوتنامه
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
<Tabs defaultValue="active" className="w-full">
<TabsList className="w-full justify-start">
<TabsTrigger value="active" className="gap-2">
<UserCheck className="size-4" />
اعضای فعال
<Badge
variant="secondary"
className="ms-1 size-5 justify-center rounded-full p-0 text-xs"
>
{activeMembers.length.toLocaleString("fa-IR")}
</Badge>
</TabsTrigger>
<TabsTrigger value="pending" className="gap-2">
<Clock className="size-4" />
دعوت‌های باز
{pendingInvites.length > 0 && (
<Badge
variant="secondary"
className="ms-1 size-5 justify-center rounded-full bg-amber-100 p-0 text-xs text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"
>
{pendingInvites.length.toLocaleString("fa-IR")}
</Badge>
)}
</TabsTrigger>
</TabsList>
<TabsContent value="active" className="mt-4">
<div className="rounded-lg border">
<ul className="divide-y">
{activeMembers.map((member) => (
<li
key={member.id}
className="flex items-center justify-between gap-4 px-4 py-3"
>
<div className="flex items-center gap-3">
<Avatar className="size-10">
<AvatarImage src={member.avatar} alt={`آواتار ${member.name}`} />
<AvatarFallback>
{member.name
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div>
<p className="text-sm font-medium">{member.name}</p>
<p className="text-xs text-muted-foreground">
{member.email}
</p>
</div>
</div>
<div className="flex items-center gap-3">
<div className="hidden text-end sm:block">
<Badge variant="outline" className="font-normal">
{member.role}
</Badge>
<p className="mt-1 text-xs text-muted-foreground">
{member.lastActive === "الان" ? (
<span className="flex items-center gap-1">
<span className="size-2 rounded-full bg-green-500" />
آنلاین
</span>
) : (
`آخرین فعالیت · ${member.lastActive}`
)}
</p>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8"
>
<MoreVertical className="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>ویرایش سطح دسترسی</DropdownMenuItem>
<DropdownMenuItem>مشاهده پروفایل عضو</DropdownMenuItem>
<DropdownMenuItem className="text-destructive">
حذف از تیم
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</li>
))}
</ul>
</div>
</TabsContent>
<TabsContent value="pending" className="mt-4">
{pendingInvites.length === 0 ? (
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed py-12 text-center">
<Clock className="mb-3 size-10 text-muted-foreground/50" />
<p className="text-sm font-medium">دعوت بازی وجود ندارد</p>
<p className="text-xs text-muted-foreground">
برای گسترش تیم، اعضای جدید را دعوت کنید
</p>
</div>
) : (
<div className="rounded-lg border">
<ul className="divide-y">
{pendingInvites.map((invite) => (
<li
key={invite.id}
className="flex items-center justify-between gap-4 px-4 py-3"
>
<div className="flex items-center gap-3">
<div className="flex size-10 items-center justify-center rounded-full bg-muted">
<Clock className="size-4 text-muted-foreground" />
</div>
<div>
<p className="text-sm font-medium">
{invite.email}
</p>
<p className="text-xs text-muted-foreground">
ارسال در {invite.sentAt} · انقضا:{" "}
{invite.expiresIn}
</p>
</div>
</div>
<div className="flex items-center gap-2">
<Badge variant="outline" className="font-normal">
{invite.role}
</Badge>
<Button variant="ghost" size="sm">
<Send className="size-4" />
ارسال مجدد دعوت
</Button>
<Button
variant="ghost"
size="icon"
className="size-8 text-muted-foreground hover:text-destructive"
>
<X className="size-4" />
</Button>
</div>
</li>
))}
</ul>
</div>
)}
</TabsContent>
</Tabs>
</div>
</div>
</section>
);
};
export { SettingsMembers5 };