Добавлены новые зависимости: "react-select" и "@floating-ui/core". Реализована локализация с использованием i18next, добавлены переводы для английского и русского языков. Обновлены компоненты для поддержки локализации, включая AppHeader, Attendance, Dashboard и другие. Улучшена логика отображения данных и взаимодействия с пользователем.
This commit is contained in:
@@ -1,28 +1,33 @@
|
||||
import React from 'react'
|
||||
import { ResponsiveBar } from '@nivo/bar'
|
||||
import { type BarDatum, ResponsiveBar } from '@nivo/bar'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export const Bar = ({ data }) => (
|
||||
<ResponsiveBar
|
||||
data={data}
|
||||
keys={['count']}
|
||||
indexBy="lessonIndex"
|
||||
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
|
||||
padding={0.3}
|
||||
valueScale={{ type: 'linear' }}
|
||||
indexScale={{ type: 'band', round: true }}
|
||||
colors={{ scheme: 'set3' }}
|
||||
axisTop={null}
|
||||
axisRight={null}
|
||||
labelSkipWidth={12}
|
||||
labelSkipHeight={12}
|
||||
labelTextColor={{
|
||||
from: 'color',
|
||||
modifiers: [['brighter', 1.4]],
|
||||
}}
|
||||
role="application"
|
||||
ariaLabel="График посещаемости лекций"
|
||||
barAriaLabel={(e) =>
|
||||
e.id + ': ' + e.formattedValue + ' on lection: ' + e.indexValue
|
||||
}
|
||||
/>
|
||||
)
|
||||
export const Bar = ({ data }: { data: BarDatum[] }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ResponsiveBar
|
||||
data={data}
|
||||
keys={['count']}
|
||||
indexBy="lessonIndex"
|
||||
margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
|
||||
padding={0.3}
|
||||
valueScale={{ type: 'linear' }}
|
||||
indexScale={{ type: 'band', round: true }}
|
||||
colors={{ scheme: 'set3' }}
|
||||
axisTop={null}
|
||||
axisRight={null}
|
||||
labelSkipWidth={12}
|
||||
labelSkipHeight={12}
|
||||
labelTextColor={{
|
||||
from: 'color',
|
||||
modifiers: [['brighter', 1.4]],
|
||||
}}
|
||||
role="application"
|
||||
ariaLabel={t('journal.pl.lesson.attendanceChart')}
|
||||
barAriaLabel={(e) =>
|
||||
e.id + ': ' + e.formattedValue + ' on lection: ' + e.indexValue
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
useToast,
|
||||
} from '@chakra-ui/react'
|
||||
import { EditIcon } from '@chakra-ui/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { qrCode } from '../../../assets'
|
||||
|
||||
@@ -46,10 +47,11 @@ export const Item: React.FC<ItemProps> = ({
|
||||
const toast = useToast()
|
||||
const [updateLesson, updateLessonRqst] = api.useUpdateLessonMutation()
|
||||
const createdLessonRef = useRef(null)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const onSubmit = (lessonData) => {
|
||||
toastRef.current = toast({
|
||||
title: 'Отправляем',
|
||||
title: t('journal.pl.common.sending'),
|
||||
status: 'loading',
|
||||
duration: 9000,
|
||||
})
|
||||
@@ -58,7 +60,7 @@ export const Item: React.FC<ItemProps> = ({
|
||||
updateLesson(lessonData)
|
||||
} else {
|
||||
toast.update(toastRef.current, {
|
||||
title: 'Отсутствует интернет',
|
||||
title: t('journal.pl.lesson.noInternet'),
|
||||
status: 'error',
|
||||
duration: 3000
|
||||
})
|
||||
@@ -68,8 +70,8 @@ export const Item: React.FC<ItemProps> = ({
|
||||
useEffect(() => {
|
||||
if (updateLessonRqst.isSuccess) {
|
||||
const toastProps = {
|
||||
title: 'Лекция Обновлена',
|
||||
description: `Лекция ${createdLessonRef.current?.name} успешно обновлена`,
|
||||
title: t('journal.pl.lesson.updated'),
|
||||
description: t('journal.pl.lesson.updateMessage', { name: createdLessonRef.current?.name }),
|
||||
status: 'success' as const,
|
||||
duration: 9000,
|
||||
isClosable: true,
|
||||
@@ -92,8 +94,8 @@ export const Item: React.FC<ItemProps> = ({
|
||||
setEdit(false)
|
||||
}}
|
||||
lesson={{ _id: id, id, name, date }}
|
||||
title={'Редактирование лекции'}
|
||||
nameButton={'Сохранить'}
|
||||
title={t('journal.pl.lesson.editTitle')}
|
||||
nameButton={t('journal.pl.save')}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
@@ -129,13 +131,13 @@ export const Item: React.FC<ItemProps> = ({
|
||||
setEdit(true)
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
{t('journal.pl.edit')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={setlessonToDelete}>Delete</MenuItem>
|
||||
<MenuItem onClick={setlessonToDelete}>{t('journal.pl.delete')}</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)}
|
||||
{edit && <Button onClick={setlessonToDelete}>Сохранить</Button>}
|
||||
{edit && <Button onClick={setlessonToDelete}>{t('journal.pl.save')}</Button>}
|
||||
</Td>
|
||||
)}
|
||||
<Td isNumeric>{students.length}</Td>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
Input,
|
||||
} from '@chakra-ui/react'
|
||||
import { AddIcon } from '@chakra-ui/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { dateToCalendarFormat } from '../../../utils/time'
|
||||
import { Lesson } from '../../../__data__/model'
|
||||
@@ -45,6 +46,8 @@ export const LessonForm = ({
|
||||
title,
|
||||
nameButton,
|
||||
}: LessonFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const getNearestTimeSlot = () => {
|
||||
const now = new Date();
|
||||
const minutes = now.getMinutes();
|
||||
@@ -96,21 +99,21 @@ export const LessonForm = ({
|
||||
<Controller
|
||||
control={control}
|
||||
name="date"
|
||||
rules={{ required: 'Обязательное поле' }}
|
||||
rules={{ required: t('journal.pl.common.required') }}
|
||||
render={({ field }) => (
|
||||
<FormControl>
|
||||
<FormLabel>Дата</FormLabel>
|
||||
<FormLabel>{t('journal.pl.lesson.form.date')}</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
required={false}
|
||||
placeholder="Укажите дату лекции"
|
||||
placeholder={t('journal.pl.lesson.form.datePlaceholder')}
|
||||
size="md"
|
||||
type="datetime-local"
|
||||
/>
|
||||
{errors.date ? (
|
||||
<FormErrorMessage>{errors.date?.message}</FormErrorMessage>
|
||||
) : (
|
||||
<FormHelperText>Укажите дату и время лекции</FormHelperText>
|
||||
<FormHelperText>{t('journal.pl.lesson.form.dateTime')}</FormHelperText>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
@@ -119,14 +122,14 @@ export const LessonForm = ({
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
rules={{ required: 'Обязательное поле' }}
|
||||
rules={{ required: t('journal.pl.common.required') }}
|
||||
render={({ field }) => (
|
||||
<FormControl isRequired isInvalid={Boolean(errors.name)}>
|
||||
<FormLabel>Название новой лекции:</FormLabel>
|
||||
<FormLabel>{t('journal.pl.lesson.form.title')}</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
required={false}
|
||||
placeholder="Название лекции"
|
||||
placeholder={t('journal.pl.lesson.form.namePlaceholder')}
|
||||
size="md"
|
||||
/>
|
||||
{errors.name && (
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
AlertDialogOverlay,
|
||||
} from '@chakra-ui/react'
|
||||
import { AddIcon } from '@chakra-ui/icons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useAppSelector } from '../../__data__/store'
|
||||
import { api } from '../../__data__/api/api'
|
||||
@@ -57,6 +58,7 @@ const LessonList = () => {
|
||||
const toastRef = useRef(null)
|
||||
const createdLessonRef = useRef(null)
|
||||
const [editLesson, setEditLesson] = useState<Lesson>(null)
|
||||
const { t } = useTranslation()
|
||||
const sorted = useMemo(
|
||||
() => [...(data?.body || [])]?.sort((a, b) => (a.date > b.date ? 1 : -1)),
|
||||
[data, data?.body],
|
||||
@@ -93,7 +95,7 @@ const LessonList = () => {
|
||||
|
||||
const onSubmit = (lessonData) => {
|
||||
toastRef.current = toast({
|
||||
title: 'Отправляем',
|
||||
title: t('journal.pl.common.sending'),
|
||||
status: 'loading',
|
||||
duration: 9000,
|
||||
})
|
||||
@@ -123,7 +125,7 @@ const LessonList = () => {
|
||||
title={
|
||||
<>
|
||||
<Box pb={3}>
|
||||
<Text fontSize="xl">{`Удалена лекция ${lesson.name}`}</Text>
|
||||
<Text fontSize="xl">{t('journal.pl.lesson.deletedMessage', { name: lesson.name })}</Text>
|
||||
</Box>
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -131,7 +133,7 @@ const LessonList = () => {
|
||||
toast.close(id)
|
||||
}}
|
||||
>
|
||||
Восстановить
|
||||
{t('journal.pl.common.restored')}
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
@@ -145,8 +147,8 @@ const LessonList = () => {
|
||||
useEffect(() => {
|
||||
if (crLQuery.isSuccess) {
|
||||
const toastProps = {
|
||||
title: 'Лекция создана',
|
||||
description: `Лекция ${createdLessonRef.current?.name} успешно создана`,
|
||||
title: t('journal.pl.lesson.created'),
|
||||
description: t('journal.pl.lesson.successMessage', { name: createdLessonRef.current?.name }),
|
||||
status: 'success' as const,
|
||||
duration: 9000,
|
||||
isClosable: true,
|
||||
@@ -160,8 +162,8 @@ const LessonList = () => {
|
||||
useEffect(() => {
|
||||
if (updateLessonRqst.isSuccess) {
|
||||
const toastProps = {
|
||||
title: 'Лекция Обновлена',
|
||||
description: `Лекция ${createdLessonRef.current?.name} успешно обновлена`,
|
||||
title: t('journal.pl.lesson.updated'),
|
||||
description: t('journal.pl.lesson.updateMessage', { name: createdLessonRef.current?.name }),
|
||||
status: 'success' as const,
|
||||
duration: 9000,
|
||||
isClosable: true,
|
||||
@@ -186,12 +188,11 @@ const LessonList = () => {
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
Удалить занятие от{' '}
|
||||
{dayjs(lessonToDelete?.date).format('DD.MM.YY')}?
|
||||
{t('journal.pl.lesson.deleteConfirm', { date: dayjs(lessonToDelete?.date).format('DD.MM.YY') })}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>
|
||||
Все данные о посещении данного занятия будут удалены
|
||||
{t('journal.pl.lesson.deleteWarning')}
|
||||
</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
@@ -200,7 +201,7 @@ const LessonList = () => {
|
||||
ref={cancelRef}
|
||||
onClick={() => setlessonToDelete(null)}
|
||||
>
|
||||
Cancel
|
||||
{t('journal.pl.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="red"
|
||||
@@ -209,7 +210,7 @@ const LessonList = () => {
|
||||
onClick={() => deleteLesson(lessonToDelete.id)}
|
||||
ml={3}
|
||||
>
|
||||
Delete
|
||||
{t('journal.pl.delete')}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
@@ -219,12 +220,12 @@ const LessonList = () => {
|
||||
<Breadcrumb>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink as={Link} to={getNavigationsValue('journal.main')}>
|
||||
Журнал
|
||||
{t('journal.pl.common.journal')}
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
|
||||
<BreadcrumbItem isCurrentPage>
|
||||
<BreadcrumbLink href="#">Курс</BreadcrumbLink>
|
||||
<BreadcrumbLink href="#">{t('journal.pl.common.course')}</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
</BreadcrumbsWrapper>
|
||||
@@ -242,8 +243,8 @@ const LessonList = () => {
|
||||
}}
|
||||
error={(crLQuery.error as any)?.error}
|
||||
lesson={editLesson}
|
||||
title={editLesson ? 'Редактирование лекции' : 'Создание лекции'}
|
||||
nameButton={editLesson ? 'Редактировать' : 'Создать'}
|
||||
title={editLesson ? t('journal.pl.lesson.editTitle') : t('journal.pl.lesson.createTitle')}
|
||||
nameButton={editLesson ? t('journal.pl.edit') : t('journal.pl.common.create')}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
@@ -251,7 +252,7 @@ const LessonList = () => {
|
||||
colorScheme="green"
|
||||
onClick={() => setShowForm(true)}
|
||||
>
|
||||
Добавить
|
||||
{t('journal.pl.common.create')}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
@@ -272,15 +273,15 @@ const LessonList = () => {
|
||||
<Tr>
|
||||
{isTeacher(user) && (
|
||||
<Th align="center" width={1}>
|
||||
ссылка
|
||||
{t('journal.pl.lesson.link')}
|
||||
</Th>
|
||||
)}
|
||||
<Th textAlign="center" width={1}>
|
||||
{groupByDate ? 'Время' : 'Дата'}
|
||||
{groupByDate ? t('journal.pl.lesson.time') : t('journal.pl.common.date')}
|
||||
</Th>
|
||||
<Th width="100%">Название</Th>
|
||||
{isTeacher(user) && <Th>action</Th>}
|
||||
<Th isNumeric>Отмечено</Th>
|
||||
<Th width="100%">{t('journal.pl.common.name')}</Th>
|
||||
{isTeacher(user) && <Th>{t('journal.pl.lesson.action')}</Th>}
|
||||
<Th isNumeric>{t('journal.pl.common.marked')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
|
||||
Reference in New Issue
Block a user