203 lines
7.0 KiB
TypeScript
203 lines
7.0 KiB
TypeScript
/**
|
|
* Role Permissions Tab
|
|
* Allows administrators to manage user roles and permissions
|
|
*/
|
|
import { useState } from "react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Shield, User, Eye, Edit3, Lock, CheckCircle2 } from "lucide-react";
|
|
import { Role, Permission, ROLE_PERMISSIONS, ROLE_DISPLAY_NAMES, getPermissionsForRole } from "@/lib/permissions";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
interface PermissionGroup {
|
|
category: string;
|
|
permissions: Permission[];
|
|
icon: React.ComponentType<{ className?: string }>;
|
|
}
|
|
|
|
const PERMISSION_GROUPS: PermissionGroup[] = [
|
|
{
|
|
category: "Dashboard",
|
|
permissions: ["dashboard.access"],
|
|
icon: Eye,
|
|
},
|
|
{
|
|
category: "Ứng dụng",
|
|
permissions: ["application.view", "application.edit", "application.verify", "application.approve"],
|
|
icon: Edit3,
|
|
},
|
|
{
|
|
category: "Đánh giá",
|
|
permissions: ["evaluation.view"],
|
|
icon: CheckCircle2,
|
|
},
|
|
{
|
|
category: "Chat Assistant",
|
|
permissions: ["chat.view", "chat.interact"],
|
|
icon: User,
|
|
},
|
|
{
|
|
category: "Chương trình",
|
|
permissions: ["curriculum.view", "curriculum.edit", "curriculum.delete"],
|
|
icon: Edit3,
|
|
},
|
|
{
|
|
category: "Quản trị",
|
|
permissions: ["admin.access", "admin.users", "admin.settings"],
|
|
icon: Shield,
|
|
},
|
|
{
|
|
category: "Báo cáo",
|
|
permissions: ["reports.view", "reports.create", "reports.edit"],
|
|
icon: Edit3,
|
|
},
|
|
];
|
|
|
|
export function RolePermissionsTab() {
|
|
const [selectedRole, setSelectedRole] = useState<Role>("admin");
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Role Selection */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<Shield className="h-5 w-5" />
|
|
Quản lý Vai trò và Quyền
|
|
</CardTitle>
|
|
<CardDescription>
|
|
Xem và quản lý quyền truy cập cho các vai trò trong hệ thống
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Tabs value={selectedRole} onValueChange={(value) => setSelectedRole(value as Role)}>
|
|
<TabsList className="grid w-full grid-cols-3">
|
|
{(["admin", "editor", "viewer"] as Role[]).map((role) => (
|
|
<TabsTrigger key={role} value={role} className="flex items-center gap-2">
|
|
<User className="h-4 w-4" />
|
|
{ROLE_DISPLAY_NAMES[role]}
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
|
|
{(["admin", "editor", "viewer"] as Role[]).map((role) => (
|
|
<TabsContent key={role} value={role} className="mt-6">
|
|
<RolePermissionsView role={role} />
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Permission Summary */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Tổng quan Quyền</CardTitle>
|
|
<CardDescription>
|
|
So sánh quyền giữa các vai trò
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
{(["admin", "editor", "viewer"] as Role[]).map((role) => {
|
|
const permissions = getPermissionsForRole(role);
|
|
return (
|
|
<div key={role} className="border rounded-lg p-4">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<div className="flex items-center gap-2">
|
|
<Shield className="h-4 w-4" />
|
|
<span className="font-medium">{ROLE_DISPLAY_NAMES[role]}</span>
|
|
</div>
|
|
<Badge variant="secondary">{permissions.length} quyền</Badge>
|
|
</div>
|
|
<div className="flex flex-wrap gap-1 mt-2">
|
|
{permissions.slice(0, 5).map((perm) => (
|
|
<Badge key={perm} variant="outline" className="text-xs">
|
|
{perm}
|
|
</Badge>
|
|
))}
|
|
{permissions.length > 5 && (
|
|
<Badge variant="outline" className="text-xs">
|
|
+{permissions.length - 5} khác
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function RolePermissionsView({ role }: { role: Role }) {
|
|
const rolePermissions = getPermissionsForRole(role);
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="text-lg font-semibold">{ROLE_DISPLAY_NAMES[role]}</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
{rolePermissions.length} quyền được cấp
|
|
</p>
|
|
</div>
|
|
<Badge variant="default">{rolePermissions.length} quyền</Badge>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{PERMISSION_GROUPS.map((group) => {
|
|
const groupPermissions = group.permissions.filter((perm) =>
|
|
rolePermissions.includes(perm)
|
|
);
|
|
const hasAnyPermission = groupPermissions.length > 0;
|
|
|
|
return (
|
|
<Card key={group.category} className={hasAnyPermission ? "" : "opacity-50"}>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-sm flex items-center gap-2">
|
|
<group.icon className="h-4 w-4" />
|
|
{group.category}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-2">
|
|
{group.permissions.map((permission) => {
|
|
const hasPermission = rolePermissions.includes(permission);
|
|
return (
|
|
<div
|
|
key={permission}
|
|
className="flex items-center justify-between p-2 rounded border"
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
{hasPermission ? (
|
|
<CheckCircle2 className="h-4 w-4 text-green-600" />
|
|
) : (
|
|
<Lock className="h-4 w-4 text-muted-foreground" />
|
|
)}
|
|
<Label
|
|
htmlFor={permission}
|
|
className={`text-sm ${hasPermission ? "" : "text-muted-foreground"}`}
|
|
>
|
|
{permission}
|
|
</Label>
|
|
</div>
|
|
<Badge variant={hasPermission ? "default" : "secondary"}>
|
|
{hasPermission ? "Có" : "Không"}
|
|
</Badge>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|