169 lines
4.6 KiB
TypeScript
169 lines
4.6 KiB
TypeScript
import React, { useEffect, useState, useRef, useMemo } from 'react'
|
||
import { useParams, Link } from 'react-router-dom'
|
||
import dayjs from 'dayjs'
|
||
import QRCode from 'qrcode'
|
||
import { sha256 } from 'js-sha256'
|
||
import { getConfigValue, getNavigationsValue } from '@brojs/cli'
|
||
import {
|
||
Box,
|
||
Breadcrumb,
|
||
BreadcrumbItem,
|
||
BreadcrumbLink,
|
||
Container,
|
||
VStack,
|
||
Heading,
|
||
Stack,
|
||
} from '@chakra-ui/react'
|
||
|
||
import { api } from '../__data__/api/api'
|
||
import { User } from '../__data__/model'
|
||
import { UserCard } from '../components/user-card'
|
||
|
||
import {
|
||
QRCanvas,
|
||
StudentList,
|
||
BreadcrumbsWrapper,
|
||
} from './style'
|
||
import { useAppSelector } from '../__data__/store'
|
||
import { isTeacher } from '../utils/user'
|
||
|
||
export function getGravatarURL(email, user) {
|
||
if (!email) return void 0
|
||
const address = String(email).trim().toLowerCase()
|
||
const hash = sha256(address)
|
||
|
||
// Grab the actual image URL
|
||
return `https://www.gravatar.com/avatar/${hash}?d=robohash`
|
||
}
|
||
|
||
const LessonDetail = () => {
|
||
const { lessonId, courseId } = useParams()
|
||
const canvRef = useRef(null)
|
||
const user = useAppSelector((s) => s.user)
|
||
|
||
const {
|
||
isFetching,
|
||
data: accessCode,
|
||
isSuccess,
|
||
refetch,
|
||
} = api.useCreateAccessCodeQuery(
|
||
{ lessonId },
|
||
{
|
||
skip: !isTeacher(user),
|
||
pollingInterval:
|
||
Number(getConfigValue('journal.polling-interval')) || 3000,
|
||
skipPollingIfUnfocused: true,
|
||
},
|
||
)
|
||
const AllStudents = api.useCourseAllStudentsQuery(courseId)
|
||
const [manualAdd, manualAddRqst] = api.useManualAddStudentMutation()
|
||
const userUrl = useMemo(
|
||
() => `${location.origin}/journal/u/${lessonId}/${accessCode?.body?._id}`,
|
||
[accessCode, lessonId],
|
||
)
|
||
|
||
useEffect(() => {
|
||
if (manualAddRqst.isSuccess) {
|
||
refetch()
|
||
}
|
||
}, [manualAddRqst.isSuccess])
|
||
|
||
useEffect(() => {
|
||
if (!isFetching && isSuccess) {
|
||
QRCode.toCanvas(
|
||
canvRef.current,
|
||
userUrl,
|
||
{ width: 600 },
|
||
function (error) {
|
||
if (error) console.error(error)
|
||
console.log('success!')
|
||
},
|
||
)
|
||
}
|
||
}, [isFetching, isSuccess])
|
||
|
||
const studentsArr = useMemo(() => {
|
||
let allStudents: (User & { present?: boolean })[] = [
|
||
...(AllStudents.data?.body || []),
|
||
].map((st) => ({ ...st, present: false }))
|
||
let presentStudents: (User & { present?: boolean })[] = [
|
||
...(accessCode?.body.lesson.students || []),
|
||
]
|
||
|
||
while (allStudents.length && presentStudents.length) {
|
||
const student = presentStudents.pop()
|
||
|
||
const present = allStudents.find((st) => st.sub === student.sub)
|
||
|
||
if (present) {
|
||
present.present = true
|
||
} else {
|
||
allStudents.push({ ...student, present: true })
|
||
}
|
||
}
|
||
|
||
return allStudents.sort((a, b) => (a.present ? -1 : 1))
|
||
}, [accessCode?.body, AllStudents.data])
|
||
|
||
return (
|
||
<>
|
||
<BreadcrumbsWrapper>
|
||
<Breadcrumb>
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink as={Link} to={getNavigationsValue('journal.main')}>
|
||
Журнал
|
||
</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
|
||
<BreadcrumbItem>
|
||
<BreadcrumbLink
|
||
as={Link}
|
||
to={`${getNavigationsValue('journal.main')}/lessons-list/${courseId}`}
|
||
>
|
||
Курс
|
||
</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
|
||
<BreadcrumbItem isCurrentPage>
|
||
<BreadcrumbLink href="#">Лекция</BreadcrumbLink>
|
||
</BreadcrumbItem>
|
||
</Breadcrumb>
|
||
</BreadcrumbsWrapper>
|
||
<Container maxW="2280px">
|
||
<VStack align="left">
|
||
<Heading as="h3" mt="4" mb="3">
|
||
Тема занятия:
|
||
</Heading>
|
||
<Box as="span">{accessCode?.body?.lesson?.name}</Box>
|
||
<Box as="span">
|
||
{dayjs(accessCode?.body?.lesson?.date).format('DD MMMM YYYYг.')}{' '}
|
||
Отмечено - {accessCode?.body?.lesson?.students?.length}{' '}
|
||
{AllStudents.isSuccess
|
||
? `/ ${AllStudents?.data?.body?.length}`
|
||
: ''}{' '}
|
||
человек
|
||
</Box>
|
||
</VStack>
|
||
<Stack spacing="8" sx={{ flexDirection: { sm: 'column', md: 'row' } }}>
|
||
<a href={userUrl}>
|
||
<QRCanvas ref={canvRef} />
|
||
</a>
|
||
<StudentList>
|
||
{isTeacher(user) && studentsArr.map((student) => (
|
||
<UserCard
|
||
wrapperAS="li"
|
||
key={student.sub}
|
||
student={student}
|
||
present={student.present}
|
||
onAddUser={(user: User) => manualAdd({ lessonId, user })}
|
||
/>
|
||
))}
|
||
</StudentList>
|
||
</Stack>
|
||
</Container>
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default LessonDetail
|