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>
|
||
)
|
||
}
|