sciagent code + Gitea Actions CI/CD
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user