'use client'; import { useEffect, useState } from 'react'; import { Loader2, Pencil, Plus, ShieldCheck, Trash2 } from 'lucide-react'; import { AdminShell } from '@/components/id/admin-shell'; import { useAuth } from '@/components/id/auth-provider'; import { useToast } from '@/components/id/toast-provider'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { AdminPermission, AdminRole, apiFetch } from '@/lib/api'; type RoleFormState = { slug: string; name: string; description: string; permissionSlugs: string[]; }; const emptyForm: RoleFormState = { slug: '', name: '', description: '', permissionSlugs: [] }; export default function AdminRbacPage() { const { token, user } = useAuth(); const { showToast } = useToast(); const [roles, setRoles] = useState([]); const [permissions, setPermissions] = useState([]); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [dialogOpen, setDialogOpen] = useState(false); const [editingRole, setEditingRole] = useState(null); const [form, setForm] = useState(emptyForm); async function reloadRoles() { if (!token) return; const response = await apiFetch<{ roles: AdminRole[] }>('/admin/rbac/roles', {}, token); setRoles(response.roles ?? []); } useEffect(() => { if (!token || !user?.canManageRoles) return; Promise.all([ apiFetch<{ roles: AdminRole[] }>('/admin/rbac/roles', {}, token), apiFetch<{ permissions: AdminPermission[] }>('/admin/rbac/permissions', {}, token) ]) .then(([rolesResponse, permissionsResponse]) => { setRoles(rolesResponse.roles ?? []); setPermissions(permissionsResponse.permissions ?? []); }) .catch((error) => showToast(error instanceof Error ? error.message : 'Не удалось загрузить роли')) .finally(() => setLoading(false)); }, [showToast, token, user?.canManageRoles]); function openCreateDialog() { setEditingRole(null); setForm(emptyForm); setDialogOpen(true); } function openEditDialog(role: AdminRole) { setEditingRole(role); setForm({ slug: role.slug, name: role.name, description: role.description ?? '', permissionSlugs: role.permissions.map((permission) => permission.slug) }); setDialogOpen(true); } async function handleSaveRole() { if (!token) return; setSaving(true); try { if (editingRole) { await apiFetch(`/admin/rbac/roles/${editingRole.slug}`, { method: 'PATCH', body: JSON.stringify({ name: form.name, description: form.description || undefined, permissionSlugs: form.permissionSlugs }) }, token); showToast('Роль обновлена'); } else { await apiFetch('/admin/rbac/roles', { method: 'POST', body: JSON.stringify(form) }, token); showToast('Роль создана'); } setDialogOpen(false); setForm(emptyForm); setEditingRole(null); await reloadRoles(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось сохранить роль'); } finally { setSaving(false); } } async function handleDeleteRole(role: AdminRole) { if (!token || role.isSystem) return; if (!window.confirm(`Удалить роль «${role.name}»? Она будет снята со всех пользователей.`)) return; setSaving(true); try { await apiFetch(`/admin/rbac/roles/${role.slug}`, { method: 'DELETE' }, token); showToast('Роль удалена'); await reloadRoles(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось удалить роль'); } finally { setSaving(false); } } if (!user?.canManageRoles) { return (

Управление ролями доступно пользователям с правом rbac.manage.

); } return (

Роли и права

Редактируйте роли, назначайте права всем пользователям или отдельным аккаунтам

{loading ? (
Загрузка...
) : (
{roles.map((role) => (
{!role.isSystem ? ( ) : null}
{role.name} {role.permissions.length} прав · {role.slug} {role.isSystem ? ' · системная' : ''} {role.isDefault ? ' · по умолчанию' : ''}
{role.permissions.length ? ( role.permissions.map((permission) => (
{permission.name}
{permission.slug}
)) ) : (

Права не назначены

)}
))}
)} {editingRole ? `Редактирование: ${editingRole.name}` : 'Новая роль'}
{!editingRole ? ( setForm((current) => ({ ...current, slug: event.target.value }))} placeholder="slug, например support" /> ) : (

Slug: {editingRole.slug}

)} setForm((current) => ({ ...current, name: event.target.value }))} placeholder="Название роли" /> setForm((current) => ({ ...current, description: event.target.value }))} placeholder="Описание" />

Права

{permissions.map((permission) => ( ))}
); }