import React, { useState } from 'react' import { useTranslation } from 'react-i18next' import { Box, Heading, Table, Input, Text, Button, HStack, VStack, Select, DialogRoot, DialogContent, DialogHeader, DialogTitle, DialogBody, DialogFooter, DialogActionTrigger, createListCollection, } from '@chakra-ui/react' import ReactMarkdown from 'react-markdown' import { useGetSystemStatsV2Query, useGetUserSubmissionsQuery } from '../../__data__/api/api' import { LoadingSpinner } from '../../components/LoadingSpinner' import { ErrorAlert } from '../../components/ErrorAlert' import { EmptyState } from '../../components/EmptyState' import { StatusBadge } from '../../components/StatusBadge' import type { ActiveParticipant, ChallengeSubmission, SubmissionStatus, ChallengeTask, ChallengeUser, } from '../../types/challenge' export const SubmissionsPage: React.FC = () => { const { t } = useTranslation() const { data: stats, isLoading: isStatsLoading, error: statsError, refetch: refetchStats } = useGetSystemStatsV2Query(undefined) const [searchQuery, setSearchQuery] = useState('') const [statusFilter, setStatusFilter] = useState('all') const [selectedSubmission, setSelectedSubmission] = useState(null) const [selectedUserId, setSelectedUserId] = useState(null) const { data: submissions, isLoading: isSubmissionsLoading, error: submissionsError, refetch: refetchSubmissions, } = useGetUserSubmissionsQuery( { userId: selectedUserId!, taskId: undefined }, { skip: !selectedUserId } ) const isLoading = isStatsLoading || (selectedUserId && isSubmissionsLoading) const error = statsError || submissionsError const handleRetry = () => { refetchStats() if (selectedUserId) { refetchSubmissions() } } if (isLoading) { return } if (error || !stats) { return } const participants: ActiveParticipant[] = stats.activeParticipants || [] const submissionsList: ChallengeSubmission[] = submissions || [] const filteredSubmissions = submissionsList.filter((submission) => { const user = submission.user as ChallengeUser const task = submission.task as ChallengeTask const matchesSearch = user.nickname.toLowerCase().includes(searchQuery.toLowerCase()) || task.title.toLowerCase().includes(searchQuery.toLowerCase()) const matchesStatus = statusFilter === 'all' || submission.status === statusFilter return matchesSearch && matchesStatus }) const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleString('ru-RU', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }) } const getCheckTime = (submission: ChallengeSubmission) => { if (!submission.checkedAt) return '—' const submitted = new Date(submission.submittedAt).getTime() const checked = new Date(submission.checkedAt).getTime() const diff = Math.round((checked - submitted) / 1000) return t('challenge.admin.submissions.check.time', { time: diff }) } const statusOptions = createListCollection({ items: [ { label: t('challenge.admin.submissions.status.all'), value: 'all' }, { label: t('challenge.admin.submissions.status.accepted'), value: 'accepted' }, { label: t('challenge.admin.submissions.status.needs.revision'), value: 'needs_revision' }, { label: t('challenge.admin.submissions.status.in.progress'), value: 'in_progress' }, { label: t('challenge.admin.submissions.status.pending'), value: 'pending' }, ], }) const userOptions = createListCollection({ items: participants.map((participant) => ({ label: `${participant.nickname} (${participant.userId})`, value: participant.userId, })), }) const hasParticipants = participants.length > 0 const hasSelectedUser = !!selectedUserId return ( {t('challenge.admin.submissions.title')} {/* Filters */} {hasParticipants && ( setSelectedUserId(e.value[0] ?? null)} maxW="300px" > {userOptions.items.map((option) => ( {option.label} ))} {submissionsList.length > 0 && ( <> setSearchQuery(e.target.value)} maxW="400px" /> setStatusFilter(e.value[0] as SubmissionStatus | 'all')} maxW="200px" > {statusOptions.items.map((option) => ( {option.label} ))} )} )} {!hasParticipants ? ( ) : !hasSelectedUser ? ( ) : filteredSubmissions.length === 0 ? ( ) : ( {t('challenge.admin.submissions.table.user')} {t('challenge.admin.submissions.table.task')} {t('challenge.admin.submissions.table.status')} {t('challenge.admin.submissions.table.attempt')} {t('challenge.admin.submissions.table.submitted')} {t('challenge.admin.submissions.table.check.time')} {t('challenge.admin.submissions.table.actions')} {filteredSubmissions.map((submission) => { const user = submission.user as ChallengeUser const task = submission.task as ChallengeTask return ( {user.nickname} {task.title} #{submission.attemptNumber} {formatDate(submission.submittedAt)} {getCheckTime(submission)} ) })} )} {/* Submission Details Modal */} setSelectedSubmission(null)} /> ) } interface SubmissionDetailsModalProps { submission: ChallengeSubmission | null isOpen: boolean onClose: () => void } const SubmissionDetailsModal: React.FC = ({ submission, isOpen, onClose, }) => { const { t } = useTranslation() if (!submission) return null const user = submission.user as ChallengeUser const task = submission.task as ChallengeTask const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleString('ru-RU', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', }) } const getCheckTimeValue = () => { if (!submission.checkedAt) return null const submitted = new Date(submission.submittedAt).getTime() const checked = new Date(submission.checkedAt).getTime() return ((checked - submitted) / 1000).toFixed(2) } return ( !e.open && onClose()} size="xl"> {t('challenge.admin.submissions.details.title')} #{submission.attemptNumber} {/* Meta */} {t('challenge.admin.submissions.details.user')} {user.nickname} {t('challenge.admin.submissions.details.status')} {t('challenge.admin.submissions.details.submitted')} {formatDate(submission.submittedAt)} {submission.checkedAt && ( <> {t('challenge.admin.submissions.details.checked')} {formatDate(submission.checkedAt)} {t('challenge.admin.submissions.details.check.time')} {t('challenge.admin.submissions.check.time', { time: getCheckTimeValue() })} )} {/* Task */} {t('challenge.admin.submissions.details.task')} {task.title} {task.description} {/* Solution */} {t('challenge.admin.submissions.details.solution')} {submission.result} {/* Feedback */} {submission.feedback && ( {t('challenge.admin.submissions.details.feedback')} {submission.feedback} )} ) }