247 lines
13 KiB
TypeScript
247 lines
13 KiB
TypeScript
import { useState } from "react";
|
||
import { useNavigate, useParams } from "react-router-dom";
|
||
import { useRole, useRolePermissions, usePermissions, useAssignPermission, useRemovePermission } from "../core/_hooks";
|
||
import { Button } from "@/components/ui/Button";
|
||
import { Permission } from "@/types/auth";
|
||
import { ArrowRight, Plus, Trash2, Check, Shield } from "lucide-react";
|
||
import { Modal } from "@/components/ui/Modal";
|
||
import { PageContainer } from "@/components/ui/Typography";
|
||
|
||
const RolePermissionsPage = () => {
|
||
const navigate = useNavigate();
|
||
const { id = "" } = useParams();
|
||
const [showAssignModal, setShowAssignModal] = useState(false);
|
||
const [removePermissionId, setRemovePermissionId] = useState<string | null>(null);
|
||
|
||
const { data: role, isLoading: roleLoading } = useRole(id);
|
||
const { data: rolePermissions, isLoading: permissionsLoading } = useRolePermissions(id);
|
||
const { data: allPermissions, isLoading: allPermissionsLoading } = usePermissions();
|
||
|
||
const assignedPermissionIds = (rolePermissions || []).map(p => p.id);
|
||
|
||
// Access the permissions array from the object structure
|
||
const safeAllPermissions = Array.isArray((allPermissions as any)?.permissions) ? (allPermissions as any).permissions : [];
|
||
const availablePermissions = safeAllPermissions.filter((p: any) => !assignedPermissionIds.includes(p.id));
|
||
|
||
const { mutate: assignPermission, isPending: assigning } = useAssignPermission();
|
||
const { mutate: removePermission, isPending: removing } = useRemovePermission();
|
||
|
||
const handleAssignPermission = (permissionId: number) => {
|
||
assignPermission({
|
||
roleId: id,
|
||
permissionId: permissionId.toString(),
|
||
});
|
||
};
|
||
|
||
const handleRemovePermission = (permissionId: number) => {
|
||
setRemovePermissionId(permissionId.toString());
|
||
};
|
||
|
||
const confirmRemovePermission = () => {
|
||
if (removePermissionId) {
|
||
removePermission({
|
||
roleId: id,
|
||
permissionId: removePermissionId,
|
||
}, {
|
||
onSuccess: () => {
|
||
setRemovePermissionId(null);
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
const cancelRemovePermission = () => {
|
||
setRemovePermissionId(null);
|
||
};
|
||
|
||
const isLoading = roleLoading || permissionsLoading;
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<PageContainer>
|
||
<div className="space-y-6 animate-pulse">
|
||
<div className="h-8 bg-gray-200 dark:bg-gray-700 rounded w-64"></div>
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{[...Array(2)].map((_, i) => (
|
||
<div key={i} className="card">
|
||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||
<div className="h-6 bg-gray-200 dark:bg-gray-700 rounded w-48"></div>
|
||
</div>
|
||
<div className="p-6 space-y-3">
|
||
{[...Array(5)].map((_, j) => (
|
||
<div key={j} className="h-12 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</PageContainer>
|
||
);
|
||
}
|
||
if (!role) return <div className="text-red-600">نقش یافت نشد</div>;
|
||
|
||
return (
|
||
<PageContainer>
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="flex items-center gap-4">
|
||
<Button
|
||
variant="secondary"
|
||
onClick={() => navigate('/roles')}
|
||
className="flex items-center gap-2"
|
||
>
|
||
<ArrowRight className="h-4 w-4" />
|
||
بازگشت
|
||
</Button>
|
||
<div>
|
||
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
||
مدیریت دسترسیهای نقش: {role?.title}
|
||
</h1>
|
||
<p className="text-gray-600 dark:text-gray-400 mt-1">
|
||
تخصیص و حذف دسترسیها برای این نقش
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||
{/* دسترسیهای تخصیص یافته */}
|
||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||
دسترسیهای تخصیص یافته ({(rolePermissions || []).length})
|
||
</h2>
|
||
</div>
|
||
|
||
<div className="p-6">
|
||
{permissionsLoading ? (
|
||
<div className="space-y-3 animate-pulse">
|
||
{[...Array(5)].map((_, i) => (
|
||
<div key={i} className="h-12 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="space-y-3">
|
||
{(rolePermissions || []).length > 0 ? (
|
||
(rolePermissions || []).map((permission: Permission) => (
|
||
<div
|
||
key={permission.id}
|
||
className="flex items-center justify-between p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg"
|
||
>
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-green-900 dark:text-green-100">
|
||
{permission.title}
|
||
</h4>
|
||
<p className="text-sm text-green-700 dark:text-green-300">
|
||
{permission.description}
|
||
</p>
|
||
</div>
|
||
<Button
|
||
size="sm"
|
||
variant="danger"
|
||
onClick={() => handleRemovePermission(permission.id)}
|
||
className="flex items-center gap-1 ml-3"
|
||
>
|
||
<Trash2 className="h-3 w-3" />
|
||
حذف
|
||
</Button>
|
||
</div>
|
||
))
|
||
) : (
|
||
<p className="text-center text-gray-500 dark:text-gray-400 py-8">
|
||
هیچ دسترسی تخصیص داده نشده است
|
||
</p>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* دسترسیهای در دسترس */}
|
||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md">
|
||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
|
||
دسترسیهای قابل تخصیص ({availablePermissions.length})
|
||
</h2>
|
||
</div>
|
||
|
||
<div className="p-6">
|
||
{allPermissionsLoading ? (
|
||
<div className="space-y-3 animate-pulse">
|
||
{[...Array(5)].map((_, i) => (
|
||
<div key={i} className="h-12 bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||
))}
|
||
</div>
|
||
) : (
|
||
<div className="space-y-3">
|
||
{availablePermissions.length > 0 ? (
|
||
availablePermissions.map((permission: Permission) => (
|
||
<div
|
||
key={permission.id}
|
||
className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg"
|
||
>
|
||
<div className="flex-1">
|
||
<h4 className="font-medium text-gray-900 dark:text-gray-100">
|
||
{permission.title}
|
||
</h4>
|
||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||
{permission.description}
|
||
</p>
|
||
</div>
|
||
<Button
|
||
size="sm"
|
||
variant="primary"
|
||
onClick={() => handleAssignPermission(permission.id)}
|
||
className="flex items-center gap-1 ml-3"
|
||
loading={assigning}
|
||
>
|
||
<Plus className="h-3 w-3" />
|
||
اختصاص
|
||
</Button>
|
||
</div>
|
||
))
|
||
) : (
|
||
<p className="text-center text-gray-500 dark:text-gray-400 py-8">
|
||
تمام دسترسیها به این نقش تخصیص داده شده است
|
||
</p>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Modal تأیید حذف دسترسی */}
|
||
<Modal
|
||
isOpen={!!removePermissionId}
|
||
onClose={() => setRemovePermissionId(null)}
|
||
title="حذف دسترسی"
|
||
>
|
||
<div className="space-y-4">
|
||
<p className="text-gray-600 dark:text-gray-400">
|
||
آیا از حذف این دسترسی از نقش اطمینان دارید؟
|
||
</p>
|
||
<div className="flex justify-end space-x-2 space-x-reverse">
|
||
<Button
|
||
variant="secondary"
|
||
onClick={() => setRemovePermissionId(null)}
|
||
disabled={removing}
|
||
>
|
||
انصراف
|
||
</Button>
|
||
<Button
|
||
variant="danger"
|
||
onClick={confirmRemovePermission}
|
||
loading={removing}
|
||
>
|
||
حذف
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
</div>
|
||
</PageContainer>
|
||
);
|
||
};
|
||
|
||
export default RolePermissionsPage;
|