144 lines
4.9 KiB
TypeScript
144 lines
4.9 KiB
TypeScript
import React, { useState, useMemo, useEffect } from 'react'
|
|
import {
|
|
Box,
|
|
Button,
|
|
Container,
|
|
Text,
|
|
useColorMode,
|
|
useBreakpointValue
|
|
} from '@chakra-ui/react'
|
|
import { AddIcon } from '@chakra-ui/icons'
|
|
import { useTranslation } from 'react-i18next'
|
|
import { getFeatures } from '@brojs/cli'
|
|
|
|
import { useAppSelector } from '../../__data__/store'
|
|
import { api } from '../../__data__/api/api'
|
|
import { isTeacher } from '../../utils/user'
|
|
import { PageLoader } from '../../components/page-loader/page-loader'
|
|
import { useSetBreadcrumbs } from '../../components'
|
|
import { useGroupedCourses } from './hooks'
|
|
import { CreateCourseForm, YearGroup, CoursesOverview } from './components'
|
|
import { Lesson } from '../../__data__/model'
|
|
|
|
/**
|
|
* Основной компонент списка курсов
|
|
*/
|
|
export const CoursesList = () => {
|
|
const user = useAppSelector((s) => s.user)
|
|
const { data, isLoading } = api.useCoursesListQuery()
|
|
const [showForm, setShowForm] = useState(false)
|
|
const { t } = useTranslation()
|
|
const { colorMode } = useColorMode()
|
|
|
|
// Устанавливаем хлебные крошки для главной страницы
|
|
useSetBreadcrumbs([
|
|
{
|
|
title: t('journal.pl.breadcrumbs.home'),
|
|
path: '/',
|
|
isCurrentPage: true
|
|
}
|
|
])
|
|
|
|
// Получаем значения фичей
|
|
const features = getFeatures('journal')
|
|
const coursesStatistics = features?.['courses.statistics']
|
|
|
|
// Создаем API запросы для получения уроков
|
|
const [getLessons] = api.useLazyLessonListQuery()
|
|
|
|
const buttonSize = useBreakpointValue({ base: 'md', md: 'lg' })
|
|
const containerPadding = useBreakpointValue({ base: '2', md: '4' })
|
|
|
|
// Используем хук для группировки курсов по годам
|
|
const groupedCourses = useGroupedCourses(data?.body)
|
|
|
|
// Создаем объект с детализированными данными для всех курсов
|
|
const [lessonsByCourse, setLessonsByCourse] = useState<Record<string, Lesson[]>>({})
|
|
|
|
// Используем useMemo для проверки наличия данных
|
|
const courses = useMemo(() => data?.body || [], [data])
|
|
|
|
// Загружаем данные для каждого курса параллельно
|
|
useEffect(() => {
|
|
if (courses.length > 0 && !showForm) {
|
|
// Создаем запросы для получения данных о занятиях каждого курса
|
|
const fetchLessonsForCourses = async () => {
|
|
const lessonsData: Record<string, Lesson[]> = {}
|
|
|
|
// Получаем данные курсов параллельно (по 3 курса за раз, чтобы не перегружать сервер)
|
|
for (let i = 0; i < courses.length; i += 3) {
|
|
const batch = courses.slice(i, i + 3)
|
|
const batchPromises = batch.map(async course => {
|
|
// Используем существующий API метод с Lazy Query
|
|
const response = await getLessons(course.id)
|
|
if (response.data?.body) {
|
|
lessonsData[course._id] = response.data.body
|
|
}
|
|
})
|
|
|
|
await Promise.all(batchPromises)
|
|
}
|
|
|
|
setLessonsByCourse(lessonsData)
|
|
}
|
|
|
|
fetchLessonsForCourses()
|
|
}
|
|
}, [courses, showForm, getLessons])
|
|
|
|
if (isLoading) {
|
|
return <PageLoader />
|
|
}
|
|
|
|
const handleCloseForm = () => setShowForm(false)
|
|
|
|
return (
|
|
<Container maxW="container.xl" px={containerPadding}>
|
|
{isTeacher(user) && (
|
|
<Box mt={{ base: 3, md: 5 }} mb={{ base: 3, md: 5 }}>
|
|
{showForm ? (
|
|
<CreateCourseForm onClose={handleCloseForm} />
|
|
) : (
|
|
<Box p={{ base: 1, md: 2 }} m={{ base: 1, md: 2 }}>
|
|
<Button
|
|
leftIcon={<AddIcon />}
|
|
colorScheme="green"
|
|
onClick={() => setShowForm(true)}
|
|
size={buttonSize}
|
|
width={{ base: '100%', sm: 'auto' }}
|
|
>
|
|
{t('journal.pl.common.add')}
|
|
</Button>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
)}
|
|
|
|
{!showForm && coursesStatistics && (
|
|
<CoursesOverview
|
|
courses={courses}
|
|
isLoading={isLoading}
|
|
lessonsByCourse={lessonsByCourse}
|
|
/>
|
|
)}
|
|
|
|
{Object.keys(groupedCourses).length > 0 ? (
|
|
Object.entries(groupedCourses)
|
|
.sort(([yearA], [yearB]) => Number(yearB) - Number(yearA)) // Сортируем годы по убыванию
|
|
.map(([year, courses]) => (
|
|
<YearGroup
|
|
key={year}
|
|
year={year}
|
|
courses={courses}
|
|
colorMode={colorMode}
|
|
/>
|
|
))
|
|
) : (
|
|
<Box textAlign="center" py={10}>
|
|
<Text color="gray.500">{t('journal.pl.course.noCourses')}</Text>
|
|
</Box>
|
|
)}
|
|
</Container>
|
|
)
|
|
}
|