This commit is contained in:
@@ -96,7 +96,7 @@ const CommunityRolesModal: Component<CommunityRolesModalProps> = (props) => {
|
||||
const handleRoleToggle = (roleId: string) => {
|
||||
const currentRoles = userRoles()
|
||||
if (currentRoles.includes(roleId)) {
|
||||
setUserRoles(currentRoles.filter((r) => r !== roleId))
|
||||
setUserRoles(currentRoles.filter((r) => r !== roleId))
|
||||
} else {
|
||||
setUserRoles([...currentRoles, roleId])
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ const InviteEditModal: Component<InviteEditModalProps> = (props) => {
|
||||
<input
|
||||
type="number"
|
||||
value={formData().inviter_id}
|
||||
onInput={(e) => updateField('inviter_id', Number.parseInt(e.target.value) || 0)}
|
||||
onInput={(e) => updateField('inviter_id', Number.parseInt(e.target.value, 10) || 0)}
|
||||
class={`${formStyles.input} ${errors().inviter_id ? formStyles.error : ''} ${!isCreating() ? formStyles.disabled : ''}`}
|
||||
placeholder="1"
|
||||
required
|
||||
@@ -165,7 +165,7 @@ const InviteEditModal: Component<InviteEditModalProps> = (props) => {
|
||||
<input
|
||||
type="number"
|
||||
value={formData().author_id}
|
||||
onInput={(e) => updateField('author_id', Number.parseInt(e.target.value) || 0)}
|
||||
onInput={(e) => updateField('author_id', Number.parseInt(e.target.value, 10) || 0)}
|
||||
class={`${formStyles.input} ${errors().author_id ? formStyles.error : ''} ${!isCreating() ? formStyles.disabled : ''}`}
|
||||
placeholder="2"
|
||||
required
|
||||
@@ -194,7 +194,7 @@ const InviteEditModal: Component<InviteEditModalProps> = (props) => {
|
||||
<input
|
||||
type="number"
|
||||
value={formData().shout_id}
|
||||
onInput={(e) => updateField('shout_id', Number.parseInt(e.target.value) || 0)}
|
||||
onInput={(e) => updateField('shout_id', Number.parseInt(e.target.value, 10) || 0)}
|
||||
class={`${formStyles.input} ${errors().shout_id ? formStyles.error : ''} ${!isCreating() ? formStyles.disabled : ''}`}
|
||||
placeholder="123"
|
||||
required
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function TopicEditModal(props: TopicEditModalProps) {
|
||||
* Обработка изменения выбора родительских топиков из таблеточек
|
||||
*/
|
||||
const handleParentSelectionChange = (selectedIds: string[]) => {
|
||||
const parentIds = selectedIds.map((id) => Number.parseInt(id))
|
||||
const parentIds = selectedIds.map((id) => Number.parseInt(id, 10))
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
parent_ids: parentIds
|
||||
|
||||
@@ -204,7 +204,7 @@ const TopicHierarchyModal = (props: TopicHierarchyModalProps) => {
|
||||
|
||||
// Добавляем в список изменений
|
||||
setChanges((prev) => [
|
||||
...prev.filter((c) => c.topicId !== selectedId),
|
||||
...prev.filter((c) => c.topicId !== selectedId),
|
||||
{
|
||||
topicId: selectedId,
|
||||
newParentIds,
|
||||
|
||||
@@ -130,7 +130,7 @@ const TopicMergeModal: Component<TopicMergeModalProps> = (props) => {
|
||||
*/
|
||||
const handleTargetTopicChange = (e: Event) => {
|
||||
const target = e.target as HTMLSelectElement
|
||||
const topicId = target.value ? Number.parseInt(target.value) : null
|
||||
const topicId = target.value ? Number.parseInt(target.value, 10) : null
|
||||
setTargetTopicId(topicId)
|
||||
|
||||
// Убираем выбранную целевую тему из исходных тем
|
||||
|
||||
@@ -3,8 +3,8 @@ import type { AuthorsSortField } from '../context/sort'
|
||||
import { AUTHORS_SORT_CONFIG } from '../context/sortConfig'
|
||||
import { query } from '../graphql'
|
||||
import type { Query, AdminUserInfo as User } from '../graphql/generated/schema'
|
||||
import { ADMIN_GET_USERS_QUERY } from '../graphql/queries'
|
||||
import { ADMIN_UPDATE_USER_MUTATION } from '../graphql/mutations'
|
||||
import { ADMIN_GET_USERS_QUERY } from '../graphql/queries'
|
||||
import UserEditModal from '../modals/RolesModal'
|
||||
import styles from '../styles/Admin.module.css'
|
||||
import Pagination from '../ui/Pagination'
|
||||
@@ -84,7 +84,10 @@ const AuthorsRoute: Component<AuthorsRouteProps> = (props) => {
|
||||
email: userData.email,
|
||||
name: userData.name,
|
||||
slug: userData.slug,
|
||||
roles: userData.roles.split(',').map(role => role.trim()).filter(role => role.length > 0)
|
||||
roles: userData.roles
|
||||
.split(',')
|
||||
.map((role) => role.trim())
|
||||
.filter((role) => role.length > 0)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Component, createEffect, createSignal, For, on, onMount, Show, untrack } from 'solid-js'
|
||||
import { useTableSort } from '../context/sort'
|
||||
import { COMMUNITIES_SORT_CONFIG } from '../context/sortConfig'
|
||||
import { query } from '../graphql'
|
||||
import {
|
||||
CREATE_COMMUNITY_MUTATION,
|
||||
DELETE_COMMUNITY_MUTATION,
|
||||
UPDATE_COMMUNITY_MUTATION
|
||||
} from '../graphql/mutations'
|
||||
import { GET_COMMUNITIES_QUERY } from '../graphql/queries'
|
||||
import { query } from '../graphql'
|
||||
import CommunityEditModal from '../modals/CommunityEditModal'
|
||||
import styles from '../styles/Table.module.css'
|
||||
import Button from '../ui/Button'
|
||||
@@ -22,19 +22,13 @@ interface Community {
|
||||
id: number
|
||||
slug: string
|
||||
name: string
|
||||
desc?: string
|
||||
pic: string
|
||||
created_at: number
|
||||
created_by?: { // Делаем created_by необязательным
|
||||
id: number
|
||||
name: string
|
||||
email: string
|
||||
} | null
|
||||
stat: {
|
||||
shouts: number
|
||||
followers: number
|
||||
authors: number
|
||||
}
|
||||
description: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
creator_id: number
|
||||
creator_name: string
|
||||
followers_count: number
|
||||
shouts_count: number
|
||||
}
|
||||
|
||||
interface CommunitiesRouteProps {
|
||||
@@ -42,6 +36,53 @@ interface CommunitiesRouteProps {
|
||||
onSuccess: (message: string) => void
|
||||
}
|
||||
|
||||
// Types for GraphQL responses
|
||||
interface CommunitiesResponse {
|
||||
get_communities_all: Array<{
|
||||
id: number
|
||||
name: string
|
||||
slug: string
|
||||
description: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
creator_id: number
|
||||
creator_name: string
|
||||
followers_count: number
|
||||
shouts_count: number
|
||||
}>
|
||||
}
|
||||
|
||||
interface CreateCommunityResponse {
|
||||
create_community: {
|
||||
success: boolean
|
||||
error?: string
|
||||
community?: {
|
||||
id: number
|
||||
name: string
|
||||
slug: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface UpdateCommunityResponse {
|
||||
update_community: {
|
||||
success: boolean
|
||||
error?: string
|
||||
community?: {
|
||||
id: number
|
||||
name: string
|
||||
slug: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface DeleteCommunityResponse {
|
||||
delete_community: {
|
||||
success: boolean
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Компонент для управления сообществами
|
||||
*/
|
||||
@@ -78,7 +119,7 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
const result = await query('/graphql', GET_COMMUNITIES_QUERY)
|
||||
|
||||
// Получаем данные и сортируем их на клиенте
|
||||
const communitiesData = (result as any)?.get_communities_all || []
|
||||
const communitiesData = (result as CommunitiesResponse)?.get_communities_all || []
|
||||
const sortedCommunities = sortCommunities(communitiesData)
|
||||
setCommunities(sortedCommunities)
|
||||
} catch (error) {
|
||||
@@ -91,8 +132,8 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
/**
|
||||
* Форматирует дату
|
||||
*/
|
||||
const formatDate = (timestamp: number): string => {
|
||||
return new Date(timestamp * 1000).toLocaleDateString('ru-RU')
|
||||
const formatDate = (dateString: string): string => {
|
||||
return new Date(dateString).toLocaleDateString('ru-RU')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,22 +156,22 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
comparison = (a.slug || '').localeCompare(b.slug || '', 'ru')
|
||||
break
|
||||
case 'created_at':
|
||||
comparison = a.created_at - b.created_at
|
||||
comparison = a.created_at.localeCompare(b.created_at, 'ru')
|
||||
break
|
||||
case 'created_by': {
|
||||
const aName = a.created_by?.name || a.created_by?.email || ''
|
||||
const bName = b.created_by?.name || b.created_by?.email || ''
|
||||
const aName = a.creator_name || ''
|
||||
const bName = b.creator_name || ''
|
||||
comparison = aName.localeCompare(bName, 'ru')
|
||||
break
|
||||
}
|
||||
case 'shouts':
|
||||
comparison = (a.stat?.shouts || 0) - (b.stat?.shouts || 0)
|
||||
comparison = (a.shouts_count || 0) - (b.shouts_count || 0)
|
||||
break
|
||||
case 'followers':
|
||||
comparison = (a.stat?.followers || 0) - (b.stat?.followers || 0)
|
||||
comparison = (a.followers_count || 0) - (b.followers_count || 0)
|
||||
break
|
||||
case 'authors':
|
||||
comparison = (a.stat?.authors || 0) - (b.stat?.authors || 0)
|
||||
comparison = (a.creator_id || 0) - (b.creator_id || 0)
|
||||
break
|
||||
default:
|
||||
comparison = a.id - b.id
|
||||
@@ -163,13 +204,15 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
const mutation = isCreating ? CREATE_COMMUNITY_MUTATION : UPDATE_COMMUNITY_MUTATION
|
||||
|
||||
// Удаляем created_by, если он null или undefined
|
||||
if (communityData.created_by === null || communityData.created_by === undefined) {
|
||||
delete communityData.created_by
|
||||
if (communityData.creator_id === null || communityData.creator_id === undefined) {
|
||||
delete communityData.creator_id
|
||||
}
|
||||
|
||||
const result = await query('/graphql', mutation, { community_input: communityData })
|
||||
|
||||
const resultData = isCreating ? (result as any).create_community : (result as any).update_community
|
||||
const resultData = isCreating
|
||||
? (result as CreateCommunityResponse).create_community
|
||||
: (result as UpdateCommunityResponse).update_community
|
||||
if (resultData.error) {
|
||||
throw new Error(resultData.error)
|
||||
}
|
||||
@@ -191,7 +234,7 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
const deleteCommunity = async (slug: string) => {
|
||||
try {
|
||||
const result = await query('/graphql', DELETE_COMMUNITY_MUTATION, { slug })
|
||||
const deleteResult = (result as any).delete_community
|
||||
const deleteResult = (result as DeleteCommunityResponse).delete_community
|
||||
|
||||
if (deleteResult.error) {
|
||||
throw new Error(deleteResult.error)
|
||||
@@ -303,19 +346,17 @@ const CommunitiesRoute: Component<CommunitiesRouteProps> = (props) => {
|
||||
'text-overflow': 'ellipsis',
|
||||
'white-space': 'nowrap'
|
||||
}}
|
||||
title={community.desc}
|
||||
title={community.description}
|
||||
>
|
||||
{community.desc || '—'}
|
||||
{community.description || '—'}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<Show when={community.created_by} fallback={<span>—</span>}>
|
||||
<span>{community.created_by?.name || community.created_by?.email || ''}</span>
|
||||
</Show>
|
||||
<span>{community.creator_name || ''}</span>
|
||||
</td>
|
||||
<td>{community.stat.shouts}</td>
|
||||
<td>{community.stat.followers}</td>
|
||||
<td>{community.stat.authors}</td>
|
||||
<td>{community.shouts_count}</td>
|
||||
<td>{community.followers_count}</td>
|
||||
<td>{community.creator_id}</td>
|
||||
<td>{formatDate(community.created_at)}</td>
|
||||
<td onClick={(e) => e.stopPropagation()}>
|
||||
<button
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
import { Component, createSignal } from 'solid-js'
|
||||
import { ADMIN_UPDATE_PERMISSIONS_MUTATION } from '../graphql/mutations'
|
||||
import { query } from '../graphql'
|
||||
import Button from '../ui/Button'
|
||||
import { ADMIN_UPDATE_PERMISSIONS_MUTATION } from '../graphql/mutations'
|
||||
import styles from '../styles/Admin.module.css'
|
||||
import Button from '../ui/Button'
|
||||
|
||||
/**
|
||||
* Интерфейс свойств компонента PermissionsRoute
|
||||
@@ -66,8 +66,8 @@ const PermissionsRoute: Component<PermissionsRouteProps> = (props) => {
|
||||
</ul>
|
||||
|
||||
<div class={styles['warning-box']}>
|
||||
<strong>⚠️ Внимание:</strong> Эта операция затрагивает все сообщества в системе.
|
||||
Рекомендуется выполнять только при изменении системы прав.
|
||||
<strong>⚠️ Внимание:</strong> Эта операция затрагивает все сообщества в системе. Рекомендуется
|
||||
выполнять только при изменении системы прав.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ const ReactionsRoute: Component<ReactionsRouteProps> = (props) => {
|
||||
}>(`${location.origin}/graphql`, ADMIN_GET_REACTIONS_QUERY, {
|
||||
search: isShoutId ? '' : query_value, // Если это ID, не передаем в обычный поиск
|
||||
kind: kindFilter() || undefined,
|
||||
shout_id: isShoutId ? Number.parseInt(query_value) : undefined, // Если это ID, передаем в shout_id
|
||||
shout_id: isShoutId ? Number.parseInt(query_value, 10) : undefined, // Если это ID, передаем в shout_id
|
||||
status: showDeletedOnly() ? 'deleted' : 'all',
|
||||
limit: pagination().limit,
|
||||
offset: (pagination().page - 1) * pagination().limit
|
||||
|
||||
@@ -20,7 +20,7 @@ const Button: Component<ButtonProps> = (props) => {
|
||||
const customClass = local.class || ''
|
||||
|
||||
return [baseClass, variantClass, sizeClass, loadingClass, fullWidthClass, customClass]
|
||||
.filter(Boolean)
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,8 @@ const CommunitySelector = () => {
|
||||
const allCommunities = communities()
|
||||
console.log('[CommunitySelector] Состояние:', {
|
||||
selectedId: current,
|
||||
selectedName: current !== null
|
||||
? allCommunities.find((c) => c.id === current)?.name
|
||||
: 'Все сообщества',
|
||||
selectedName:
|
||||
current !== null ? allCommunities.find((c) => c.id === current)?.name : 'Все сообщества',
|
||||
totalCommunities: allCommunities.length
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user