Refactor Header and TaskWorkspace components to enhance UI and user feedback. Header now accepts chainName and taskProgress props for improved context display. TaskWorkspace updates include styling adjustments, conditional rendering for queue status, and feedback messages, streamlining user interaction during task processing.
Some checks failed
platform/bro-js/challenge-pl/pipeline/head There was a failure building this commit

This commit is contained in:
Primakov Alexandr Alexandrovich 2025-11-04 11:55:09 +03:00
parent bb31cc5c6c
commit 79e3dc9864
3 changed files with 73 additions and 85 deletions

View File

@ -3,7 +3,12 @@ import { Box, Button, Flex, Heading, Text } from '@chakra-ui/react'
import { useChallenge } from '../context/ChallengeContext' import { useChallenge } from '../context/ChallengeContext'
export const Header = () => { interface HeaderProps {
chainName?: string
taskProgress?: string
}
export const Header = ({ chainName, taskProgress }: HeaderProps) => {
const { nickname, logout } = useChallenge() const { nickname, logout } = useChallenge()
if (!nickname) return null if (!nickname) return null
@ -13,11 +18,21 @@ export const Header = () => {
<Flex maxW="1200px" mx="auto" justify="space-between" align="center"> <Flex maxW="1200px" mx="auto" justify="space-between" align="center">
<Box> <Box>
<Heading size="md" color="teal.600"> <Heading size="md" color="teal.600">
Challenge Platform {chainName || 'Challenge Platform'}
</Heading> </Heading>
<Text fontSize="sm" color="gray.600" mt={1}> <Flex gap={3} align="center" mt={1}>
<Text fontSize="sm" color="gray.600">
{nickname} {nickname}
</Text> </Text>
{taskProgress && (
<>
<Text fontSize="sm" color="gray.400"></Text>
<Text fontSize="sm" color="gray.600">
{taskProgress}
</Text>
</>
)}
</Flex>
</Box> </Box>
<Button onClick={logout} variant="ghost" size="sm"> <Button onClick={logout} variant="ghost" size="sm">
Выйти Выйти

View File

@ -36,109 +36,102 @@ export const TaskWorkspace = ({ task, onTaskComplete }: TaskWorkspaceProps) => {
}, [finalSubmission, refreshStats]) }, [finalSubmission, refreshStats])
return ( return (
<VStack align="stretch" gap={4}> <VStack align="stretch" gap={3}>
<Box borderWidth="1px" borderRadius="lg" borderColor="gray.200" p={4} bg={descriptionBg}> <Box borderWidth="1px" borderRadius="md" borderColor="gray.200" p={3} bg={descriptionBg}>
<Text fontSize="lg" fontWeight="semibold" mb={2}> <Text fontSize="md" fontWeight="semibold" mb={1}>
{task.title} {task.title}
</Text> </Text>
<Text whiteSpace="pre-line" color="gray.700"> <Text whiteSpace="pre-line" color="gray.700" fontSize="sm">
{task.description} {task.description}
</Text> </Text>
</Box> </Box>
{/* Статус проверки */} {/* Статус проверки */}
{queueStatus && !finalSubmission && ( {queueStatus && !finalSubmission && (
<Box borderWidth="1px" borderRadius="lg" borderColor="blue.200" bg="blue.50" p={4}> <Box borderWidth="1px" borderRadius="md" borderColor="blue.200" bg="blue.50" p={2}>
<HStack> <HStack gap={2}>
<Spinner size="md" color="blue.500" /> <Spinner size="sm" color="blue.500" />
<VStack align="flex-start" gap={1}> <Text fontSize="sm" fontWeight="medium" color="blue.700">
<Text fontWeight="semibold" color="blue.700">
{queueStatus.status === 'waiting' ? 'Ожидание в очереди...' : 'Проверяем решение...'} {queueStatus.status === 'waiting' ? 'Ожидание в очереди...' : 'Проверяем решение...'}
{typeof queueStatus.position === 'number' && queueStatus.position > 0 && ` (позиция: ${queueStatus.position})`}
</Text> </Text>
{typeof queueStatus.position === 'number' && queueStatus.position > 0 && (
<Text fontSize="sm" color="blue.600">
Позиция в очереди: {queueStatus.position}
</Text>
)}
</VStack>
</HStack> </HStack>
</Box> </Box>
)} )}
{/* Результат проверки - успех */} {/* Результат проверки - успех */}
{isAccepted && ( {isAccepted && (
<Box borderWidth="2px" borderRadius="lg" borderColor="green.300" bg="green.50" p={4}> <Box borderWidth="1px" borderRadius="md" borderColor="green.300" bg="green.50" p={3}>
<VStack gap={3}> <HStack justify="space-between" align="start">
<Text fontSize="3xl"></Text> <HStack align="start" gap={2} flex={1}>
<Text fontSize="xl" fontWeight="bold" color="green.700"> <Text fontSize="xl"></Text>
<Box>
<Text fontSize="md" fontWeight="bold" color="green.700">
Задание принято! Задание принято!
</Text> </Text>
{finalSubmission?.feedback && ( {finalSubmission?.feedback && (
<Box w="100%" bg="white" borderRadius="md" p={3} borderWidth="1px" borderColor="green.200"> <Text fontSize="sm" color="gray.700" mt={1}>
<Text fontWeight="medium" color="green.800" mb={1}>
Комментарий:
</Text>
<Text whiteSpace="pre-wrap" color="gray.700">
{finalSubmission.feedback} {finalSubmission.feedback}
</Text> </Text>
</Box>
)} )}
<Button onClick={onTaskComplete} colorScheme="green" size="lg"> </Box>
Следующее задание </HStack>
<Button onClick={onTaskComplete} colorScheme="green" size="sm">
Следующее
</Button> </Button>
</VStack> </HStack>
</Box> </Box>
)} )}
{/* Результат проверки - требуется доработка */} {/* Результат проверки - требуется доработка */}
{needsRevision && ( {needsRevision && (
<Box borderWidth="2px" borderRadius="lg" borderColor="orange.300" bg="orange.50" p={4}> <Box borderWidth="1px" borderRadius="md" borderColor="orange.300" bg="orange.50" p={3}>
<VStack gap={3}> <HStack align="start" gap={2}>
<Text fontSize="3xl"></Text> <Text fontSize="xl"></Text>
<Text fontSize="xl" fontWeight="bold" color="orange.700"> <Box flex={1}>
<HStack>
<Text fontSize="md" fontWeight="bold" color="orange.700">
Требуется доработка Требуется доработка
</Text> </Text>
{finalSubmission?.feedback && ( <Text fontSize="xs" color="gray.600">
<Box w="100%" bg="white" borderRadius="md" p={3} borderWidth="1px" borderColor="orange.200"> (попытка {finalSubmission?.attemptNumber})
<Text fontWeight="medium" color="orange.800" mb={1}>
Комментарий проверяющего:
</Text> </Text>
<Text whiteSpace="pre-wrap" color="gray.700"> </HStack>
{finalSubmission?.feedback && (
<Text fontSize="sm" color="gray.700" mt={1}>
{finalSubmission.feedback} {finalSubmission.feedback}
</Text> </Text>
</Box>
)} )}
<Text fontSize="sm" color="gray.600"> </Box>
Попытка {finalSubmission?.attemptNumber} </HStack>
</Text>
</VStack>
</Box> </Box>
)} )}
<Box> <Box>
<Text fontWeight="medium" mb={2}> <Text fontWeight="medium" mb={1} fontSize="sm">
Ваше решение Ваше решение
</Text> </Text>
<Textarea <Textarea
value={result} value={result}
onChange={(event) => setResult(event.target.value)} onChange={(event) => setResult(event.target.value)}
placeholder="Напишите ваше решение..." placeholder="Напишите ваше решение..."
rows={14} rows={8}
bg="white" bg="white"
fontSize="sm"
// @ts-expect-error Chakra UI v2 uses isDisabled // @ts-expect-error Chakra UI v2 uses isDisabled
isDisabled={isChecking || isAccepted} isDisabled={isChecking || isAccepted}
/> />
</Box> </Box>
<HStack justify="flex-end" gap={3}> <HStack justify="flex-end" gap={2}>
{!isAccepted && ( {!isAccepted && (
<> <>
{/* @ts-expect-error Chakra UI v2 uses isDisabled */} {/* @ts-expect-error Chakra UI v2 uses isDisabled */}
<Button onClick={reset} variant="ghost" isDisabled={isChecking}> <Button onClick={reset} variant="ghost" size="sm" isDisabled={isChecking}>
Сбросить Сбросить
</Button> </Button>
{/* @ts-expect-error Chakra UI v2 uses isLoading/isDisabled */} {/* @ts-expect-error Chakra UI v2 uses isLoading/isDisabled */}
<Button onClick={submit} colorScheme="teal" isLoading={isChecking} isDisabled={!result.trim() || isChecking}> <Button onClick={submit} colorScheme="teal" size="sm" isLoading={isChecking} isDisabled={!result.trim() || isChecking}>
{needsRevision ? 'Отправить снова' : 'Отправить на проверку'} {needsRevision ? 'Отправить снова' : 'Отправить на проверку'}
</Button> </Button>
</> </>

View File

@ -1,9 +1,6 @@
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { import {
Box, Box,
Button,
Flex,
Heading,
Text, Text,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { Alert } from '@chakra-ui/react/alert' import { Alert } from '@chakra-ui/react/alert'
@ -44,11 +41,6 @@ export const MainPage = () => {
} }
} }
const handleBackToChains = () => {
setSelectedChain(null)
setSelectedTask(null)
}
useEffect(() => { useEffect(() => {
const unsubscribe = eventEmitter.on('submission_completed', (event) => { const unsubscribe = eventEmitter.on('submission_completed', (event) => {
const submission = (event.data as { submission?: { status: string; attemptNumber: number } })?.submission const submission = (event.data as { submission?: { status: string; attemptNumber: number } })?.submission
@ -100,11 +92,13 @@ export const MainPage = () => {
) )
} }
const taskProgress = `Задание ${selectedChain.tasks.findIndex(t => t.id === selectedTask?.id) + 1} из ${selectedChain.tasks.length}`
// Показываем выбранную цепочку и задания // Показываем выбранную цепочку и задания
return ( return (
<> <>
<Header /> <Header chainName={selectedChain.name} taskProgress={taskProgress} />
<Box bg="gray.50" minH="100vh" py={8} px={{ base: 4, md: 8 }}> <Box bg="gray.50" minH="100vh" py={4} px={{ base: 4, md: 8 }}>
<Box maxW="1200px" mx="auto"> <Box maxW="1200px" mx="auto">
{notification && ( {notification && (
<Alert.Root status={notification.status} borderRadius="md" mb={4}> <Alert.Root status={notification.status} borderRadius="md" mb={4}>
@ -123,20 +117,6 @@ export const MainPage = () => {
</Alert.Root> </Alert.Root>
)} )}
<Flex justify="space-between" align="center" mb={8}>
<Box>
<Heading size="lg" mb={1}>
{selectedChain.name}
</Heading>
<Text color="gray.600">
Задание {selectedChain.tasks.findIndex(t => t.id === selectedTask?.id) + 1} из {selectedChain.tasks.length}
</Text>
</Box>
<Button onClick={handleBackToChains} variant="outline">
Вернуться к цепочкам
</Button>
</Flex>
{selectedTask && ( {selectedTask && (
<TaskWorkspace task={selectedTask} onTaskComplete={handleTaskComplete} /> <TaskWorkspace task={selectedTask} onTaskComplete={handleTaskComplete} />
)} )}