feat(roles): add roles list page
- Complete table view with role information - CRUD operations (view, edit, delete, permissions) - Delete confirmation modal - Responsive design with proper Persian RTL - Loading states and error handling
This commit is contained in:
parent
efb92ac136
commit
0ee448d9be
|
|
@ -0,0 +1,193 @@
|
|||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useRoles, useDeleteRole } from "../core/_hooks";
|
||||
import { Button } from "@/components/ui/Button";
|
||||
import { LoadingSpinner } from "@/components/ui/LoadingSpinner";
|
||||
import { Role } from "@/types/auth";
|
||||
import { Trash2, Edit, Plus, Eye, Users } from "lucide-react";
|
||||
import { Modal } from "@/components/ui/Modal";
|
||||
|
||||
const RolesListPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const [deleteRoleId, setDeleteRoleId] = useState<string | null>(null);
|
||||
|
||||
const { data: roles, isLoading, error } = useRoles();
|
||||
const { mutate: deleteRole, isPending: isDeleting } = useDeleteRole();
|
||||
|
||||
const handleEdit = (roleId: number) => {
|
||||
navigate(`/roles/${roleId}/edit`);
|
||||
};
|
||||
|
||||
const handleView = (roleId: number) => {
|
||||
navigate(`/roles/${roleId}`);
|
||||
};
|
||||
|
||||
const handlePermissions = (roleId: number) => {
|
||||
navigate(`/roles/${roleId}/permissions`);
|
||||
};
|
||||
|
||||
const handleDelete = (roleId: number) => {
|
||||
setDeleteRoleId(roleId.toString());
|
||||
};
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (deleteRoleId) {
|
||||
deleteRole(deleteRoleId, {
|
||||
onSuccess: () => {
|
||||
setDeleteRoleId(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const cancelDelete = () => {
|
||||
setDeleteRoleId(null);
|
||||
};
|
||||
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
if (error) return <div className="text-red-600">خطا در بارگذاری نقشها</div>;
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="mb-6">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100">
|
||||
مدیریت نقشها
|
||||
</h1>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => navigate('/roles/create')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
افزودن نقش جدید
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead className="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
نام نقش
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
توضیحات
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
تعداد دسترسیها
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
تاریخ ایجاد
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
عملیات
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{roles?.map((role: Role) => (
|
||||
<tr key={role.id} className="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{role.title}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-300 max-w-xs truncate">
|
||||
{role.description}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<span className="px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800 rounded-full">
|
||||
{role.permissions?.length || 0} دسترسی
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-300">
|
||||
{new Date(role.created_at).toLocaleDateString('fa-IR')}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => handleView(role.id)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Eye className="h-3 w-3" />
|
||||
مشاهده
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={() => handlePermissions(role.id)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Users className="h-3 w-3" />
|
||||
دسترسیها
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={() => handleEdit(role.id)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Edit className="h-3 w-3" />
|
||||
ویرایش
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="danger"
|
||||
onClick={() => handleDelete(role.id)}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
حذف
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{roles?.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-gray-500 dark:text-gray-400">هیچ نقشی یافت نشد</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Delete Confirmation Modal */}
|
||||
<Modal
|
||||
isOpen={!!deleteRoleId}
|
||||
onClose={cancelDelete}
|
||||
title="تأیید حذف"
|
||||
size="sm"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
آیا از حذف این نقش اطمینان دارید؟ این عمل قابل بازگشت نیست.
|
||||
</p>
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button variant="secondary" onClick={cancelDelete}>
|
||||
انصراف
|
||||
</Button>
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={confirmDelete}
|
||||
loading={isDeleting}
|
||||
>
|
||||
حذف
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RolesListPage;
|
||||
Loading…
Reference in New Issue