Добавлены новые переводы для полноэкранного режима таблицы посещаемости в файлы локализации (en.json и ru.json). Обновлен компонент AttendanceTable: реализована возможность отображения таблицы в полноэкранном режиме с соответствующими кнопками и модальным окном.

This commit is contained in:
Primakov Alexandr Alexandrovich 2025-03-23 12:09:56 +03:00
parent 5997723166
commit 2a5d7efcbb
3 changed files with 126 additions and 77 deletions

View File

@ -100,6 +100,9 @@
"journal.pl.attendance.table.copy": "Copy Table",
"journal.pl.attendance.table.show": "Show Table",
"journal.pl.attendance.table.hide": "Hide Table",
"journal.pl.attendance.table.fullscreen": "Fullscreen",
"journal.pl.attendance.table.exitFullscreen": "Exit Fullscreen",
"journal.pl.attendance.table.attendanceData": "Attendance Data",
"journal.pl.attendance.table.copySuccess": "Table copied",
"journal.pl.attendance.table.copySuccessDescription": "Table data successfully copied to clipboard",
"journal.pl.attendance.table.copyError": "Copy error",

View File

@ -95,6 +95,9 @@
"journal.pl.attendance.table.copy": "Копировать таблицу",
"journal.pl.attendance.table.show": "Показать таблицу",
"journal.pl.attendance.table.hide": "Скрыть таблицу",
"journal.pl.attendance.table.fullscreen": "На весь экран",
"journal.pl.attendance.table.exitFullscreen": "Выйти из полноэкранного режима",
"journal.pl.attendance.table.attendanceData": "Данные о посещаемости",
"journal.pl.attendance.table.copySuccess": "Таблица скопирована",
"journal.pl.attendance.table.copySuccessDescription": "Данные таблицы успешно скопированы в буфер обмена",
"journal.pl.attendance.table.copyError": "Ошибка копирования",

View File

