'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { Ban, Crown, FileText, KeyRound, Loader2, Search, ShieldPlus, UserCog } from 'lucide-react'; import { UserDocumentsDialog } from '@/components/documents/user-documents-dialog'; 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 { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { AdminRole, AdminUser, apiFetch } from '@/lib/api'; const statusLabels: Record = { ACTIVE: 'Активен', SUSPENDED: 'Заблокирован', DELETED: 'Удалён' }; const roleLabels: Record = { admin: 'Администратор', moderator: 'Модератор', manager: 'Менеджер', 'super-admin': 'Супер-админ' }; export default function AdminUsersPage() { const { token, user: currentUser } = useAuth(); const { showToast } = useToast(); const [users, setUsers] = useState([]); const [roles, setRoles] = useState([]); const [search, setSearch] = useState(''); const [loading, setLoading] = useState(true); const [selectedUser, setSelectedUser] = useState(null); const [password, setPassword] = useState(''); const [actionLoading, setActionLoading] = useState(false); const [dialog, setDialog] = useState<'password' | 'roles' | null>(null); const [documentsUser, setDocumentsUser] = useState(null); const loadUsers = useCallback(async () => { if (!token) return; setLoading(true); try { const response = await apiFetch<{ users: AdminUser[] }>(`/admin/users${search ? `?search=${encodeURIComponent(search)}` : ''}`, {}, token); setUsers(response.users ?? []); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось загрузить пользователей'); } finally { setLoading(false); } }, [search, showToast, token]); useEffect(() => { void loadUsers(); }, [loadUsers]); useEffect(() => { if (!token || !currentUser?.canManageRoles) return; apiFetch<{ roles: AdminRole[] }>('/admin/rbac/roles', {}, token) .then((response) => setRoles(response.roles ?? [])) .catch(() => undefined); }, [currentUser?.canManageRoles, token]); const assignableRoles = useMemo(() => roles.filter((role) => role.slug !== 'super-admin'), [roles]); async function handleSuspend(user: AdminUser) { if (!token) return; setActionLoading(true); try { await apiFetch(`/admin/users/${user.id}/suspend`, { method: 'POST' }, token); showToast('Пользователь заблокирован'); await loadUsers(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось заблокировать пользователя'); } finally { setActionLoading(false); } } async function handleResetPassword() { if (!token || !selectedUser || password.length < 8) return; setActionLoading(true); try { await apiFetch(`/admin/users/${selectedUser.id}/reset-password`, { method: 'POST', body: JSON.stringify({ newPassword: password }) }, token); showToast('Пароль обновлён'); setDialog(null); setPassword(''); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось сбросить пароль'); } finally { setActionLoading(false); } } async function handleToggleSuperAdmin(user: AdminUser) { if (!token || !currentUser?.canManageRoles) return; setActionLoading(true); try { await apiFetch(`/admin/users/${user.id}/super-admin`, { method: 'PATCH', body: JSON.stringify({ isSuperAdmin: !user.isSuperAdmin }) }, token); showToast(user.isSuperAdmin ? 'Права супер-админа сняты' : 'Пользователь назначен супер-админом'); await loadUsers(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось изменить права'); } finally { setActionLoading(false); } } async function handleAssignRole(roleSlug: string) { if (!token || !selectedUser) return; setActionLoading(true); try { const response = await apiFetch<{ roles: string[] }>(`/admin/rbac/users/${selectedUser.id}/roles`, { method: 'POST', body: JSON.stringify({ roleSlug }) }, token); setSelectedUser({ ...selectedUser, roles: response.roles ?? [] }); showToast('Роль назначена'); await loadUsers(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось назначить роль'); } finally { setActionLoading(false); } } async function handleRemoveRole(roleSlug: string) { if (!token || !selectedUser) return; setActionLoading(true); try { await apiFetch(`/admin/rbac/users/${selectedUser.id}/roles/${roleSlug}`, { method: 'DELETE' }, token); setSelectedUser({ ...selectedUser, roles: (selectedUser.roles ?? []).filter((role) => role !== roleSlug) }); showToast('Роль снята'); await loadUsers(); } catch (error) { showToast(error instanceof Error ? error.message : 'Не удалось снять роль'); } finally { setActionLoading(false); } } function renderRoles(user: AdminUser) { const userRoles = user.roles ?? []; const labels = user.isSuperAdmin ? ['Супер-админ', ...userRoles.map((role) => roleLabels[role] ?? role)] : userRoles.map((role) => roleLabels[role] ?? role); if (!labels.length) return 'Пользователь'; return labels.join(', '); } return (

Пользователи

Поиск, блокировка, сброс пароля и управление доступом

setSearch(event.target.value)} placeholder="Поиск по имени, почте или телефону" className="pl-10" />
{loading ? (
Загрузка...
) : ( Пользователь Телефон Роли Статус Действия {users.map((user) => (
{user.displayName}
{user.email ?? user.username ?? '—'}
{user.phone ?? '—'} {renderRoles(user)} {statusLabels[user.status] ?? user.status}
{currentUser?.canManageUsers ? ( <> ) : null} {currentUser?.canViewUserDocuments ? ( ) : null} {currentUser?.canManageRoles ? ( <> ) : null}
))}
)}
!open && setDialog(null)}> Сброс пароля

Новый пароль для {selectedUser?.displayName}

setPassword(event.target.value)} placeholder="Минимум 8 символов" />
!open && setDialog(null)}> Роли и доступ

{selectedUser?.displayName}

{(selectedUser?.roles ?? []).map((role) => (
{roleLabels[role] ?? role}
))} {!(selectedUser?.roles ?? []).length ?

Роли не назначены

: null}

Назначить роль

{assignableRoles.map((role) => ( ))}
{documentsUser ? ( { if (!open) setDocumentsUser(null); }} targetUserId={documentsUser.id} targetUserName={documentsUser.displayName} token={token} /> ) : null}
); }