267 lines
8.1 KiB
TypeScript
267 lines
8.1 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
CornerDownLeft,
|
|
Search,
|
|
SlidersHorizontal,
|
|
UserRoundPlus,
|
|
} from "lucide-react";
|
|
import { useMemo, useState } from "react";
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface User {
|
|
name?: string;
|
|
email: string;
|
|
accessLevel: string;
|
|
status?: string;
|
|
}
|
|
|
|
interface UsersTableProps {
|
|
users: User[];
|
|
}
|
|
|
|
const UsersTable = ({ users }: UsersTableProps) => {
|
|
return (
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>عضو</TableHead>
|
|
<TableHead>سطح دسترسی</TableHead>
|
|
<TableHead></TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{users.map((user) => (
|
|
<TableRow key={user.email}>
|
|
<TableCell className="flex max-w-56 items-center gap-2 font-medium">
|
|
<span className="flex size-6 shrink-0 items-center justify-center rounded-full bg-primary text-background">
|
|
{user.name ? user.name.charAt(0) : "?"}
|
|
</span>
|
|
{user.name}
|
|
<span className="text-muted-foreground">{user.email}</span>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Badge variant="secondary">{user.accessLevel}</Badge>
|
|
</TableCell>
|
|
<TableCell>
|
|
{user.status === "دعوت در انتظار" && (
|
|
<Badge className="bg-blue-100 text-blue-800">
|
|
{user.status}
|
|
</Badge>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
);
|
|
};
|
|
|
|
const InviteUserForm = () => {
|
|
return (
|
|
<Dialog>
|
|
<DialogTrigger asChild>
|
|
<Button>
|
|
<UserRoundPlus /> دعوت عضو
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="gap-0 overflow-hidden p-0">
|
|
<DialogTitle className="flex items-center gap-2 border-b p-4 text-sm font-medium">
|
|
<UserRoundPlus className="size-4" />
|
|
دعوت عضو جدید
|
|
</DialogTitle>
|
|
<form
|
|
autoComplete="off"
|
|
onSubmit={(e) => e.preventDefault()}
|
|
className="flex flex-col gap-4 bg-muted pt-4"
|
|
>
|
|
<div className="flex flex-col gap-4 px-4">
|
|
<div className="space-y-1">
|
|
<Label className="text-xs">ایمیل سازمانی</Label>
|
|
<Input
|
|
placeholder="colleague@company.com"
|
|
className="bg-background"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<Label className="text-xs">نقش دسترسی</Label>
|
|
<Select defaultValue="editor">
|
|
<SelectTrigger className="w-full bg-background">
|
|
<SelectValue placeholder="انتخاب نقش" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="owner">مالک فضای کاری</SelectItem>
|
|
<SelectItem value="editor">فروشنده</SelectItem>
|
|
<SelectItem value="viewer">فقط مشاهده</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
</div>
|
|
<DialogFooter className="border-t bg-background px-4 py-3">
|
|
<Button size="sm" type="submit">
|
|
ارسال دعوتنامه <CornerDownLeft className="rtl:rotate-180" />
|
|
</Button>
|
|
</DialogFooter>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
};
|
|
|
|
interface SettingsMembers1Props {
|
|
heading?: string;
|
|
subHeading?: string;
|
|
className?: string;
|
|
users?: User[];
|
|
}
|
|
|
|
const SettingsMembers1 = ({
|
|
heading = "اعضای فضای کاری",
|
|
subHeading = "دسترسی همکاران به CRM را مدیریت کنید و نقش هر عضو را تنظیم کنید.",
|
|
users = [
|
|
{
|
|
name: "الهام حسینی",
|
|
email: "emily.w@acme.io",
|
|
accessLevel: "مالک فضای کاری",
|
|
},
|
|
{
|
|
email: "john.d@acme.io",
|
|
accessLevel: "فروشنده",
|
|
status: "دعوت در انتظار",
|
|
},
|
|
{
|
|
name: "مهدی پارسا",
|
|
email: "michael.p@acme.io",
|
|
accessLevel: "فقط مشاهده",
|
|
},
|
|
{
|
|
email: "lisa.t@acme.io",
|
|
accessLevel: "فروشنده",
|
|
status: "دعوت در انتظار",
|
|
},
|
|
],
|
|
className,
|
|
}: SettingsMembers1Props) => {
|
|
const [searchValue, setSearchValue] = useState("");
|
|
const [selectedAccessLevels, setSelectedAccessLevels] = useState<string[]>(
|
|
[],
|
|
);
|
|
|
|
const accessLevels = useMemo(() => {
|
|
return Array.from(new Set(users.map((user) => user.accessLevel)));
|
|
}, [users]);
|
|
|
|
const filteredUsers = useMemo(() => {
|
|
return users.filter((user) => {
|
|
const isAccessLevelSelected =
|
|
selectedAccessLevels.length === 0 ||
|
|
selectedAccessLevels.includes(user.accessLevel);
|
|
const isSearchMatch =
|
|
user.name?.toLowerCase().includes(searchValue.toLowerCase()) ||
|
|
user.email.toLowerCase().includes(searchValue.toLowerCase());
|
|
return isAccessLevelSelected && isSearchMatch;
|
|
});
|
|
}, [users, selectedAccessLevels, searchValue]);
|
|
|
|
const toggleAccessLevel = (accessLevel: string) => {
|
|
setSelectedAccessLevels((prev) => {
|
|
if (prev.includes(accessLevel)) {
|
|
return prev.filter((level) => level !== accessLevel);
|
|
}
|
|
return [...prev, accessLevel];
|
|
});
|
|
};
|
|
|
|
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-8">
|
|
<div className="space-y-3 border-b pb-8">
|
|
<h3 className="text-xl font-semibold tracking-tight">{heading}</h3>
|
|
<p className="text-sm font-medium text-muted-foreground">
|
|
{subHeading}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-3 sm:flex-nowrap">
|
|
<div className="relative w-full min-w-20">
|
|
<Search className="absolute top-1/2 start-2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
<Input
|
|
placeholder="جستجوی عضو…"
|
|
className="ps-7"
|
|
value={searchValue}
|
|
onChange={(e) => setSearchValue(e.target.value)}
|
|
/>
|
|
</div>
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<Button variant="outline">
|
|
<SlidersHorizontal />
|
|
فیلتر نقش
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent align="end" className="w-56">
|
|
<div className="space-y-3">
|
|
<p className="text-sm font-medium">سطح دسترسی</p>
|
|
<div className="space-y-2">
|
|
{accessLevels.map((accessLevel) => (
|
|
<Label
|
|
key={accessLevel}
|
|
className="cursor-pointer font-normal"
|
|
>
|
|
<Checkbox
|
|
checked={selectedAccessLevels.includes(accessLevel)}
|
|
onCheckedChange={() => toggleAccessLevel(accessLevel)}
|
|
/>
|
|
<span className="text-sm">{accessLevel}</span>
|
|
</Label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
<InviteUserForm />
|
|
</div>
|
|
|
|
<UsersTable users={filteredUsers} />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { SettingsMembers1 };
|