sciagent code + Gitea Actions CI/CD
CI/CD / backend (push) Failing after 2m8s
CI/CD / frontend (push) Failing after 1m40s
CI/CD / deploy (push) Has been skipped

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Thinh Lam
2026-06-30 09:38:30 +07:00
commit 688fac73e9
1167 changed files with 158244 additions and 0 deletions
@@ -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 Vai trò Quyền
</CardTitle>
<CardDescription>
Xem quản 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>
);
}