diff --git a/api/app/application/users_repository.py b/api/app/application/users_repository.py index a1b9e81..d061f0c 100644 --- a/api/app/application/users_repository.py +++ b/api/app/application/users_repository.py @@ -3,6 +3,7 @@ from typing import Optional, List from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession +from app.domain.models import Role from app.domain.models.users import User @@ -31,6 +32,15 @@ class UsersRepository: result = await self.db.execute(query) return result.scalars().first() + async def get_by_role_name(self, role_name: str) -> List[User]: + query = ( + select(User) + .join(User.role) + .filter(Role.title == role_name) + ) + result = await self.db.execute(query) + return result.scalars().all() + async def create(self, user: User) -> User: self.db.add(user) await self.db.commit() diff --git a/api/app/controllers/users_router.py b/api/app/controllers/users_router.py index b4d59ad..dfa31ac 100644 --- a/api/app/controllers/users_router.py +++ b/api/app/controllers/users_router.py @@ -86,3 +86,18 @@ async def create_user( ): register_service = RegisterService(db) return await register_service.create_user(user) + + +@users_router.get( + '/role/{role_name}/', + response_model=List[UserRead], + summary='Return all users with given role', + description='Return all users with given role', +) +async def get_users_by_role_name( + role_name: str, + db: AsyncSession = Depends(get_db), + current_user: User = Depends(require_auth_user), +): + users_service = UsersService(db) + return await users_service.get_by_role_name(role_name) diff --git a/api/app/infrastructure/users_service.py b/api/app/infrastructure/users_service.py index 3a110ee..d84ae13 100644 --- a/api/app/infrastructure/users_service.py +++ b/api/app/infrastructure/users_service.py @@ -95,3 +95,11 @@ class UsersService: user_model = await self.users_repository.update(user_model) return UserRead.model_validate(user_model) + + async def get_by_role_name(self, role_name: str) -> List[UserRead]: + users = await self.users_repository.get_by_role_name(role_name) + response = [] + for user in users: + response.append(UserRead.model_validate(user)) + + return response diff --git a/web/src/Api/coursesApi.js b/web/src/Api/coursesApi.js new file mode 100644 index 0000000..c526836 --- /dev/null +++ b/web/src/Api/coursesApi.js @@ -0,0 +1,74 @@ +import CONFIG from "../Core/сonfig.js"; +import {baseQueryWithAuth} from "./baseQuery.js"; +import {createApi} from "@reduxjs/toolkit/query/react"; + + +export const coursesApi = createApi({ + reducerPath: 'coursesApi', + baseQuery: baseQueryWithAuth, + endpoints: (builder) => ({ + getAllCourses: builder.query({ + query: () => ({ + url: "/courses/", + method: "GET", + }), + providesTags: ['course'], + }), + createCourse: builder.mutation({ + query: (data) => ({ + url: "/courses/", + method: "POST", + body: data, + }), + invalidatesTags: ['course'], + }), + updateCourse: builder.mutation({ + query: ({courseId, ...data}) => ({ + url: `/courses/${courseId}/`, + method: "PUT", + body: data, + }), + invalidatesTags: ['course'], + }), + getCourseTeachers: builder.query({ + query: (courseId) => ({ + url: `/courses/${courseId}/teachers/`, + method: "GET", + }), + providesTags: ['teacher'], + }), + replaceCourseTeachers: builder.mutation({ + query: ({courseId, ...data}) => ({ + url: `/courses/${courseId}/teachers/`, + method: "PUT", + body: data, + }), + invalidatesTags: ['teacher'], + }), + getCourseStudents: builder.query({ + query: (courseId) => ({ + url: `/courses/${courseId}/students/`, + method: "GET", + }), + providesTags: ['student'], + }), + replaceCourseStudents: builder.mutation({ + query: ({courseId, ...data}) => ({ + url: `/courses/${courseId}/students/`, + method: "PUT", + body: data, + }), + invalidatesTags: ['student'], + }), + }), +}); + +export const { + useGetAllCoursesQuery, + useCreateCourseMutation, + useUpdateCourseMutation, + useGetCourseTeachersQuery, + useReplaceCourseTeachersMutation, + useGetCourseStudentsQuery, + useReplaceCourseStudentsMutation, +} = coursesApi; \ No newline at end of file diff --git a/web/src/Api/usersApi.js b/web/src/Api/usersApi.js index 280b620..e1a2333 100644 --- a/web/src/Api/usersApi.js +++ b/web/src/Api/usersApi.js @@ -39,6 +39,13 @@ export const usersApi = createApi({ }), invalidatesTags: ["user"], }), + getUsersByRoleName: builder.query({ + query: (roleName) => ({ + url: `/users/role/${roleName}/`, + method: "GET", + }), + providesTags: ["user"], + }), }), }); @@ -48,4 +55,5 @@ export const { useUpdateUserMutation, useUpdateUserPasswordMutation, useCreateUserMutation, + useGetUsersByRoleNameQuery, } = usersApi; \ No newline at end of file diff --git a/web/src/Components/Pages/AdminPage/AdminPage.jsx b/web/src/Components/Pages/AdminPage/AdminPage.jsx index 33d3290..dcefc4e 100644 --- a/web/src/Components/Pages/AdminPage/AdminPage.jsx +++ b/web/src/Components/Pages/AdminPage/AdminPage.jsx @@ -2,8 +2,8 @@ import {Button, Col, Flex, FloatButton, Input, Result, Row, Table, Tooltip, Typo import {ControlOutlined, PlusOutlined} from "@ant-design/icons"; import useAdminPage from "./useAdminPage.js"; import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; -import CreateUserModalForm from "./CreateUserModalForm/CreateUserModalForm.jsx"; -import UpdateUserModalForm from "./UpdateUserModalForm/UpdateUserModalForm.jsx"; +import CreateUserModalForm from "./Components/CreateUserModalForm/CreateUserModalForm.jsx"; +import UpdateUserModalForm from "./Components/UpdateUserModalForm/UpdateUserModalForm.jsx"; const {Title} = Typography; diff --git a/web/src/Components/Pages/AdminPage/CreateUserModalForm/CreateUserModalForm.jsx b/web/src/Components/Pages/AdminPage/Components/CreateUserModalForm/CreateUserModalForm.jsx similarity index 100% rename from web/src/Components/Pages/AdminPage/CreateUserModalForm/CreateUserModalForm.jsx rename to web/src/Components/Pages/AdminPage/Components/CreateUserModalForm/CreateUserModalForm.jsx diff --git a/web/src/Components/Pages/AdminPage/CreateUserModalForm/useCreateUserModalForm.js b/web/src/Components/Pages/AdminPage/Components/CreateUserModalForm/useCreateUserModalForm.js similarity index 92% rename from web/src/Components/Pages/AdminPage/CreateUserModalForm/useCreateUserModalForm.js rename to web/src/Components/Pages/AdminPage/Components/CreateUserModalForm/useCreateUserModalForm.js index 559bfdd..94bbe43 100644 --- a/web/src/Components/Pages/AdminPage/CreateUserModalForm/useCreateUserModalForm.js +++ b/web/src/Components/Pages/AdminPage/Components/CreateUserModalForm/useCreateUserModalForm.js @@ -1,8 +1,8 @@ import {useDispatch, useSelector} from "react-redux"; import {Form, notification} from "antd"; -import {setOpenModalCreateUser, setSelectedUserToUpdate} from "../../../../Redux/Slices/usersSlice.js"; -import {useGetAllRolesQuery} from "../../../../Api/rolesApi.js"; -import {useCreateUserMutation} from "../../../../Api/usersApi.js"; +import {setOpenModalCreateUser, setSelectedUserToUpdate} from "../../../../../Redux/Slices/usersSlice.js"; +import {useGetAllRolesQuery} from "../../../../../Api/rolesApi.js"; +import {useCreateUserMutation} from "../../../../../Api/usersApi.js"; const useCreateUserModalForm = () => { diff --git a/web/src/Components/Pages/AdminPage/UpdateUserModalForm/UpdateUserModalForm.jsx b/web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/UpdateUserModalForm.jsx similarity index 99% rename from web/src/Components/Pages/AdminPage/UpdateUserModalForm/UpdateUserModalForm.jsx rename to web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/UpdateUserModalForm.jsx index b364ece..991d690 100644 --- a/web/src/Components/Pages/AdminPage/UpdateUserModalForm/UpdateUserModalForm.jsx +++ b/web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/UpdateUserModalForm.jsx @@ -23,7 +23,7 @@ import { TagOutlined, } from "@ant-design/icons"; import dayjs from "dayjs"; -import LoadingIndicator from "../../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; +import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; import useUpdateUserModalForm from "./useUpdateUserModalForm.js"; const {Title, Text} = Typography; diff --git a/web/src/Components/Pages/AdminPage/UpdateUserModalForm/useUpdateUserModalForm.js b/web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/useUpdateUserModalForm.js similarity index 94% rename from web/src/Components/Pages/AdminPage/UpdateUserModalForm/useUpdateUserModalForm.js rename to web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/useUpdateUserModalForm.js index d749512..8267780 100644 --- a/web/src/Components/Pages/AdminPage/UpdateUserModalForm/useUpdateUserModalForm.js +++ b/web/src/Components/Pages/AdminPage/Components/UpdateUserModalForm/useUpdateUserModalForm.js @@ -1,11 +1,11 @@ import {useDispatch, useSelector} from "react-redux"; import {Form, notification} from "antd"; -import {setSelectedUserToUpdate} from "../../../../Redux/Slices/usersSlice.js"; -import {useGetAllRolesQuery} from "../../../../Api/rolesApi.js"; +import {setSelectedUserToUpdate} from "../../../../../Redux/Slices/usersSlice.js"; +import {useGetAllRolesQuery} from "../../../../../Api/rolesApi.js"; import {useEffect} from "react"; import dayjs from "dayjs"; -import {useUpdateUserMutation, useUpdateUserPasswordMutation} from "../../../../Api/usersApi.js"; -import {useGetStatusesQuery} from "../../../../Api/statusesApi.js"; +import {useUpdateUserMutation, useUpdateUserPasswordMutation} from "../../../../../Api/usersApi.js"; +import {useGetStatusesQuery} from "../../../../../Api/statusesApi.js"; const useUpdateUserModalForm = () => { diff --git a/web/src/Components/Pages/Courses/Components/CreateCourseModalForm/CreateCourseModalForm.jsx b/web/src/Components/Pages/Courses/Components/CreateCourseModalForm/CreateCourseModalForm.jsx new file mode 100644 index 0000000..87a2f32 --- /dev/null +++ b/web/src/Components/Pages/Courses/Components/CreateCourseModalForm/CreateCourseModalForm.jsx @@ -0,0 +1,98 @@ +import useCreateCourseModalForm from "./useCreateCourseModalForm.js"; +import {Button, Col, Form, Input, Modal, Result, Row, Select} from "antd"; +import TextArea from "antd/es/input/TextArea.js"; +import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx"; + + +const CreateCourseModal = () => { + const { + openCreateCourseModal, + handleCancel, + form, + isLoadinging, + isError, + teachers, + students + } = useCreateCourseModalForm(); + + if (isLoadinging) { + return ( + + ); + } + + if (isError) { + return ( + + ); + } + + return ( + + Отмена + , + , + ]} + width={800} + > +
+ + + + + +