import React, { useState } from 'react' import { Table, Thead, Tbody, Tr, Th, Td, Box, useColorMode, Button, useToast, Flex, Collapse, HStack, Text, Icon, Tooltip, Avatar, AvatarBadge, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, useDisclosure } from '@chakra-ui/react' import { CopyIcon, ChevronDownIcon, ChevronUpIcon } from '@chakra-ui/icons' import { FaSmile, FaMeh, FaFrown, FaSadTear, FaExpand, FaCompress } from 'react-icons/fa' import dayjs from 'dayjs' import { useTranslation } from 'react-i18next' import { getGravatarURL } from '../../../utils/gravatar' import { ShortText } from './ShortText' import { AttendanceData } from '../hooks' interface AttendanceTableProps { data: AttendanceData } export const AttendanceTable: React.FC = ({ data }) => { const { colorMode } = useColorMode() const toast = useToast() const { t } = useTranslation() const [showTable, setShowTable] = useState(false) const { isOpen, onOpen, onClose } = useDisclosure() const getPresentColor = () => { return colorMode === 'dark' ? 'green.600' : 'green.100' } const getAbsentColor = () => { return colorMode === 'dark' ? 'red.800' : 'red.100' } // Получаем эмоджи на основе посещаемости const getAttendanceEmoji = (attendedCount: number, totalLessons: number) => { const attendanceRate = totalLessons > 0 ? attendedCount / totalLessons : 0 if (attendanceRate >= 0.9) { return { icon: FaSmile, color: 'green.500', label: t('journal.pl.attendance.emojis.excellent') } } else if (attendanceRate >= 0.75) { return { icon: FaMeh, color: 'blue.400', label: t('journal.pl.attendance.emojis.good') } } else if (attendanceRate >= 0.5) { return { icon: FaFrown, color: 'orange.400', label: t('journal.pl.attendance.emojis.poor') } } else { return { icon: FaSadTear, color: 'red.500', label: t('journal.pl.attendance.emojis.none') } } } // Функция для копирования данных таблицы без сокращений const copyTableData = () => { if (!data.attendance?.length) return // Строим заголовок таблицы let tableContent = [] // Добавляем заголовки с именами преподавателей let headerRow = [] data.teachers?.forEach(teacher => { headerRow.push(teacher.value) }) // Добавляем столбцы даты и названия занятия headerRow.push(t('journal.pl.common.date'), t('journal.pl.common.lessonName')) // Добавляем студентов data.students.forEach(student => { headerRow.push(student.name || student.value || t('journal.pl.common.name')) }) // Добавляем заголовок в таблицу tableContent.push(headerRow.join('\t')) // Формируем данные для каждой строки data.attendance.forEach(lesson => { let row = [] // Добавляем данные о присутствии преподавателей data.teachers?.forEach(teacher => { const wasThere = Boolean(lesson.teachers) && lesson.teachers.findIndex(u => u.sub === teacher.sub) !== -1 row.push(wasThere ? '+' : '-') }) // Добавляем дату row.push(dayjs(lesson.date).format('DD.MM.YYYY')) // Добавляем полное название занятия (без сокращений) row.push(lesson.name) // Добавляем данные о присутствии студентов data.students.forEach(student => { const wasThere = lesson.students.findIndex(u => u.sub === student.sub) !== -1 row.push(wasThere ? '+' : '-') }) // Добавляем строку в таблицу tableContent.push(row.join('\t')) }) // Копируем в буфер обмена const finalContent = tableContent.join('\n') navigator.clipboard.writeText(finalContent) .then(() => { toast({ title: t('journal.pl.attendance.table.copySuccess'), description: t('journal.pl.attendance.table.copySuccessDescription'), status: 'success', duration: 3000, isClosable: true, }) }) .catch(err => { toast({ title: t('journal.pl.attendance.table.copyError'), description: t('journal.pl.attendance.table.copyErrorDescription'), status: 'error', duration: 3000, isClosable: true, }) console.error('Ошибка копирования', err) }) } // Расчет статистики посещаемости для каждого студента const getStudentAttendance = () => { const totalLessons = data.attendance.length return data.students.map(student => { let attendedCount = 0 data.attendance.forEach(lesson => { if (lesson.students.findIndex(s => s.sub === student.sub) !== -1) { attendedCount++ } }) return { student, name: student.name || student.value || t('journal.pl.common.name'), email: student.email, picture: student.picture || getGravatarURL(student.email), attendedCount, totalLessons, attendance: totalLessons > 0 ? (attendedCount / totalLessons) * 100 : 0 } }) } if (!data.attendance?.length || !data.students?.length) { return {t('journal.pl.common.noData')} } // Создаем компонент таблицы для переиспользования const AttendanceTableContent = () => ( {data.teachers?.map(teacher => ( ))} {data.students.map((student) => ( ))} {data.attendance.map((lesson) => ( {data.teachers?.map((teacher) => { const wasThere = Boolean(lesson.teachers) && lesson.teachers.findIndex((u) => u.sub === teacher.sub) !== -1 return ( ) })} {data.students.map((st) => { const wasThere = lesson.students.findIndex((u) => u.sub === st.sub) !== -1 return ( ) })} ))}
{teacher.value}{t('journal.pl.common.date')} {t('journal.pl.common.lessonName')} {student.name || student.value || t('journal.pl.common.name')}
{wasThere ? ( ) : ( )} {dayjs(lesson.date).format('DD.MM.YYYY')} {wasThere ? ( ) : ( )}
) return ( {/* Краткая статистика по каждому студенту с эмоджи */} {getStudentAttendance().map(({ student, name, attendedCount, totalLessons, attendance, picture }) => { const emoji = getAttendanceEmoji(attendedCount, totalLessons) return ( {name} {attendedCount} {t('journal.pl.common.of')} {totalLessons} ({attendance.toFixed(0)}%) ) })} {/* Полная таблица с возможностью скрытия/показа */} {/* Модальное окно для отображения таблицы на весь экран */} {t('journal.pl.attendance.table.attendanceData')} ) }