Приложение для тестирования сотрудников клиники методом один вопрос - до пяти ответов один из которых правильный. Сотрудник должен выбрать правильный вариант ответа
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

145 lines
4.0 KiB

import { CheckCircleTwoTone, CloseCircleTwoTone } from '@ant-design/icons'
import { useQuery } from '@tanstack/react-query'
import { Button, DatePicker, Select, Space, Table, Tag, Typography } from 'antd'
import type { ColumnsType } from 'antd/es/table'
import dayjs, { Dayjs } from 'dayjs'
import { useState } from 'react'
import { AttemptListItem, attemptsApi } from '../../api/attempts'
import { testsApi } from '../../api/tests'
const { Title } = Typography
const { RangePicker } = DatePicker
export default function Tracker() {
const [testId, setTestId] = useState<number | undefined>()
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs] | null>(null)
const [page, setPage] = useState(1)
const pageSize = 20
const params = {
test_id: testId,
date_from: dateRange?.[0].startOf('day').toISOString(),
date_to: dateRange?.[1].endOf('day').toISOString(),
page,
page_size: pageSize,
}
const { data, isLoading } = useQuery({
queryKey: ['attempts', params],
queryFn: () => attemptsApi.list(params).then((r) => r.data),
})
const { data: testsData } = useQuery({
queryKey: ['tests'],
queryFn: () => testsApi.list().then((r) => r.data),
})
const handleReset = () => {
setTestId(undefined)
setDateRange(null)
setPage(1)
}
const columns: ColumnsType<AttemptListItem> = [
{
title: 'Сотрудник',
dataIndex: 'user_name',
width: 120,
},
{
title: 'Тест',
key: 'test',
render: (_, r) => (
<span>
{r.test_title}{' '}
<Tag color="default" style={{ fontSize: 11 }}>
v{r.test_version}
</Tag>
</span>
),
},
{
title: 'Начало',
dataIndex: 'started_at',
width: 160,
render: (v: string) => dayjs(v).format('DD.MM.YYYY HH:mm'),
},
{
title: 'Завершение',
dataIndex: 'finished_at',
width: 160,
render: (v: string | null) => (v ? dayjs(v).format('DD.MM.YYYY HH:mm') : '—'),
},
{
title: 'Результат',
key: 'result',
width: 140,
render: (_, r) =>
r.correct_count != null && r.total_count != null
? `${r.correct_count} / ${r.total_count} (${r.score?.toFixed(1)}%)`
: '—',
},
{
title: 'Зачёт',
dataIndex: 'passed',
width: 90,
render: (passed: boolean | null) => {
if (passed == null) return '—'
return passed ? (
<Space size={4}>
<CheckCircleTwoTone twoToneColor="#52c41a" />
<Tag color="success">Сдал</Tag>
</Space>
) : (
<Space size={4}>
<CloseCircleTwoTone twoToneColor="#ff4d4f" />
<Tag color="error">Не сдал</Tag>
</Space>
)
},
},
]
return (
<div style={{ maxWidth: 1000, margin: '32px auto', padding: '0 24px' }}>
<Title level={2}>Трекер результатов</Title>
{/* Фильтры */}
<Space wrap style={{ marginBottom: 16 }}>
<Select
allowClear
placeholder="Все тесты"
style={{ width: 260 }}
value={testId}
onChange={(v) => { setTestId(v); setPage(1) }}
options={(testsData ?? []).map((t) => ({
value: t.id,
label: `${t.title} (v${t.version})`,
}))}
/>
<RangePicker
value={dateRange}
onChange={(v) => { setDateRange(v as [Dayjs, Dayjs] | null); setPage(1) }}
format="DD.MM.YYYY"
placeholder={['Дата от', 'Дата до']}
/>
<Button onClick={handleReset}>Сбросить</Button>
</Space>
<Table
rowKey="id"
columns={columns}
dataSource={data?.items ?? []}
loading={isLoading}
pagination={{
current: page,
pageSize,
total: data?.total ?? 0,
onChange: setPage,
showTotal: (total) => `Всего: ${total}`,
}}
/>
</div>
)
}