5 Commits

Author SHA1 Message Date
Primakov Alexandr Alexandrovich
0034704af6 3.4.0 2024-10-30 14:28:55 +03:00
Primakov Alexandr Alexandrovich
3dfd854a4c inline edit mode 2024-10-30 14:28:42 +03:00
Primakov Alexandr Alexandrovich
6b903b4d54 3.3.1 2024-10-22 17:52:44 +03:00
Primakov Alexandr Alexandrovich
c3de9692d8 fix unmount stuff 2024-10-22 16:56:19 +03:00
Primakov Alexandr Alexandrovich
b2898ef4b3 try open exam as spa 2024-10-22 16:39:32 +03:00
8 changed files with 338 additions and 188 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "journal.pl",
"version": "3.3.0",
"version": "3.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "journal.pl",
"version": "3.3.0",
"version": "3.4.0",
"license": "MIT",
"dependencies": {
"@brojs/cli": "^0.0.4-beta.0",

View File

@@ -1,6 +1,6 @@
{
"name": "journal.pl",
"version": "3.3.0",
"version": "3.4.0",
"description": "bro-js platform journal ui repo",
"main": "./src/index.tsx",
"scripts": {

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/display-name */
import React from 'react';
import ReactDOM from 'react-dom/client';
@@ -24,7 +25,7 @@ export const mount = async (Сomponent, element = document.getElementById('app')
}
const store = createStore({ user });
const rootElement = ReactDOM.createRoot(element);
rootElement = ReactDOM.createRoot(element);
rootElement.render(<Сomponent store={store} />);
if(module.hot) {

View File

@@ -1,7 +1,7 @@
import React from 'react'
import dayjs from 'dayjs'
import { Link as ConnectedLink } from 'react-router-dom'
import { getNavigationsValue } from '@brojs/cli'
import { getNavigationsValue, getHistory } from '@brojs/cli'
import {
Stack,
Heading,
@@ -21,6 +21,8 @@ type CourseDetailsProps = {
populatedCourse: PopulatedCourse;
}
const history = getHistory()
export const CourseDetails = ({ populatedCourse }: CourseDetailsProps) => {
const user = useAppSelector((s) => s.user)
const exam = populatedCourse.examWithJury
@@ -35,6 +37,10 @@ export const CourseDetails = ({ populatedCourse }: CourseDetailsProps) => {
as={'a'}
colorScheme="blue"
href={getNavigationsValue('exam.main') + getNavigationsValue('link.exam.details').replace(':courseId', populatedCourse.id).replace(':examId', exam.id)}
onClick={event => {
event.preventDefault();
history.push(getNavigationsValue('exam.main') + getNavigationsValue('link.exam.details').replace(':courseId', populatedCourse.id).replace(':examId', exam.id))
}}
>
Открыть
</Button>

View File

@@ -0,0 +1,144 @@
import React, { useEffect, useRef, useState } from 'react'
import dayjs from 'dayjs'
import { Link } from 'react-router-dom'
import { getNavigationsValue, getFeatures } from '@brojs/cli'
import {
Button,
Tr,
Td,
Menu,
MenuButton,
MenuItem,
MenuList,
useToast,
} from '@chakra-ui/react'
import { EditIcon } from '@chakra-ui/icons'
import { qrCode } from '../../../assets'
import { LessonForm } from './lessons-form'
import { api } from '../../../__data__/api/api'
const features = getFeatures('journal')
const groupByDate = features?.['group.by.date']
type ItemProps = {
id: string
date: string
name: string
isTeacher: boolean
courseId: string
setlessonToDelete(): void
students: unknown[]
}
export const Item: React.FC<ItemProps> = ({
id,
date,
name,
isTeacher,
courseId,
setlessonToDelete,
students,
}) => {
const [edit, setEdit] = useState(false)
const toastRef = useRef(null)
const toast = useToast()
const [updateLesson, updateLessonRqst] = api.useUpdateLessonMutation()
const createdLessonRef = useRef(null)
const onSubmit = (lessonData) => {
toastRef.current = toast({
title: 'Отправляем',
status: 'loading',
duration: 9000,
})
createdLessonRef.current = lessonData
if (navigator.onLine) {
updateLesson(lessonData)
} else {
toast.update(toastRef.current, {
title: 'Отсутствует интернет',
status: 'error',
duration: 3000
})
}
}
useEffect(() => {
if (updateLessonRqst.isSuccess) {
const toastProps = {
title: 'Лекция Обновлена',
description: `Лекция ${createdLessonRef.current?.name} успешно обновлена`,
status: 'success' as const,
duration: 9000,
isClosable: true,
}
if (toastRef.current) toast.update(toastRef.current, toastProps)
else toast(toastProps)
setEdit(false)
}
}, [updateLessonRqst.isSuccess])
if (edit && isTeacher) {
return (
<Tr>
<Td colSpan={5}>
<LessonForm
isLoading={updateLessonRqst.isLoading}
error={(updateLessonRqst.error as any)?.error}
onSubmit={onSubmit}
onCancel={() => {
setEdit(false)
}}
lesson={{ id, name, date }}
title={'Редактирование лекции'}
nameButton={'Сохранить'}
/>
</Td>
</Tr>
)
}
return (
<Tr>
{isTeacher && (
<Td>
<Link
to={`${getNavigationsValue('journal.main')}/lesson/${courseId}/${id}`}
style={{ display: 'flex' }}
>
<img width={24} src={qrCode} style={{ margin: '0 auto' }} />
</Link>
</Td>
)}
<Td textAlign="center">
{dayjs(date).format(groupByDate ? 'HH:mm' : 'HH:mm DD.MM.YY')}
</Td>
<Td>{name}</Td>
{isTeacher && (
<Td>
{!edit && (
<Menu>
<MenuButton as={Button}>
<EditIcon />
</MenuButton>
<MenuList>
<MenuItem
onClick={() => {
setEdit(true)
}}
>
Edit
</MenuItem>
<MenuItem onClick={setlessonToDelete}>Delete</MenuItem>
</MenuList>
</Menu>
)}
{edit && <Button onClick={setlessonToDelete}>Сохранить</Button>}
</Td>
)}
<Td isNumeric>{students.length}</Td>
</Tr>
)
}

View File

@@ -0,0 +1,45 @@
import React from 'react'
import dayjs from 'dayjs'
import {
Tr,
Td,
} from '@chakra-ui/react'
import { Lesson } from '../../../__data__/model'
import { Item } from './item'
type LessonItemProps = {
date: string
lessons: Lesson[]
isTeacher: boolean
courseId: string
setlessonToDelete(lesson: Lesson): void
}
export const LessonItems: React.FC<LessonItemProps> = ({
date,
lessons,
isTeacher,
courseId,
setlessonToDelete,
}) => (
<>
{date && (
<Tr>
<Td colSpan={isTeacher ? 5 : 3}>
{dayjs(date).format('DD MMMM YYYY')}
</Td>
</Tr>
)}
{lessons.map((lesson) => (
<Item
key={lesson.id}
{...lesson}
setlessonToDelete={() => setlessonToDelete(lesson)}
courseId={courseId}
isTeacher={isTeacher}
/>
))}
</>
)

View File

@@ -22,8 +22,8 @@ import { Lesson } from '../../../__data__/model'
import { ErrorSpan } from '../style'
interface NewLessonForm {
name: string;
date: string;
name: string
date: string
}
interface LessonFormProps {
@@ -51,7 +51,10 @@ export const LessonForm = ({
reset,
formState: { errors },
} = useForm<NewLessonForm>({
defaultValues: (lesson && { ...lesson, date: dateToCalendarFormat(lesson.date) }) || {
defaultValues: (lesson && {
...lesson,
date: dateToCalendarFormat(lesson.date),
}) || {
name: '',
date: dateToCalendarFormat(),
},

View File

@@ -1,9 +1,4 @@
import React, {
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import dayjs from 'dayjs'
import { Link, useParams } from 'react-router-dom'
import { getNavigationsValue, getFeatures } from '@brojs/cli'
@@ -22,12 +17,7 @@ import {
Tr,
Th,
Tbody,
Td,
Menu,
MenuButton,
MenuItem,
Text,
MenuList,
AlertDialog,
AlertDialogBody,
AlertDialogContent,
@@ -35,18 +25,18 @@ import {
AlertDialogHeader,
AlertDialogOverlay,
} from '@chakra-ui/react'
import { AddIcon, EditIcon } from '@chakra-ui/icons'
import { AddIcon } from '@chakra-ui/icons'
import { useAppSelector } from '../../__data__/store'
import { api } from '../../__data__/api/api'
import { isTeacher } from '../../utils/user'
import { qrCode } from '../../assets'
import { Lesson } from '../../__data__/model'
import { XlSpinner } from '../../components/xl-spinner'
import { LessonForm } from './components/lessons-form'
import { BreadcrumbsWrapper } from './style'
import { Bar } from './components/bar'
import { LessonItems } from './components/lesson-items'
import { BreadcrumbsWrapper } from './style'
const features = getFeatures('journal')
@@ -67,7 +57,10 @@ const LessonList = () => {
const toastRef = useRef(null)
const createdLessonRef = useRef(null)
const [editLesson, setEditLesson] = useState<Lesson>(null)
const sorted = useMemo(() => [...(data?.body || [])]?.sort((a, b) => a.date > b.date ? 1 : -1), [data, data?.body])
const sorted = useMemo(
() => [...(data?.body || [])]?.sort((a, b) => (a.date > b.date ? 1 : -1)),
[data, data?.body],
)
const lessonCalc = useMemo(() => {
if (!isSuccess) {
@@ -95,7 +88,7 @@ const LessonList = () => {
}
}
return lessonsData.sort((a, b) => a.date < b.date? 1 : -1)
return lessonsData.sort((a, b) => (a.date < b.date ? 1 : -1))
}, [groupByDate, isSuccess, sorted])
const onSubmit = (lessonData) => {
@@ -153,8 +146,8 @@ const LessonList = () => {
if (crLQuery.isSuccess) {
const toastProps = {
title: 'Лекция создана',
description: `Лекция ${createdLessonRef.current.name} успешно создана`,
status: 'success' as 'success',
description: `Лекция ${createdLessonRef.current?.name} успешно создана`,
status: 'success' as const,
duration: 9000,
isClosable: true,
}
@@ -168,8 +161,8 @@ const LessonList = () => {
if (updateLessonRqst.isSuccess) {
const toastProps = {
title: 'Лекция Обновлена',
description: `Лекция ${createdLessonRef.current.name} успешно обновлена`,
status: 'success' as 'success',
description: `Лекция ${createdLessonRef.current?.name} успешно обновлена`,
status: 'success' as const,
duration: 9000,
isClosable: true,
}
@@ -180,7 +173,7 @@ const LessonList = () => {
}, [updateLessonRqst.isSuccess])
if (isLoading) {
return <XlSpinner />;
return <XlSpinner />
}
return (
@@ -213,7 +206,7 @@ const LessonList = () => {
colorScheme="red"
loadingText=""
isLoading={deletingRqst.isLoading}
onClick={() => deleteLesson(lessonToDelete._id)}
onClick={() => deleteLesson(lessonToDelete.id)}
ml={3}
>
Delete
@@ -240,7 +233,7 @@ const LessonList = () => {
<Box mt="15" mb="15">
{showForm ? (
<LessonForm
key={editLesson?._id}
key={editLesson?.id}
isLoading={crLQuery.isLoading}
onSubmit={onSubmit}
onCancel={() => {
@@ -265,7 +258,7 @@ const LessonList = () => {
)}
</Box>
)}
{barFeature && sorted?.length && (
{barFeature && sorted?.length > 1 && (
<Box height="300">
<Bar
data={sorted.map((lesson, index) => ({
@@ -285,7 +278,7 @@ const LessonList = () => {
</Th>
)}
<Th textAlign="center" width={1}>
Дата
{groupByDate ? 'Время' : 'Дата'}
</Th>
<Th width="100%">Название</Th>
{isTeacher(user) && <Th>action</Th>}
@@ -294,56 +287,14 @@ const LessonList = () => {
</Thead>
<Tbody>
{lessonCalc?.map(({ data: lessons, date }) => (
<React.Fragment key={date}>
{date && <Tr><Td colSpan={isTeacher(user) ? 5 : 3}>{dayjs(date).format('DD MMMM YYYY')}</Td></Tr>}
{lessons.map((lesson) => (
<Tr key={lesson._id}>
{isTeacher(user) && (
<Td>
<Link
to={`${getNavigationsValue('journal.main')}/lesson/${courseId}/${lesson._id}`}
style={{ display: 'flex' }}
>
<img
width={24}
src={qrCode}
style={{ margin: '0 auto' }}
<LessonItems
courseId={courseId}
date={date}
isTeacher={isTeacher(user)}
lessons={lessons}
setlessonToDelete={setlessonToDelete}
key={date}
/>
</Link>
</Td>
)}
<Td textAlign="center">
{dayjs(lesson.date).format(groupByDate ? 'HH:mm' : 'HH:mm DD.MM.YY')}
</Td>
<Td>{lesson.name}</Td>
{isTeacher(user) && (
<Td>
<Menu>
<MenuButton as={Button}>
<EditIcon />
</MenuButton>
<MenuList>
<MenuItem
onClick={() => {
setShowForm(true)
setEditLesson(lesson)
}}
>
Edit
</MenuItem>
<MenuItem
onClick={() => setlessonToDelete(lesson)}
>
Delete
</MenuItem>
</MenuList>
</Menu>
</Td>
)}
<Td isNumeric>{lesson.students.length}</Td>
</Tr>
))}
</React.Fragment>
))}
</Tbody>
</Table>