diff --git a/auth/orm.py b/auth/orm.py index dddcc826..7057f840 100644 --- a/auth/orm.py +++ b/auth/orm.py @@ -127,7 +127,7 @@ class AuthorRole(Base): __table_args__ = {"extend_existing": True} id = None - community = Column(ForeignKey("community.id"), primary_key=True, index=True) + community = Column(ForeignKey("community.id"), primary_key=True, index=True, default=1) author = Column(ForeignKey("author.id"), primary_key=True, index=True) role = Column(ForeignKey("role.id"), primary_key=True, index=True) diff --git a/panel/admin.tsx b/panel/admin.tsx index 5b40fed0..41555350 100644 --- a/panel/admin.tsx +++ b/panel/admin.tsx @@ -3,7 +3,7 @@ * @module AdminPage */ -import { Component, For, Show, createSignal, onMount } from 'solid-js' +import { Component, For, Show, createSignal, onMount, createEffect } from 'solid-js' import { logout } from './auth' import { query } from './graphql' @@ -53,7 +53,17 @@ interface AdminGetRolesResponse { * Интерфейс для ответа обновления пользователя */ interface AdminUpdateUserResponse { - adminUpdateUser: boolean + success: boolean + error?: string +} + +/** + * Интерфейс для входных данных обновления пользователя + */ +interface AdminUserUpdateInput { + id: number + roles: string[] + community?: number } /** @@ -121,10 +131,36 @@ const AdminPage: Component = (props) => { // Периодическая проверка авторизации onMount(() => { + // Получаем параметры из URL при загрузке + const urlParams = new URLSearchParams(window.location.search); + const page = parseInt(urlParams.get('page') || '1'); + const limit = parseInt(urlParams.get('limit') || '10'); + const search = urlParams.get('search') || ''; + + setPagination({ ...pagination(), page, limit }); + setSearchQuery(search); + // Загружаем данные при монтировании loadUsers() loadRoles() }) + + // Обновление URL при изменении параметров пагинации + createEffect(() => { + const pagData = pagination(); + const search = searchQuery(); + + const urlParams = new URLSearchParams(); + urlParams.set('page', pagData.page.toString()); + urlParams.set('limit', pagData.limit.toString()); + + if (search) { + urlParams.set('search', search); + } + + const newUrl = `${window.location.pathname}?${urlParams.toString()}`; + window.history.replaceState({}, '', newUrl); + }); /** * Загрузка списка пользователей с учетом пагинации и поиска @@ -134,6 +170,7 @@ const AdminPage: Component = (props) => { setError(null) try { + // Используем актуальные данные из состояния const { page, limit } = pagination() const offset = (page - 1) * limit const search = searchQuery().trim() @@ -291,13 +328,17 @@ const AdminPage: Component = (props) => { `${location.origin}/graphql`, ` mutation AdminUpdateUser($user: AdminUserUpdateInput!) { - adminUpdateUser(user: $user) + adminUpdateUser(user: $user) { + success + error + } } `, { user: { id: userId, - roles: newRoles + roles: newRoles, + community: 1 // Добавляем обязательный параметр community } } ) @@ -315,14 +356,24 @@ const AdminPage: Component = (props) => { // Закрываем модальное окно closeRolesModal() - // Показываем сообщение об успехе + // Показываем сообщение об успехе и обновляем список пользователей setSuccessMessage('Роли пользователя успешно обновлены') + + // Перезагружаем список пользователей + loadUsers() // Скрываем сообщение через 3 секунды setTimeout(() => setSuccessMessage(null), 3000) } catch (err) { console.error('Ошибка обновления ролей:', err) - setError(err instanceof Error ? err.message : 'Ошибка обновления ролей') + let errorMessage = err instanceof Error ? err.message : 'Ошибка обновления ролей'; + + // Если ошибка связана с недостающим полем community + if (errorMessage.includes('author_role.community')) { + errorMessage = 'Ошибка: для роли author требуется указать community. Обратитесь к администратору.'; + } + + setError(errorMessage) } } @@ -600,21 +651,24 @@ const AdminPage: Component = (props) => { const RolesModal: Component = () => { const user = selectedUser() const [selectedRoles, setSelectedRoles] = createSignal(user ? [...user.roles] : []) + const availableRoles = roles(); // Получаем список доступных ролей // Получаем дополнительные описания ролей const getRoleDescription = (roleId: string): string => { - // Если есть описание в списке ролей, используем его - const roleFromList = roles().find(r => r.id === roleId); - if (roleFromList?.description) { - return roleFromList.description; - } - // Иначе возвращаем стандартное описание switch(roleId) { case 'reader': return 'Базовая роль. Позволяет авторизоваться и оставлять реакции.'; + case 'expert': + return 'Эксперт. Позволяет оставлять комментарии и апрувить публикации для главной.'; case 'author': return 'Расширенная роль. Позволяет создавать контент и голосовать за публикации для вывода на главную страницу.'; + case 'editor': + return 'Редактор. Позволяет редактировать темы и публикации сообщества.'; + case 'moderator': + return 'Модератор. Позволяет модерировать контент и управлять пользователями.'; + case 'admin': + return 'Администратор. Позволяет управлять всеми функциями системы.'; default: return 'Нет описания'; } @@ -631,6 +685,7 @@ const AdminPage: Component = (props) => { const saveRoles = () => { if (user) { + // При сохранении ролей передаем правильный набор параметров updateUserRoles(user.id, selectedRoles()) } } @@ -642,14 +697,9 @@ const AdminPage: Component = (props) => {