From c68cea7fa6914fdef4bfc46c8d7d515736e619a3 Mon Sep 17 00:00:00 2001 From: primakov Date: Mon, 1 Apr 2024 17:42:30 +0300 Subject: [PATCH 1/2] =?UTF-8?q?(#16)=20Layout=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BB=D0=B5=D0=BA=D1=86=D0=B8=D0=B9=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=BA=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + src/pages/course-list.tsx | 12 +- src/pages/lesson-details.tsx | 79 +++++----- src/pages/lesson-list.tsx | 294 ++++++++++++++++++++--------------- src/pages/style.ts | 3 + 5 files changed, 227 insertions(+), 163 deletions(-) diff --git a/.gitignore b/.gitignore index 24f06e4..877c623 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ pids *.pid *.seed +*prom.json + # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/src/pages/course-list.tsx b/src/pages/course-list.tsx index d985fcb..cc738cb 100644 --- a/src/pages/course-list.tsx +++ b/src/pages/course-list.tsx @@ -35,6 +35,7 @@ import { useAppSelector } from '../__data__/store' import { api } from '../__data__/api/api' import { isTeacher } from '../utils/user' import { AddIcon, ArrowDownIcon, ArrowUpIcon, LinkIcon } from '@chakra-ui/icons' +import { Course } from '../__data__/model' interface NewCourseForm { startDt: string @@ -226,7 +227,15 @@ const CoursesList = () => { ) } -const CourseCard = ({ course, isOpened, openDetails }) => { +const CourseCard = ({ + course, + isOpened, + openDetails, +}: { + course: Course + isOpened: boolean + openDetails: () => void +}) => { const [getLessonList, lessonList] = api.useLazyLessonListQuery() useEffect(() => { if (isOpened) { @@ -262,6 +271,7 @@ const CourseCard = ({ course, isOpened, openDetails }) => { {lessonList.data?.body?.map((lesson) => ( { }, [accessCode?.body, AllStudents.data]) return ( - - + <> + @@ -108,41 +109,45 @@ const LessonDetail = () => { Лекция - - Тема занятия: - - - {accessCode?.body?.lesson?.name} - - - {dayjs(accessCode?.body?.lesson?.date).format('DD MMMM YYYYг.')}{' '} - Отмечено - {accessCode?.body?.lesson?.students?.length}{' '} - {AllStudents.isSuccess ? `/ ${AllStudents?.data?.body?.length}` : ''}{' '} - человек - - - - - - - - {studentsArr.map((student) => ( - - - {student.name || student.preferred_username}{' '} - {!student.present && ( - manualAdd({ lessonId, user: student })} - > - add - - )} - - - ))} - - - + + + + + Тема занятия: + + {accessCode?.body?.lesson?.name} + + {dayjs(accessCode?.body?.lesson?.date).format('DD MMMM YYYYг.')}{' '} + Отмечено - {accessCode?.body?.lesson?.students?.length}{' '} + {AllStudents.isSuccess + ? `/ ${AllStudents?.data?.body?.length}` + : ''}{' '} + человек + + + + + + + + {studentsArr.map((student) => ( + + + {student.name || student.preferred_username}{' '} + {!student.present && ( + manualAdd({ lessonId, user: student })} + > + add + + )} + + + ))} + + + + ) } diff --git a/src/pages/lesson-list.tsx b/src/pages/lesson-list.tsx index 26d9e3e..d1b99b6 100644 --- a/src/pages/lesson-list.tsx +++ b/src/pages/lesson-list.tsx @@ -24,16 +24,24 @@ import { FormHelperText, FormErrorMessage, Input, + TableContainer, + Table, + Thead, + Tr, + Th, + Tbody, + Td, } from '@chakra-ui/react' import { AddIcon } from '@chakra-ui/icons' -import { LessonItem, Lessonname, ErrorSpan } from './style' +import { LessonItem, Lessonname, ErrorSpan, BreadcrumbsWrapper } from './style' import { keycloak } from '../__data__/kc' import { useAppSelector } from '../__data__/store' import { api } from '../__data__/api/api' import { isTeacher } from '../utils/user' +import { qrCode } from '../assets' interface NewLessonForm { name: string @@ -94,135 +102,171 @@ const LessonList = () => { }, [crLQuery.isSuccess]) return ( - - - - - Журнал - - + <> + + + + + Журнал + + - - Курс - - + + Курс + + + + + {isTeacher(user) && ( + + {showForm ? ( + + + + Создание лекции + + setShowForm(false)} /> + + +
+ + ( + + Дата + + {errors.date ? ( + + {errors.date?.message} + + ) : ( + + Укажите дату и время лекции + + )} + + )} + /> - {isTeacher(user) && ( - - {showForm ? ( - - - - Создание лекции - - setShowForm(false)} /> - - - - - ( - - Дата - - {errors.date ? ( - - {errors.date?.message} - - ) : ( - - Укажите дату и время лекции - - )} - - )} - /> - - ( - ( + + Название новой лекции: + + {errors.name && ( + + {errors.name.message} + + )} + + )} + /> + + - - + Создать + + + - {crLQuery.error && ( - {(crLQuery.error as any).error} - )} -
-
-
- ) : ( - - + + )} +
+ )} + + + + + + + + + + + + {data?.body?.map((lesson) => ( + + + + + + + ))} + +
ссылкаДатаintoУчастников
+ + + + {dayjs(lesson.date).format('H:mm DD.MM.YY')}{lesson.name}{lesson.students.length}
+
+ {/*
    + {data?.body?.map((lesson) => ( + + - Добавить - - - )} - - )} -
      - {data?.body?.map((lesson) => ( - - - {lesson.name} - {dayjs(lesson.date).format('DD MMMM YYYYг.')} - - Участников - {lesson.students.length} - - - - ))} -
    - + {lesson.name} + {dayjs(lesson.date).format('DD MMMM YYYYг.')} + + Участников - {lesson.students.length} + + +
    + ))} +
*/} +
+ ) } diff --git a/src/pages/style.ts b/src/pages/style.ts index 4a0aa23..7f7355c 100644 --- a/src/pages/style.ts +++ b/src/pages/style.ts @@ -4,6 +4,9 @@ import { Card } from '@chakra-ui/react' +export const BreadcrumbsWrapper = styled.div` + padding: 12px; +`; export const MainWrapper = styled.main` display: flex; -- 2.45.2 From 032cdaaa1244b527495634b177e9853ea311b712 Mon Sep 17 00:00:00 2001 From: primakov Date: Mon, 1 Apr 2024 23:20:09 +0300 Subject: [PATCH 2/2] (#16) delete lesson --- Jenkinsfile | 1 - src/__data__/api/api.ts | 94 ++++++++++++------ src/pages/lesson-list.tsx | 202 +++++++++++++++++++++++++++++--------- 3 files changed, 216 insertions(+), 81 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 27caec4..c458a35 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,6 @@ pipeline { stages { stage('install') { steps { - sh 'ls -a' sh 'node -v' sh 'npm -v' sh 'npm ci' diff --git a/src/__data__/api/api.ts b/src/__data__/api/api.ts index 49ee857..acf4884 100644 --- a/src/__data__/api/api.ts +++ b/src/__data__/api/api.ts @@ -1,80 +1,112 @@ -import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; -import { getConfigValue } from "@ijl/cli"; +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import { getConfigValue } from '@ijl/cli' -import { keycloak } from "../kc"; -import { AccessCode, BaseResponse, Course, Lesson, User, UserData } from "../model"; +import { keycloak } from '../kc' +import { + AccessCode, + BaseResponse, + Course, + Lesson, + User, + UserData, +} from '../model' export const api = createApi({ - reducerPath: "auth", + reducerPath: 'auth', baseQuery: fetchBaseQuery({ - baseUrl: getConfigValue("journal.back.url"), - fetchFn: async (input: RequestInfo | URL, init?: RequestInit | undefined) => { - const response = await fetch(input, init); + baseUrl: getConfigValue('journal.back.url'), + fetchFn: async ( + input: RequestInfo | URL, + init?: RequestInit | undefined, + ) => { + const response = await fetch(input, init) if (response.status === 403) keycloak.login() - return response; + return response }, headers: { - "Content-Type": "application/json;charset=utf-8", + 'Content-Type': 'application/json;charset=utf-8', }, prepareHeaders: (headers) => { headers.set('Authorization', `Bearer ${keycloak.token}`) - } + }, }), tagTypes: ['LessonList', 'CourseList'], endpoints: (builder) => ({ coursesList: builder.query, void>({ query: () => '/course/list', - providesTags: ['CourseList'] + providesTags: ['CourseList'], }), - createUpdateCourse: builder.mutation, Partial & Pick>({ + createUpdateCourse: builder.mutation< + BaseResponse, + Partial & Pick + >({ query: (course) => ({ url: '/course', method: 'POST', body: course, }), - invalidatesTags: ['CourseList'] + invalidatesTags: ['CourseList'], }), courseAllStudents: builder.query, string>({ - query: (courseId) => `/course/students/${courseId}` + query: (courseId) => `/course/students/${courseId}`, }), - manualAddStudent: builder.mutation, { lessonId: string, user: User }>({ - query: ({lessonId, user}) => ({ + manualAddStudent: builder.mutation< + BaseResponse, + { lessonId: string; user: User } + >({ + query: ({ lessonId, user }) => ({ url: `/lesson/add-student/${lessonId}`, method: 'POST', - body: user - }) + body: user, + }), }), lessonList: builder.query, string>({ query: (courseId) => `/lesson/list/${courseId}`, - providesTags: ['LessonList'] + providesTags: ['LessonList'], }), - createLesson: builder.mutation, Pick & { courseId: string }>({ - query: ({ name, courseId, date }) => ({ + createLesson: builder.mutation< + BaseResponse, + Partial & Pick & { courseId: string } + >({ + query: (data) => ({ url: '/lesson', method: 'POST', - body: { name, courseId, date }, + body: data, }), - invalidatesTags: ['LessonList'] + invalidatesTags: ['LessonList'], + }), + deleteLesson: builder.mutation({ + query: (lessonId) => ({ + url: `/lesson/${lessonId}`, + method: 'DELETE', + }), + invalidatesTags: ['LessonList'], }), lessonById: builder.query, string>({ - query: (lessonId: string) => `/lesson/${lessonId}` + query: (lessonId: string) => `/lesson/${lessonId}`, }), - createAccessCode: builder.query, { lessonId: string }>({ + createAccessCode: builder.query< + BaseResponse, + { lessonId: string } + >({ query: ({ lessonId }) => ({ url: '/lesson/access-code', method: 'POST', body: { lessonId }, - }) + }), }), - getAccess: builder.query, { accessCode: string }>({ + getAccess: builder.query< + BaseResponse<{ user: UserData; accessCode: AccessCode }>, + { accessCode: string } + >({ query: ({ accessCode }) => ({ url: `/lesson/access-code/${accessCode}`, method: 'GET', - }) - }) + }), + }), }), -}); +}) diff --git a/src/pages/lesson-list.tsx b/src/pages/lesson-list.tsx index d1b99b6..a17e9cd 100644 --- a/src/pages/lesson-list.tsx +++ b/src/pages/lesson-list.tsx @@ -14,13 +14,12 @@ import { CardHeader, Heading, Button, - ButtonGroup, CloseButton, useToast, - Stack, VStack, FormControl, FormLabel, + Toast, FormHelperText, FormErrorMessage, Input, @@ -31,17 +30,29 @@ import { Th, Tbody, Td, + Menu, + MenuButton, + MenuItem, + Text, + MenuList, + Center, + Spinner, + AlertDialog, + AlertDialogBody, + AlertDialogContent, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, } from '@chakra-ui/react' +import { AddIcon, EditIcon } from '@chakra-ui/icons' -import { AddIcon } from '@chakra-ui/icons' +import { ErrorSpan, BreadcrumbsWrapper } from './style' -import { LessonItem, Lessonname, ErrorSpan, BreadcrumbsWrapper } from './style' - -import { keycloak } from '../__data__/kc' 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' interface NewLessonForm { name: string @@ -53,8 +64,10 @@ const LessonList = () => { const user = useAppSelector((s) => s.user) const { data, isLoading, error } = api.useLessonListQuery(courseId) const [createLesson, crLQuery] = api.useCreateLessonMutation() - const [value, setValue] = useState('') + const [deleteLesson, deletingRqst] = api.useDeleteLessonMutation() const [showForm, setShowForm] = useState(false) + const [lessonToDelete, setlessonToDelete] = useState(null) + const cancelRef = React.useRef() const { control, handleSubmit, @@ -70,12 +83,6 @@ const LessonList = () => { const toast = useToast() const toastRef = useRef(null) - const handleChange = useCallback( - (event) => { - setValue(event.target.value.toUpperCase()) - }, - [setValue], - ) const onSubmit = ({ name, date }) => { toastRef.current = toast({ title: 'Отправляем', @@ -85,6 +92,46 @@ const LessonList = () => { createLesson({ name, courseId, date }) } + useEffect(() => { + if (deletingRqst.isError) { + toast({ + title: (deletingRqst.error as any)?.error, + status: 'error', + duration: 3000, + }) + } + + if (deletingRqst.isSuccess) { + const lesson = { ...lessonToDelete } + toast({ + status: 'warning', + duration: 9000, + render(props) { + return ( + + + {`Удалена лекция ${lesson.name}`} + + + + } + /> + ) + }, + }) + setlessonToDelete(null) + } + }, [deletingRqst.isLoading, deletingRqst.isSuccess, deletingRqst.isError]) + useEffect(() => { if (crLQuery.isSuccess) { const values = getValues() @@ -101,8 +148,61 @@ const LessonList = () => { } }, [crLQuery.isSuccess]) + if (isLoading) { + return ( + +
+ +
+
+ ) + } + return ( <> + setlessonToDelete(null)} + > + + + + Удалить занятие от{' '} + {dayjs(lessonToDelete?.date).format('DD.MM.YY')}? + + + + Все данные о посещении данного занятия будут удалены + + + + + + + + + @@ -216,55 +316,59 @@ const LessonList = () => { - - - - + {isTeacher(user) && ( + + )} + + + + {data?.body?.map((lesson) => ( - + )} + - + ))}
ссылкаДатаintoУчастников + ссылка + + Дата + НазваниеactionОтмечено
- - - + {isTeacher(user) && ( + + + + + + {dayjs(lesson.date).format('H:mm DD.MM.YY')} {dayjs(lesson.date).format('H:mm DD.MM.YY')} {lesson.name} + + + + + + Edit + setlessonToDelete(lesson)}> + Delete + + + + {lesson.students.length}
- {/*
    - {data?.body?.map((lesson) => ( - - - {lesson.name} - {dayjs(lesson.date).format('DD MMMM YYYYг.')} - - Участников - {lesson.students.length} - - - - ))} -
*/}
) -- 2.45.2