From bceb3119100ea829c313c75283a378ad16efdcf1 Mon Sep 17 00:00:00 2001 From: Untone Date: Fri, 25 Jul 2025 10:50:03 +0300 Subject: [PATCH] roles-modal-fixes --- panel/graphql/queries.ts | 24 +++ panel/modals/RolesModal.tsx | 283 +++++++++++++----------------------- panel/routes/authors.tsx | 216 +++++++++++---------------- 3 files changed, 209 insertions(+), 314 deletions(-) diff --git a/panel/graphql/queries.ts b/panel/graphql/queries.ts index 6b45c6b0..91391ed7 100644 --- a/panel/graphql/queries.ts +++ b/panel/graphql/queries.ts @@ -379,3 +379,27 @@ export const DELETE_CUSTOM_ROLE_MUTATION: string = } } `.loc?.source.body || '' + +export const ADMIN_UPDATE_USER_MUTATION = ` + mutation UpdateUser( + $id: Int! + $email: String + $name: String + $slug: String + $roles: String! + ) { + updateUser( + id: $id + email: $email + name: $name + slug: $slug + roles: $roles + ) { + id + email + name + slug + roles + } + } +` diff --git a/panel/modals/RolesModal.tsx b/panel/modals/RolesModal.tsx index 300914fa..13e03855 100644 --- a/panel/modals/RolesModal.tsx +++ b/panel/modals/RolesModal.tsx @@ -4,6 +4,9 @@ import formStyles from '../styles/Form.module.css' import Button from '../ui/Button' import Modal from '../ui/Modal' +// Список администраторских email +const ADMIN_EMAILS = ['welcome@discours.io'] + export interface UserEditModalProps { user: AdminUserInfo isOpen: boolean @@ -13,7 +16,7 @@ export interface UserEditModalProps { email?: string name?: string slug?: string - roles: string[] + roles: string }) => Promise } @@ -65,7 +68,8 @@ const UserEditModal: Component = (props) => { // Проверяем, является ли пользователь администратором по ролям, которые приходят с сервера const isAdmin = () => { - return (props.user.roles || []).includes('admin') + const roles = formData().roles + return roles.includes('admin') || (props.user.email ? ADMIN_EMAILS.includes(props.user.email) : false) } // Получаем информацию о роли по ID @@ -73,23 +77,16 @@ const UserEditModal: Component = (props) => { return AVAILABLE_ROLES.find((role) => role.id === roleId) || { name: roleId, emoji: '👤' } } - // Формируем строку с ролями и эмоджи - const getRolesDisplay = () => { - const roles = formData().roles - if (roles.length === 0) { - return isAdmin() ? '🪄 Администратор' : 'Роли не назначены' + // Обновляем поле формы + const updateField = (field: keyof ReturnType, value: string) => { + setFormData((prev) => ({ ...prev, [field]: value })) + if (errors()[field]) { + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors[field] + return newErrors + }) } - - const roleTexts = roles.map((roleId) => { - const role = getRoleInfo(roleId) - return `${role.emoji} ${role.name}` - }) - - if (isAdmin()) { - return `🪄 Администратор, ${roleTexts.join(', ')}` - } - - return roleTexts.join(', ') } // Обновляем форму при изменении пользователя @@ -106,31 +103,25 @@ const UserEditModal: Component = (props) => { } }) - const updateField = (field: string, value: string) => { - setFormData((prev) => ({ ...prev, [field]: value })) - // Очищаем ошибку при изменении поля - if (errors()[field]) { - setErrors((prev) => ({ ...prev, [field]: '' })) - } - } - const handleRoleToggle = (roleId: string) => { - // Роль администратора нельзя изменить вручную if (roleId === 'admin') { return } setFormData((prev) => { - const currentRoles = prev.roles + const currentRoles = prev.roles || [] const newRoles = currentRoles.includes(roleId) - ? currentRoles.filter((r) => r !== roleId) + ? currentRoles.filter((r: string) => r !== roleId) : [...currentRoles, roleId] return { ...prev, roles: newRoles } }) - // Очищаем ошибку ролей при изменении if (errors().roles) { - setErrors((prev) => ({ ...prev, roles: '' })) + setErrors((prev) => { + const newErrors = { ...prev } + delete newErrors.roles + return newErrors + }) } } @@ -138,23 +129,19 @@ const UserEditModal: Component = (props) => { const newErrors: Record = {} const data = formData() - // Email if (!data.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email.trim())) { newErrors.email = 'Неверный формат email' } - // Имя if (!data.name.trim() || data.name.trim().length < 2) { newErrors.name = 'Имя должно содержать минимум 2 символа' } - // Slug if (!data.slug.trim() || !/^[a-z0-9_-]+$/.test(data.slug.trim())) { newErrors.slug = 'Slug может содержать только латинские буквы, цифры, дефисы и подчеркивания' } - // Роли (админы освобождаются от этого требования) - if (!isAdmin() && data.roles.filter((role) => role !== 'admin').length === 0) { + if (!isAdmin() && (data.roles || []).filter((role: string) => role !== 'admin').length === 0) { newErrors.roles = 'Выберите хотя бы одну роль' } @@ -169,8 +156,10 @@ const UserEditModal: Component = (props) => { setLoading(true) try { - // Отправляем только обычные роли, админская роль определяется на сервере по email - await props.onSave(formData()) + await props.onSave({ + ...formData(), + roles: (formData().roles || []).join(',') + }) props.onClose() } catch (error) { console.error('Ошибка при сохранении пользователя:', error) @@ -185,148 +174,88 @@ const UserEditModal: Component = (props) => { isOpen={props.isOpen} onClose={props.onClose} title={`Редактирование пользователя #${props.user.id}`} - size="large" >
- {/* Компактная системная информация */} + {/* Основные данные */}
-
- ID: {props.user.id} +
+ + updateField('email', e.currentTarget.value)} + disabled={loading()} + placeholder="user@example.com" + /> + {errors().email && ( +
+ ⚠️ + {errors().email} +
+ )}
-
- Регистрация:{' '} - {props.user.created_at - ? new Date(props.user.created_at * 1000).toLocaleDateString('ru-RU') - : '—'} -
-
- Активность:{' '} - {props.user.last_seen - ? new Date(props.user.last_seen * 1000).toLocaleDateString('ru-RU') - : '—'} -
-
-
- {/* Текущие роли в строку */} -
- -
- {getRolesDisplay()} -
-
- - {/* Основные данные в компактной сетке */} -
-
- - updateField('email', e.currentTarget.value)} - disabled={loading()} - placeholder="user@example.com" - /> - {errors().email && ( -
- ⚠️ - {errors().email} -
- )} -
- 💡 - Администраторы определяются автоматически по настройкам сервера +
+ + updateField('name', e.currentTarget.value)} + disabled={loading()} + placeholder="Иван Иванов" + /> + {errors().name && ( +
+ ⚠️ + {errors().name} +
+ )}
-
-
- - updateField('name', e.currentTarget.value)} - disabled={loading()} - placeholder="Иван Иванов" - /> - {errors().name && ( -
- ⚠️ - {errors().name} -
- )} -
- -
- - updateField('slug', e.currentTarget.value.toLowerCase())} - disabled={loading()} - placeholder="ivan-ivanov" - /> -
- 💡 - Только латинские буквы, цифры, дефисы и подчеркивания +
+ + updateField('slug', e.currentTarget.value.toLowerCase())} + disabled={loading()} + placeholder="ivan-ivanov" + /> + {errors().slug && ( +
+ ⚠️ + {errors().slug} +
+ )}
- {errors().slug && ( -
- ⚠️ - {errors().slug} -
- )}
@@ -346,8 +275,9 @@ const UserEditModal: Component = (props) => { {(role) => { const isAdminRole = role.id === 'admin' - const isSelected = formData().roles.includes(role.id) + const isSelected = (formData().roles || []).includes(role.id) const isDisabled = isAdminRole + const roleInfo = getRoleInfo(role.id) return (