Files
challenge-pl/src/pages/main/main.tsx
T

128 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useRef, useState } from 'react'
import {
Box,
Text,
} from '@chakra-ui/react'
import { Alert } from '@chakra-ui/react/alert'
import type { ChallengeChain, ChallengeTask } from '../../__data__/types'
import { useChallenge } from '../../context/ChallengeContext'
import { TaskWorkspace } from '../../components/personal'
import { Header } from '../../components/Header'
import { LoginForm } from '../../components/LoginForm'
import { ChainSelector } from '../../components/ChainSelector'
export const MainPage = () => {
const { nickname, eventEmitter } = useChallenge()
const [selectedChain, setSelectedChain] = useState<ChallengeChain | null>(null)
const [selectedTask, setSelectedTask] = useState<ChallengeTask | null>(null)
const [isOffline, setIsOffline] = useState(() =>
typeof navigator !== 'undefined' ? !navigator.onLine : false,
)
const [notification, setNotification] = useState<{ status: 'success' | 'warning'; title: string; description?: string } | null>(null)
const notificationTimeoutRef = useRef<number | null>(null)
const handleSelectChain = (chain: ChallengeChain) => {
setSelectedChain(chain)
setSelectedTask(chain.tasks[0])
}
const handleTaskComplete = () => {
if (!selectedChain) return
const currentIndex = selectedChain.tasks.findIndex((item) => item.id === selectedTask?.id)
const nextTask = currentIndex >= 0 ? selectedChain.tasks[currentIndex + 1] : null
if (nextTask) {
setSelectedTask(nextTask)
} else {
// Цепочка завершена, возвращаемся к выбору
setSelectedChain(null)
setSelectedTask(null)
}
}
useEffect(() => {
const unsubscribe = eventEmitter.on('submission_completed', (event) => {
const submission = (event.data as { submission?: { status: string; attemptNumber: number } })?.submission
const accepted = submission?.status === 'accepted'
const title = accepted ? 'Задание принято' : 'Задание требует доработки'
const description = submission ? `Попытка №${submission.attemptNumber}` : undefined
if (notificationTimeoutRef.current) {
window.clearTimeout(notificationTimeoutRef.current)
}
setNotification({ status: accepted ? 'success' : 'warning', title, description })
notificationTimeoutRef.current = window.setTimeout(() => setNotification(null), 4000)
})
return () => {
unsubscribe()
if (notificationTimeoutRef.current) {
window.clearTimeout(notificationTimeoutRef.current)
}
}
}, [eventEmitter])
useEffect(() => {
const handleOnline = () => setIsOffline(false)
const handleOffline = () => setIsOffline(true)
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
return () => {
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
}
}, [])
// Если пользователь не авторизован, показываем форму входа
if (!nickname) {
return <LoginForm />
}
// Если цепочка не выбрана, показываем селектор цепочек
if (!selectedChain) {
return (
<>
<Header />
<ChainSelector onSelectChain={handleSelectChain} />
</>
)
}
const taskProgress = `Задание ${selectedChain.tasks.findIndex(t => t.id === selectedTask?.id) + 1} из ${selectedChain.tasks.length}`
// Показываем выбранную цепочку и задания
return (
<>
<Header chainName={selectedChain.name} taskProgress={taskProgress} />
<Box bg="gray.50" minH="100vh" py={4} px={{ base: 4, md: 8 }}>
<Box maxW="1200px" mx="auto">
{notification && (
<Alert.Root status={notification.status} borderRadius="md" mb={4}>
<Alert.Indicator />
<Box ml={3}>
<Text fontWeight="semibold">{notification.title}</Text>
{notification.description && <Text fontSize="sm">{notification.description}</Text>}
</Box>
</Alert.Root>
)}
{isOffline && (
<Alert.Root status="warning" borderRadius="md" mb={4}>
<Alert.Indicator />
Вы находитесь офлайн. Черновики сохраняются локально и будут отправлены после восстановления связи.
</Alert.Root>
)}
{selectedTask && (
<TaskWorkspace task={selectedTask} onTaskComplete={handleTaskComplete} />
)}
</Box>
</Box>
</>
)
}