From 2dd04197265fa439e81408391c0569d605009ee8 Mon Sep 17 00:00:00 2001 From: andrei Date: Fri, 28 Nov 2025 23:51:42 +0500 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BA=D1=83=D1=80=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/app/application/courses_repository.py | 30 +++- api/app/controllers/courses_router.py | 14 ++ api/app/infrastructure/courses_service.py | 26 ++- web/src/Api/coursesApi.js | 8 + .../UpdateLessonModalForm.jsx | 79 +++++++++ .../useUpdateLessonModalForm.js | 153 ++++++++++++++++++ .../CourseDetailPage/CourseDetailPage.jsx | 21 ++- .../Pages/CoursesPage/CoursesPage.jsx | 4 +- .../Pages/CoursesPage/useCoursesPage.js | 4 +- 9 files changed, 321 insertions(+), 18 deletions(-) create mode 100644 web/src/Components/Pages/CourseDetailPage/Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx create mode 100644 web/src/Components/Pages/CourseDetailPage/Components/UpdateLessonModalForm/useUpdateLessonModalForm.js diff --git a/api/app/application/courses_repository.py b/api/app/application/courses_repository.py index 6dd6964..72436c9 100644 --- a/api/app/application/courses_repository.py +++ b/api/app/application/courses_repository.py @@ -1,10 +1,10 @@ -from typing import List, Optional +from typing import List, Optional, Sequence from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload -from app.domain.models import Course +from app.domain.models import Course, CourseTeacher, Enrollment class CoursesRepository: @@ -22,6 +22,32 @@ class CoursesRepository: result = await self.db.execute(query) return result.scalars().all() + async def get_all_for_teacher(self, user_id: int) -> Optional[Sequence[Course]]: + query = ( + select(Course) + .options( + selectinload(Course.teachers), + selectinload(Course.enrollments) + ) + .join(Course.teachers) + .filter(CourseTeacher.teacher_id == user_id) + ) + result = await self.db.execute(query) + return result.scalars().all() + + async def get_all_for_enrollment(self, user_id: int) -> Optional[Sequence[Course]]: + query = ( + select(Course) + .options( + selectinload(Course.teachers), + selectinload(Course.enrollments) + ) + .join(Course.enrollments) + .filter(Enrollment.user_id == user_id) + ) + result = await self.db.execute(query) + return result.scalars().all() + async def get_by_id(self, course_id: int) -> Optional[Course]: query = ( select(Course) diff --git a/api/app/controllers/courses_router.py b/api/app/controllers/courses_router.py index 4a9c016..49f55ea 100644 --- a/api/app/controllers/courses_router.py +++ b/api/app/controllers/courses_router.py @@ -30,6 +30,20 @@ async def get_all_courses( return await courses_service.get_all() +@courses_router.get( + '/for-me/', + response_model=Optional[List[CourseRead]], + summary='Return all for current user', + description='Return all current user', +) +async def get_fors_for_teacher( + db: AsyncSession = Depends(get_db), + user: User = Depends(require_auth_user), +): + courses_service = CoursesService(db) + return await courses_service.get_all_for_me(user) + + @courses_router.get( '/{course_id}/', response_model=Optional[CourseRead], diff --git a/api/app/infrastructure/courses_service.py b/api/app/infrastructure/courses_service.py index f9cd492..dff2e92 100644 --- a/api/app/infrastructure/courses_service.py +++ b/api/app/infrastructure/courses_service.py @@ -4,13 +4,16 @@ from fastapi import HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from app.application.courses_repository import CoursesRepository +from app.core.constants import UserRoles from app.domain.entities.courses import CourseRead, CourseCreate, CourseCreated -from app.domain.models import Course +from app.domain.models import Course, User +from app.settings import Settings class CoursesService: def __init__(self, db: AsyncSession): self.courses_repository = CoursesRepository(db) + self.settings = Settings() async def get_all(self) -> List[CourseRead]: courses = await self.courses_repository.get_all() @@ -22,6 +25,27 @@ class CoursesService: return response + async def get_all_for_me(self, user: User) -> Optional[List[CourseRead]]: + if user.role.title == self.settings.root_role_name: + courses = await self.courses_repository.get_all() + + elif user.role.title == UserRoles.TEACHER: + courses = await self.courses_repository.get_all_for_teacher(user.id) + + elif user.role.title == UserRoles.STUDENT: + courses = await self.courses_repository.get_all_for_enrollment(user.id) + + else: + courses = [] + + response = [] + for course in courses: + response.append( + CourseRead.model_validate(course) + ) + + return response + async def get_by_id(self, course_id: int) -> Optional[CourseRead]: course = await self.courses_repository.get_by_id(course_id) if course is None: diff --git a/web/src/Api/coursesApi.js b/web/src/Api/coursesApi.js index e296f9b..91d57da 100644 --- a/web/src/Api/coursesApi.js +++ b/web/src/Api/coursesApi.js @@ -14,6 +14,13 @@ export const coursesApi = createApi({ }), providesTags: ['course'], }), + getAllMyCourses: builder.query({ + query: () => ({ + url: "/courses/for-me/", + method: "GET", + }), + providesTags: ['course'], + }), getCourseById: builder.query({ query: (courseId) => ({ url: `/courses/${courseId}/`, @@ -72,6 +79,7 @@ export const coursesApi = createApi({ export const { useGetAllCoursesQuery, + useGetAllMyCoursesQuery, useGetCourseByIdQuery, useCreateCourseMutation, useUpdateCourseMutation, diff --git a/web/src/Components/Pages/CourseDetailPage/Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx b/web/src/Components/Pages/CourseDetailPage/Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx new file mode 100644 index 0000000..f7b4984 --- /dev/null +++ b/web/src/Components/Pages/CourseDetailPage/Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx @@ -0,0 +1,79 @@ +import useUpdateLessonModalForm from "./useUpdateLessonModalForm.js"; +import {Button, Form, Input, InputNumber, Modal, Upload} from "antd"; +import JoditEditor from "jodit-react"; +import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; + +const {TextArea} = Input; + +const UpdateLessonModalForm = ({courseId}) => { + const { + isModalOpen, + handleCancel, + handleOk, + form, + joditConfig, + editorRef, + isLoading, + initialContent, + currentLesson, + } = useUpdateLessonModalForm({courseId}); + + if (isLoading) { + return + + + } + + return ( + + Отмена + , + , + ]} + maskClosable={false} + keyboard={false} + > +
+ + + + + +