feat: split test detail into employee view and author view
- /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>
This commit is contained in:
@@ -6,6 +6,7 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import AttemptResult from './pages/AttemptResult'
|
||||
import TestCreate from './pages/TestCreate'
|
||||
import TestDetail from './pages/TestDetail'
|
||||
import TestEdit from './pages/TestEdit'
|
||||
import TestList from './pages/TestList'
|
||||
import TestTake from './pages/TestTake'
|
||||
|
||||
@@ -20,6 +21,7 @@ export default function App() {
|
||||
<Route path="/" element={<TestList />} />
|
||||
<Route path="/tests/create" element={<TestCreate />} />
|
||||
<Route path="/tests/:id" element={<TestDetail />} />
|
||||
<Route path="/tests/:id/edit" element={<TestEdit />} />
|
||||
<Route path="/tests/:testId/take" element={<TestTake />} />
|
||||
<Route path="/attempts/:attemptId/result" element={<AttemptResult />} />
|
||||
</Routes>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ArrowLeftOutlined, CheckCircleTwoTone, CloseCircleTwoTone, PlayCircleOutlined } from '@ant-design/icons'
|
||||
import { ArrowLeftOutlined, PlayCircleOutlined } from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Button, Card, Descriptions, List, Space, Spin, Tag, Typography } from 'antd'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
@@ -78,14 +78,7 @@ export default function TestDetail() {
|
||||
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>
|
||||
<Text>{answer.text}</Text>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PlayCircleOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
import { EditOutlined, PlayCircleOutlined, PlusOutlined } from '@ant-design/icons'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Button, Space, Spin, Table, Tag, Typography } from 'antd'
|
||||
import type { ColumnsType } from 'antd/es/table'
|
||||
@@ -64,6 +64,13 @@ export default function TestList() {
|
||||
<Button size="small" onClick={() => navigate(`/tests/${record.id}`)}>
|
||||
Открыть
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => navigate(`/tests/${record.id}/edit`)}
|
||||
>
|
||||
Изменить
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
|
||||
Reference in New Issue
Block a user