Browse Source
- /tests/:id — employee view, answers shown without correct/incorrect markers - /tests/:id/edit — author view, correct answers highlighted + edit button (disabled until Sprint 4) - TestList: add 'Изменить' button linking to author view Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>master
4 changed files with 130 additions and 10 deletions
@ -0,0 +1,118 @@ |
|||||||
|
import { |
||||||
|
ArrowLeftOutlined, |
||||||
|
CheckCircleTwoTone, |
||||||
|
CloseCircleTwoTone, |
||||||
|
EditOutlined, |
||||||
|
PlayCircleOutlined, |
||||||
|
} from '@ant-design/icons' |
||||||
|
import { useQuery } from '@tanstack/react-query' |
||||||
|
import { Alert, Button, Card, Descriptions, List, Space, Spin, Tag, Typography } from 'antd' |
||||||
|
import { useNavigate, useParams } from 'react-router-dom' |
||||||
|
|
||||||
|
import { Answer, testsApi } from '../../api/tests' |
||||||
|
|
||||||
|
const { Title, Text } = Typography |
||||||
|
|
||||||
|
export default function TestEdit() { |
||||||
|
const { id } = useParams<{ id: string }>() |
||||||
|
const navigate = useNavigate() |
||||||
|
|
||||||
|
const { data: test, isLoading } = useQuery({ |
||||||
|
queryKey: ['tests', id], |
||||||
|
queryFn: () => testsApi.get(Number(id)).then((r) => r.data), |
||||||
|
}) |
||||||
|
|
||||||
|
if (isLoading) { |
||||||
|
return <Spin size="large" style={{ display: 'block', margin: '48px auto' }} /> |
||||||
|
} |
||||||
|
|
||||||
|
if (!test) return null |
||||||
|
|
||||||
|
return ( |
||||||
|
<div style={{ maxWidth: 820, margin: '0 auto', padding: 24 }}> |
||||||
|
<Space style={{ width: '100%', justifyContent: 'space-between', marginBottom: 16 }}> |
||||||
|
<Button icon={<ArrowLeftOutlined />} onClick={() => navigate('/')}> |
||||||
|
К списку тестов |
||||||
|
</Button> |
||||||
|
<Space> |
||||||
|
<Button |
||||||
|
icon={<EditOutlined />} |
||||||
|
disabled |
||||||
|
title="Редактирование будет доступно в следующем спринте" |
||||||
|
> |
||||||
|
Редактировать |
||||||
|
</Button> |
||||||
|
<Button |
||||||
|
type="primary" |
||||||
|
icon={<PlayCircleOutlined />} |
||||||
|
onClick={() => navigate(`/tests/${test.id}/take`)} |
||||||
|
> |
||||||
|
Пройти тест |
||||||
|
</Button> |
||||||
|
</Space> |
||||||
|
</Space> |
||||||
|
|
||||||
|
<Alert |
||||||
|
type="warning" |
||||||
|
showIcon |
||||||
|
message="Вид автора — правильные ответы отмечены" |
||||||
|
style={{ marginBottom: 16 }} |
||||||
|
/> |
||||||
|
|
||||||
|
<Title level={2}>{test.title}</Title> |
||||||
|
|
||||||
|
{test.description && ( |
||||||
|
<Text type="secondary" style={{ display: 'block', marginBottom: 16 }}> |
||||||
|
{test.description} |
||||||
|
</Text> |
||||||
|
)} |
||||||
|
|
||||||
|
<Card style={{ marginBottom: 24 }}> |
||||||
|
<Descriptions column={3}> |
||||||
|
<Descriptions.Item label="Вопросов">{test.questions.length}</Descriptions.Item> |
||||||
|
<Descriptions.Item label="Порог зачёта">{test.passing_score}%</Descriptions.Item> |
||||||
|
<Descriptions.Item label="Таймер"> |
||||||
|
{test.time_limit ? `${test.time_limit} мин` : 'Без ограничений'} |
||||||
|
</Descriptions.Item> |
||||||
|
<Descriptions.Item label="Возврат к вопросу"> |
||||||
|
{test.allow_navigation_back ? ( |
||||||
|
<Tag color="green">Разрешён</Tag> |
||||||
|
) : ( |
||||||
|
<Tag color="red">Запрещён</Tag> |
||||||
|
)} |
||||||
|
</Descriptions.Item> |
||||||
|
<Descriptions.Item label="Версия">{test.version}</Descriptions.Item> |
||||||
|
<Descriptions.Item label="Создан"> |
||||||
|
{new Date(test.created_at).toLocaleDateString('ru-RU')} |
||||||
|
</Descriptions.Item> |
||||||
|
</Descriptions> |
||||||
|
</Card> |
||||||
|
|
||||||
|
<Title level={3}>Вопросы ({test.questions.length})</Title> |
||||||
|
|
||||||
|
{test.questions.map((question, index) => ( |
||||||
|
<Card key={question.id} style={{ marginBottom: 12 }}> |
||||||
|
<Text strong> |
||||||
|
{index + 1}. {question.text} |
||||||
|
</Text> |
||||||
|
<List |
||||||
|
style={{ marginTop: 10 }} |
||||||
|
dataSource={question.answers} |
||||||
|
renderItem={(answer: Answer) => ( |
||||||
|
<List.Item style={{ padding: '4px 0' }}> |
||||||
|
<Space> |
||||||
|
{answer.is_correct ? ( |
||||||
|
<CheckCircleTwoTone twoToneColor="#52c41a" /> |
||||||
|
) : ( |
||||||
|
<CloseCircleTwoTone twoToneColor="#d9d9d9" /> |
||||||
|
)} |
||||||
|
<Text>{answer.text}</Text> |
||||||
|
</Space> |
||||||
|
</List.Item> |
||||||
|
)} |
||||||
|
/> |
||||||
|
</Card> |
||||||
|
))} |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
Loading…
Reference in new issue