import React, { useMemo, useState } from 'react' import { Badge, Box, HStack, Heading, SimpleGrid, Table, TableBody, TableCell, TableColumnHeader, TableContainer, TableHeader, TableRow, Text, VStack, Select, } from '@chakra-ui/react' import { useGetAllSubmissionsQuery, useGetChainsQuery, useGetSystemStatsQuery, } from '../../__data__/api/api' import type { ChallengeChain } from '../../__data__/types' import { StatCard } from '../../components/personal' import { ABTestPanel } from '../../components/admin/ABTestPanel' import { mapTaskMetrics, detectIssues, msToMinutes } from '../../utils/analytics' import { keycloak } from '../../__data__/kc' const formatNumber = (value: number | undefined) => { if (!value && value !== 0) return '—' return Intl.NumberFormat('ru-RU').format(value) } const hasTeacherRole = () => { try { return keycloak?.hasResourceRole?.('teacher', 'journal') ?? false } catch (error) { return false } } export const AdminDashboard = () => { const isTeacher = hasTeacherRole() const { data: systemStats, isLoading } = useGetSystemStatsQuery() const { data: chains = [] } = useGetChainsQuery(undefined, { skip: !isTeacher }) const { data: submissions = [] } = useGetAllSubmissionsQuery(undefined, { skip: !isTeacher }) const issues = useMemo(() => (systemStats ? detectIssues(systemStats) : []), [systemStats]) const taskMetrics = useMemo(() => mapTaskMetrics(submissions), [submissions]) const [difficultyFilter, setDifficultyFilter] = useState<'all' | 'easy' | 'medium' | 'hard'>('all') const filteredTaskMetrics = useMemo(() => { if (difficultyFilter === 'all') return taskMetrics return taskMetrics.filter((metric) => metric.difficulty === difficultyFilter) }, [difficultyFilter, taskMetrics]) if (!isTeacher) { return ( Требуется роль преподавателя У вас нет доступа к панели администратора. Обратитесь к администратору Keycloak для назначения роли teacher . ) } if (isLoading || !systemStats) { return ( Загружаем системные метрики... ) } return ( Панель преподавателя Статус очереди Всего в очереди {formatNumber(systemStats.queue.queueLength)} В ожидании {formatNumber(systemStats.queue.waiting)} В обработке {formatNumber(systemStats.queue.currentlyProcessing)} / {formatNumber(systemStats.queue.maxConcurrency)} Проблемные области {issues.length === 0 ? ( Критических проблем не обнаружено. ) : ( {issues.map((issue) => ( {issue.message} Сущность: {issue.affectedEntity} · Важность: {issue.severity} ))} )} Метрики по заданиям {filteredTaskMetrics.length === 0 ? ( Недостаточно данных о проверках для построения аналитики. ) : ( Задание Попыток Успешность Сред. попыток Сред. время (мин) Сложность {filteredTaskMetrics.map((metric) => ( {metric.title} ID: {metric.taskId} {formatNumber(metric.attemptsCount)} {formatNumber(Math.round(metric.successRate))}% {formatNumber(Math.round(metric.avgAttempts * 10) / 10)} {formatNumber(Math.round(msToMinutes(metric.avgTimeToComplete * 1000)))} {metric.difficulty} ))}
)}
Цепочки заданий {chains.length === 0 ? ( Цепочки не найдены. Создайте первое задание, чтобы начать. ) : ( {chains.map((chain: ChallengeChain) => ( {chain.name} Заданий: {chain.tasks.length} · Последнее обновление:{' '} {new Date(chain.updatedAt).toLocaleString()} ))} )}
) }