161 lines
5.8 KiB
TypeScript
161 lines
5.8 KiB
TypeScript
import React from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { Box, Heading, Text, Table, Badge } from '@chakra-ui/react'
|
|
import type { ChainDetailed, TaskProgressStatus } from '../../types/challenge'
|
|
|
|
interface ChainDetailedViewProps {
|
|
chains: ChainDetailed[]
|
|
}
|
|
|
|
interface StatusConfig {
|
|
label: string
|
|
color: 'gray' | 'yellow' | 'blue' | 'orange' | 'green'
|
|
}
|
|
|
|
const getStatusConfig = (status: TaskProgressStatus, t: (key: string) => string): StatusConfig => {
|
|
const configs: Record<TaskProgressStatus, StatusConfig> = {
|
|
not_started: { label: t('challenge.admin.detailed.stats.status.not.started'), color: 'gray' },
|
|
pending: { label: t('challenge.admin.detailed.stats.status.pending'), color: 'yellow' },
|
|
in_progress: { label: t('challenge.admin.detailed.stats.status.in.progress'), color: 'blue' },
|
|
needs_revision: { label: t('challenge.admin.detailed.stats.status.needs.revision'), color: 'orange' },
|
|
completed: { label: t('challenge.admin.detailed.stats.status.completed'), color: 'green' },
|
|
}
|
|
return configs[status]
|
|
}
|
|
|
|
export const ChainDetailedView: React.FC<ChainDetailedViewProps> = ({ chains }) => {
|
|
const { t } = useTranslation()
|
|
|
|
if (chains.length === 0) {
|
|
return (
|
|
<Box bg="white" p={6} borderRadius="lg" boxShadow="sm" borderWidth="1px" borderColor="gray.200">
|
|
<Heading size="md" mb={4}>
|
|
{t('challenge.admin.detailed.stats.chains.title')}
|
|
</Heading>
|
|
<Box color="gray.500" textAlign="center" py={8}>
|
|
{t('challenge.admin.detailed.stats.chains.empty')}
|
|
</Box>
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
const chain = chains[0] // Теперь всегда одна цепочка из API
|
|
|
|
return (
|
|
<Box bg="white" p={6} borderRadius="lg" boxShadow="sm" borderWidth="1px" borderColor="gray.200">
|
|
<Heading size="md" mb={4}>
|
|
{t('challenge.admin.detailed.stats.chains.title')}
|
|
</Heading>
|
|
|
|
<Box mb={3}>
|
|
<Heading size="sm" color="teal.600" mb={1}>
|
|
{chain.name}
|
|
</Heading>
|
|
<Text fontSize="sm" color="gray.600">
|
|
{t('challenge.admin.detailed.stats.chains.total.tasks')} {chain.totalTasks}
|
|
</Text>
|
|
</Box>
|
|
|
|
{chain.participantProgress.length > 0 ? (
|
|
<Box overflowX="auto">
|
|
<Table.Root size="sm" variant="outline">
|
|
<Table.Header>
|
|
<Table.Row bg="gray.50">
|
|
<Table.ColumnHeader
|
|
position="sticky"
|
|
left={0}
|
|
bg="gray.50"
|
|
zIndex={1}
|
|
minW="150px"
|
|
borderRight="1px"
|
|
borderColor="gray.200"
|
|
>
|
|
{t('challenge.admin.detailed.stats.chains.participant')}
|
|
</Table.ColumnHeader>
|
|
{chain.tasks.map((task) => (
|
|
<Table.ColumnHeader
|
|
key={task.taskId}
|
|
textAlign="center"
|
|
minW="120px"
|
|
maxW="200px"
|
|
>
|
|
<Text fontSize="xs" lineClamp={2} title={task.title}>
|
|
{task.title}
|
|
</Text>
|
|
</Table.ColumnHeader>
|
|
))}
|
|
<Table.ColumnHeader
|
|
textAlign="center"
|
|
minW="100px"
|
|
borderLeft="1px"
|
|
borderColor="gray.200"
|
|
fontWeight="bold"
|
|
>
|
|
{t('challenge.admin.detailed.stats.chains.progress')}
|
|
</Table.ColumnHeader>
|
|
</Table.Row>
|
|
</Table.Header>
|
|
<Table.Body>
|
|
{chain.participantProgress.map((participant) => (
|
|
<Table.Row key={participant.userId}>
|
|
<Table.Cell
|
|
position="sticky"
|
|
left={0}
|
|
bg="white"
|
|
zIndex={1}
|
|
fontWeight="medium"
|
|
borderRight="1px"
|
|
borderColor="gray.200"
|
|
>
|
|
{participant.nickname}
|
|
</Table.Cell>
|
|
{participant.taskProgress.map((taskProg) => {
|
|
const config = getStatusConfig(taskProg.status, t)
|
|
return (
|
|
<Table.Cell key={taskProg.taskId} textAlign="center">
|
|
<Badge
|
|
colorPalette={config.color}
|
|
size="sm"
|
|
title={taskProg.taskTitle}
|
|
>
|
|
{config.label}
|
|
</Badge>
|
|
</Table.Cell>
|
|
)
|
|
})}
|
|
<Table.Cell
|
|
textAlign="center"
|
|
borderLeft="1px"
|
|
borderColor="gray.200"
|
|
fontWeight="bold"
|
|
>
|
|
<Badge
|
|
colorPalette={
|
|
participant.progressPercent >= 80
|
|
? 'green'
|
|
: participant.progressPercent >= 50
|
|
? 'yellow'
|
|
: 'red'
|
|
}
|
|
>
|
|
{participant.progressPercent}%
|
|
</Badge>
|
|
<Text fontSize="xs" color="gray.600" mt={1}>
|
|
{participant.completedCount}/{chain.totalTasks}
|
|
</Text>
|
|
</Table.Cell>
|
|
</Table.Row>
|
|
))}
|
|
</Table.Body>
|
|
</Table.Root>
|
|
</Box>
|
|
) : (
|
|
<Box color="gray.500" textAlign="center" py={4} bg="gray.50" borderRadius="md">
|
|
{t('challenge.admin.detailed.stats.chains.no.participants')}
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|
|
|