@ -17,10 +17,17 @@ import {
Icon,
Tooltip,
Avatar,
AvatarBadge
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 } from 'react-icons/fa'
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'
@ -36,6 +43,7 @@ export const AttendanceTable: React.FC<AttendanceTableProps> = ({ data }) => {
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'
@ -178,6 +186,76 @@ export const AttendanceTable: React.FC<AttendanceTableProps> = ({ data }) => {
return <Box>{t('journal.pl.common.noData')}</Box>
}
// Создаем компонент таблицы для переиспользования
const AttendanceTableContent = () => (
<Table variant="simple" size="sm">
<Thead>
<Tr>
{data.teachers?.map(teacher => (
<Th key={teacher.id}>{teacher.value}</Th>
))}
<Th>{t('journal.pl.common.date')}</Th>
<Th>{t('journal.pl.common.lessonName')}</Th>
{data.students.map((student) => (
<Th key={student.sub}>
<HStack>
<Avatar
size="xs"
src={student.picture || getGravatarURL(student.email)}
name={student.name || student.value || t('journal.pl.common.name')}
/>
<Text>{student.name || student.value || t('journal.pl.common.name')}</Text>
</HStack>
</Th>
))}
</Tr>
</Thead>
<Tbody>
{data.attendance.map((lesson) => (
<Tr key={lesson.name}>
{data.teachers?.map((teacher) => {
const wasThere = Boolean(lesson.teachers) &&
lesson.teachers.findIndex((u) => u.sub === teacher.sub) !== -1
return (
<Td
key={teacher.sub}
textAlign="center"
bg={wasThere ? getPresentColor() : getAbsentColor()}
>
{wasThere ? (
<Icon as={FaSmile} color="green.500" />
) : (
<Icon as={FaFrown} color="red.500" />
)}
</Td>
)
})}
<Td>{dayjs(lesson.date).format('DD.MM.YYYY')}</Td>
<Td><ShortText text={lesson.name} /></Td>
{data.students.map((st) => {
const wasThere =
lesson.students.findIndex((u) => u.sub === st.sub) !== -1
return (
<Td
key={st.sub}
textAlign="center"
bg={wasThere ? getPresentColor() : getAbsentColor()}
>
{wasThere ? (
<Icon as={FaSmile} color="green.500" />
) : (
<Icon as={FaFrown} color="red.500" />
)}
</Td>
)
})}
</Tr>
))}
</Tbody>
</Table>
)
return (
<Box
boxShadow="md"
@ -185,15 +263,27 @@ export const AttendanceTable: React.FC<AttendanceTableProps> = ({ data }) => {
bg={colorMode === 'dark' ? 'gray.700' : 'white'}
>
<Flex justifyContent="space-between" p={3} alignItems="center">
<Button
leftIcon={<CopyIcon />}
size="sm"
colorScheme="blue"
onClick={copyTableData}
mr={2}
>
{t('journal.pl.attendance.table.copy')}
</Button>
<Flex>
<Button
leftIcon={<CopyIcon />}
size="sm"
colorScheme="blue"
onClick={copyTableData}
mr={2}
>
{t('journal.pl.attendance.table.copy')}
</Button>
<Button
leftIcon={<Icon as={FaExpand} />}
size="sm"
colorScheme="teal"
onClick={onOpen}
mr={2}
>
{t('journal.pl.attendance.table.fullscreen')}
</Button>
</Flex>
<Button
rightIcon={showTable ? <ChevronUpIcon /> : <ChevronDownIcon />}
@ -256,74 +346,27 @@ export const AttendanceTable: React.FC<AttendanceTableProps> = ({ data }) => {
borderTop="1px"
borderColor={colorMode === 'dark' ? 'gray.600' : 'gray.200'}
>
<Table variant="simple" size="sm">
<Thead>
<Tr>
{data.teachers?.map(teacher => (
<Th key={teacher.id}>{teacher.value}</Th>
))}
<Th>{t('journal.pl.common.date')}</Th>
<Th>{t('journal.pl.common.lessonName')}</Th>
{data.students.map((student) => (
<Th key={student.sub}>
<HStack>
<Avatar
size="xs"
src={student.picture || getGravatarURL(student.email)}
name={student.name || student.value || t('journal.pl.common.name')}
/>
<Text>{student.name || student.value || t('journal.pl.common.name')}</Text>
</HStack>
</Th>
))}
</Tr>
</Thead>
<Tbody>
{data.attendance.map((lesson) => (
<Tr key={lesson.name}>
{data.teachers?.map((teacher) => {
const wasThere = Boolean(lesson.teachers) &&
lesson.teachers.findIndex((u) => u.sub === teacher.sub) !== -1
return (
<Td
key={teacher.sub}
textAlign="center"
bg={wasThere ? getPresentColor() : getAbsentColor()}
>
{wasThere ? (
<Icon as={FaSmile} color="green.500" />
) : (
<Icon as={FaFrown} color="red.500" />
)}
</Td>
)
})}
<Td>{dayjs(lesson.date).format('DD.MM.YYYY')}</Td>
<Td><ShortText text={lesson.name} /></Td>
{data.students.map((st) => {
const wasThere =
lesson.students.findIndex((u) => u.sub === st.sub) !== -1
return (
<Td
key={st.sub}
textAlign="center"
bg={wasThere ? getPresentColor() : getAbsentColor()}
>
{wasThere ? (
<Icon as={FaSmile} color="green.500" />
) : (
<Icon as={FaFrown} color="red.500" />
)}
</Td>
)
})}
</Tr>
))}
</Tbody>
</Table>
<AttendanceTableContent />
</Box>
</Collapse>
{/* Модальное окно для отображения таблицы на весь экран */}
<Modal isOpen={isOpen} onClose={onClose} size="full">
<ModalOverlay />
<ModalContent>
<ModalHeader>
<Flex justifyContent="space-between" alignItems="center">
{t('journal.pl.attendance.table.attendanceData')}
</Flex>
</ModalHeader>
<ModalCloseButton size="lg" top="16px" />
<ModalBody pb={6}>
<Box overflowX="auto">
<AttendanceTableContent />
</Box>
</ModalBody>
</ModalContent>
</Modal>
</Box>
)
}