сделал создание и редактирование мета информации курса, и состава участников и учителей
This commit is contained in:
parent
44ceb93729
commit
9fe0d1e3e9
@ -2,6 +2,7 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
from app.domain.models import Course
|
from app.domain.models import Course
|
||||||
|
|
||||||
@ -11,14 +12,24 @@ class CoursesRepository:
|
|||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
async def get_all(self) -> List[Course]:
|
async def get_all(self) -> List[Course]:
|
||||||
query = select(Course)
|
query = (
|
||||||
|
select(Course)
|
||||||
|
.options(
|
||||||
|
selectinload(Course.teachers),
|
||||||
|
selectinload(Course.enrollments)
|
||||||
|
)
|
||||||
|
)
|
||||||
result = await self.db.execute(query)
|
result = await self.db.execute(query)
|
||||||
return result.scalars().all()
|
return result.scalars().all()
|
||||||
|
|
||||||
async def get_by_id(self, course_id: int) -> Optional[Course]:
|
async def get_by_id(self, course_id: int) -> Optional[Course]:
|
||||||
query = (
|
query = (
|
||||||
select(Course)
|
select(Course)
|
||||||
.order_by(id=course_id)
|
.options(
|
||||||
|
selectinload(Course.teachers),
|
||||||
|
selectinload(Course.enrollments)
|
||||||
|
)
|
||||||
|
.filter_by(id=course_id)
|
||||||
)
|
)
|
||||||
result = await self.db.execute(query)
|
result = await self.db.execute(query)
|
||||||
return result.scalars().first()
|
return result.scalars().first()
|
||||||
|
|||||||
@ -5,12 +5,12 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
|
|
||||||
from app.database.session import get_db
|
from app.database.session import get_db
|
||||||
from app.domain.entities.course_teachers import CourseTeacherRead, CourseTeacherCreate
|
from app.domain.entities.course_teachers import CourseTeacherRead, CourseTeacherCreate
|
||||||
from app.domain.entities.courses import CourseRead, CourseCreate, CourseUpdate
|
from app.domain.entities.courses import CourseRead, CourseCreate, CourseUpdate, CourseCreated
|
||||||
from app.domain.entities.enrollments import EnrollmentRead, EnrollmentCreate
|
from app.domain.entities.enrollments import EnrollmentRead, EnrollmentCreate
|
||||||
from app.domain.models import User
|
from app.domain.models import User
|
||||||
from app.infrastructure.course_teachers_service import CourseTeachersService
|
from app.infrastructure.course_teachers_service import CourseTeachersService
|
||||||
from app.infrastructure.courses_service import CoursesService
|
from app.infrastructure.courses_service import CoursesService
|
||||||
from app.infrastructure.dependencies import require_auth_user, require_teacher
|
from app.infrastructure.dependencies import require_auth_user, require_teacher, require_admin
|
||||||
from app.infrastructure.enrollments_service import EnrollmentsService
|
from app.infrastructure.enrollments_service import EnrollmentsService
|
||||||
|
|
||||||
courses_router = APIRouter()
|
courses_router = APIRouter()
|
||||||
@ -24,7 +24,7 @@ courses_router = APIRouter()
|
|||||||
)
|
)
|
||||||
async def get_all_courses(
|
async def get_all_courses(
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
user: User = Depends(require_auth_user),
|
user: User = Depends(require_admin),
|
||||||
):
|
):
|
||||||
courses_service = CoursesService(db)
|
courses_service = CoursesService(db)
|
||||||
return await courses_service.get_all()
|
return await courses_service.get_all()
|
||||||
@ -32,7 +32,7 @@ async def get_all_courses(
|
|||||||
|
|
||||||
@courses_router.post(
|
@courses_router.post(
|
||||||
'/',
|
'/',
|
||||||
response_model=Optional[CourseRead],
|
response_model=Optional[CourseCreated],
|
||||||
summary='Create a new course',
|
summary='Create a new course',
|
||||||
description='Create a new course',
|
description='Create a new course',
|
||||||
)
|
)
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
"""0002 сделал поле с фото необязательным
|
||||||
|
|
||||||
|
Revision ID: 33d77ac5ed79
|
||||||
|
Revises: 6241a16321b4
|
||||||
|
Create Date: 2025-11-28 19:23:15.655318
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '33d77ac5ed79'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '6241a16321b4'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint(op.f('course_teachers_teacher_id_fkey'), 'course_teachers', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('course_teachers_course_id_fkey'), 'course_teachers', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'course_teachers', 'users', ['teacher_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'course_teachers', 'courses', ['course_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.alter_column('courses', 'photo_filename',
|
||||||
|
existing_type=sa.VARCHAR(),
|
||||||
|
nullable=True)
|
||||||
|
op.alter_column('courses', 'photo_path',
|
||||||
|
existing_type=sa.VARCHAR(),
|
||||||
|
nullable=True)
|
||||||
|
op.drop_constraint(op.f('enrollments_student_id_fkey'), 'enrollments', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('enrollments_course_id_fkey'), 'enrollments', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'enrollments', 'courses', ['course_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'enrollments', 'users', ['student_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('lesson_files_lesson_id_fkey'), 'lesson_files', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'lesson_files', 'lessons', ['lesson_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('lessons_course_id_fkey'), 'lessons', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('lessons_creator_id_fkey'), 'lessons', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'lessons', 'users', ['creator_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'lessons', 'courses', ['course_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('solution_files_solution_id_fkey'), 'solution_files', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'solution_files', 'solutions', ['solution_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('solutions_task_id_fkey'), 'solutions', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('solutions_assessment_autor_id_fkey'), 'solutions', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('solutions_student_id_fkey'), 'solutions', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'solutions', 'users', ['student_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'solutions', 'users', ['assessment_autor_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'solutions', 'tasks', ['task_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('task_files_task_id_fkey'), 'task_files', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'task_files', 'tasks', ['task_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('tasks_creator_id_fkey'), 'tasks', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('tasks_course_id_fkey'), 'tasks', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'tasks', 'users', ['creator_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'tasks', 'courses', ['course_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.drop_constraint(op.f('users_role_id_fkey'), 'users', type_='foreignkey')
|
||||||
|
op.drop_constraint(op.f('users_status_id_fkey'), 'users', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'users', 'statuses', ['status_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
op.create_foreign_key(None, 'users', 'roles', ['role_id'], ['id'], source_schema='public', referent_schema='public')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_constraint(None, 'users', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'users', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('users_status_id_fkey'), 'users', 'statuses', ['status_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('users_role_id_fkey'), 'users', 'roles', ['role_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'tasks', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'tasks', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('tasks_course_id_fkey'), 'tasks', 'courses', ['course_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('tasks_creator_id_fkey'), 'tasks', 'users', ['creator_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'task_files', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('task_files_task_id_fkey'), 'task_files', 'tasks', ['task_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'solutions', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'solutions', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'solutions', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('solutions_student_id_fkey'), 'solutions', 'users', ['student_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('solutions_assessment_autor_id_fkey'), 'solutions', 'users', ['assessment_autor_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('solutions_task_id_fkey'), 'solutions', 'tasks', ['task_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'solution_files', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('solution_files_solution_id_fkey'), 'solution_files', 'solutions', ['solution_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'lessons', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'lessons', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('lessons_creator_id_fkey'), 'lessons', 'users', ['creator_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('lessons_course_id_fkey'), 'lessons', 'courses', ['course_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'lesson_files', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('lesson_files_lesson_id_fkey'), 'lesson_files', 'lessons', ['lesson_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'enrollments', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'enrollments', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('enrollments_course_id_fkey'), 'enrollments', 'courses', ['course_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('enrollments_student_id_fkey'), 'enrollments', 'users', ['student_id'], ['id'])
|
||||||
|
op.alter_column('courses', 'photo_path',
|
||||||
|
existing_type=sa.VARCHAR(),
|
||||||
|
nullable=False)
|
||||||
|
op.alter_column('courses', 'photo_filename',
|
||||||
|
existing_type=sa.VARCHAR(),
|
||||||
|
nullable=False)
|
||||||
|
op.drop_constraint(None, 'course_teachers', schema='public', type_='foreignkey')
|
||||||
|
op.drop_constraint(None, 'course_teachers', schema='public', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('course_teachers_course_id_fkey'), 'course_teachers', 'courses', ['course_id'], ['id'])
|
||||||
|
op.create_foreign_key(op.f('course_teachers_teacher_id_fkey'), 'course_teachers', 'users', ['teacher_id'], ['id'])
|
||||||
|
# ### end Alembic commands ###
|
||||||
@ -2,7 +2,6 @@ from pydantic import BaseModel, EmailStr, Field
|
|||||||
|
|
||||||
|
|
||||||
class CourseTeacherCreate(BaseModel):
|
class CourseTeacherCreate(BaseModel):
|
||||||
course_id: int = Field()
|
|
||||||
teacher_id: int = Field()
|
teacher_id: int = Field()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,17 @@ from app.domain.entities.course_teachers import CourseTeacherRead
|
|||||||
from app.domain.entities.enrollments import EnrollmentRead
|
from app.domain.entities.enrollments import EnrollmentRead
|
||||||
|
|
||||||
|
|
||||||
|
class CourseBase(BaseModel):
|
||||||
|
id: int
|
||||||
|
title: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
photo_filename: Optional[str] = None
|
||||||
|
photo_path: Optional[str] = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
class CourseCreate(BaseModel):
|
class CourseCreate(BaseModel):
|
||||||
title: str = Field(max_length=250)
|
title: str = Field(max_length=250)
|
||||||
description: Optional[str] = Field(default=None, max_length=1000)
|
description: Optional[str] = Field(default=None, max_length=1000)
|
||||||
@ -15,13 +26,14 @@ class CourseUpdate(CourseCreate):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CourseRead(BaseModel):
|
class CourseRead(CourseBase):
|
||||||
id: int
|
teachers: List[CourseTeacherRead] = []
|
||||||
title: str
|
enrollments: List[EnrollmentRead] = []
|
||||||
description: str
|
|
||||||
|
|
||||||
teachers: List[CourseTeacherRead]
|
|
||||||
enrollments: List[EnrollmentRead]
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class CourseCreated(CourseBase):
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pydantic import BaseModel, EmailStr, Field
|
from pydantic import BaseModel, EmailStr, Field
|
||||||
|
|
||||||
|
|
||||||
class EnrollmentCreate(BaseModel):
|
class EnrollmentCreate(BaseModel):
|
||||||
course_id: int = Field()
|
|
||||||
student_id: int = Field()
|
student_id: int = Field()
|
||||||
|
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ class EnrollmentRead(BaseModel):
|
|||||||
id: int
|
id: int
|
||||||
course_id: int
|
course_id: int
|
||||||
student_id: int
|
student_id: int
|
||||||
enrollment_date: datetime
|
enrollment_date: Optional[datetime] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|||||||
@ -17,8 +17,8 @@ class RootTable(Base):
|
|||||||
class PhotoAbstract(RootTable):
|
class PhotoAbstract(RootTable):
|
||||||
__abstract__ = True
|
__abstract__ = True
|
||||||
|
|
||||||
photo_filename: Mapped[str] = mapped_column()
|
photo_filename: Mapped[str] = mapped_column(nullable=True)
|
||||||
photo_path: Mapped[str] = mapped_column()
|
photo_path: Mapped[str] = mapped_column(nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class FileAbstract(RootTable):
|
class FileAbstract(RootTable):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy import ForeignKey
|
from sqlalchemy import ForeignKey, func
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
from app.domain.models.base import RootTable
|
from app.domain.models.base import RootTable
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from fastapi import HTTPException, status
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.application.courses_repository import CoursesRepository
|
from app.application.courses_repository import CoursesRepository
|
||||||
from app.domain.entities.courses import CourseRead, CourseCreate
|
from app.domain.entities.courses import CourseRead, CourseCreate, CourseCreated
|
||||||
from app.domain.models import Course
|
from app.domain.models import Course
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ class CoursesService:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
async def create(self, course: CourseCreate) -> Optional[CourseRead]:
|
async def create(self, course: CourseCreate) -> CourseCreated: # ← возвращаем CourseCreated
|
||||||
course_model = Course(
|
course_model = Course(
|
||||||
title=course.title,
|
title=course.title,
|
||||||
description=course.description,
|
description=course.description,
|
||||||
@ -30,7 +30,7 @@ class CoursesService:
|
|||||||
|
|
||||||
course_model = await self.courses_repository.create(course_model)
|
course_model = await self.courses_repository.create(course_model)
|
||||||
|
|
||||||
return CourseRead.model_validate(course_model)
|
return CourseCreated.model_validate(course_model)
|
||||||
|
|
||||||
async def update(self, course_id: int, course: CourseCreate) -> Optional[CourseRead]:
|
async def update(self, course_id: int, course: CourseCreate) -> Optional[CourseRead]:
|
||||||
course_model = await self.courses_repository.get_by_id(course_id)
|
course_model = await self.courses_repository.get_by_id(course_id)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import datetime
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ class EnrollmentsService:
|
|||||||
enrollments_models.append(Enrollment(
|
enrollments_models.append(Enrollment(
|
||||||
course_id=course_id,
|
course_id=course_id,
|
||||||
student_id=enrollment.student_id,
|
student_id=enrollment.student_id,
|
||||||
enrollment_date=enrollment.enrollment_date,
|
enrollment_date=datetime.datetime.now(),
|
||||||
))
|
))
|
||||||
|
|
||||||
enrollments_models = await self.enrollments_repository.create_list(enrollments_models)
|
enrollments_models = await self.enrollments_repository.create_list(enrollments_models)
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/rounded_logo.png" />
|
<link rel="icon" href="/rounded_logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>web</title>
|
<title>Система обучения lectio</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@ -38,10 +38,10 @@ export const coursesApi = createApi({
|
|||||||
providesTags: ['teacher'],
|
providesTags: ['teacher'],
|
||||||
}),
|
}),
|
||||||
replaceCourseTeachers: builder.mutation({
|
replaceCourseTeachers: builder.mutation({
|
||||||
query: ({courseId, ...data}) => ({
|
query: ({courseId, teachers}) => ({
|
||||||
url: `/courses/${courseId}/teachers/`,
|
url: `/courses/${courseId}/teachers/`,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: data,
|
body: teachers,
|
||||||
}),
|
}),
|
||||||
invalidatesTags: ['teacher'],
|
invalidatesTags: ['teacher'],
|
||||||
}),
|
}),
|
||||||
@ -53,10 +53,10 @@ export const coursesApi = createApi({
|
|||||||
providesTags: ['student'],
|
providesTags: ['student'],
|
||||||
}),
|
}),
|
||||||
replaceCourseStudents: builder.mutation({
|
replaceCourseStudents: builder.mutation({
|
||||||
query: ({courseId, ...data}) => ({
|
query: ({courseId, students}) => ({
|
||||||
url: `/courses/${courseId}/students/`,
|
url: `/courses/${courseId}/students/`,
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: data,
|
body: students,
|
||||||
}),
|
}),
|
||||||
invalidatesTags: ['student'],
|
invalidatesTags: ['student'],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -79,7 +79,7 @@ const MainLayout = () => {
|
|||||||
<Outlet/>
|
<Outlet/>
|
||||||
)}
|
)}
|
||||||
</Content>
|
</Content>
|
||||||
<Footer style={{textAlign: "center"}}>{new Date().getFullYear()}</Footer>
|
<Footer style={{textAlign: "center"}}>lectio © {new Date().getFullYear()}</Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,37 +1,37 @@
|
|||||||
|
import {Button, Col, Form, Input, Modal, Result, Row, Select, Spin} from "antd";
|
||||||
import useCreateCourseModalForm from "./useCreateCourseModalForm.js";
|
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";
|
import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
|
|
||||||
|
const {Option} = Select;
|
||||||
|
const {TextArea} = Input;
|
||||||
|
|
||||||
const CreateCourseModal = () => {
|
const CreateCourseModal = () => {
|
||||||
const {
|
const {
|
||||||
openCreateCourseModal,
|
openCreateCourseModal,
|
||||||
handleCancel,
|
handleCancel,
|
||||||
|
handleOk,
|
||||||
form,
|
form,
|
||||||
isLoadinging,
|
|
||||||
isError,
|
|
||||||
teachers,
|
teachers,
|
||||||
students
|
students,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
} = useCreateCourseModalForm();
|
} = useCreateCourseModalForm();
|
||||||
|
|
||||||
if (isLoadinging) {
|
if (isError) {
|
||||||
return (
|
return <Modal visible={openCreateCourseModal} footer={null}>
|
||||||
<LoadingIndicator/>
|
<Result status="500" title="Ошибка загрузки"/>
|
||||||
);
|
</Modal>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isError) {
|
const filterOption = (input, option) =>
|
||||||
return (
|
option.children.toString().toLowerCase().includes(input.toLowerCase());
|
||||||
<Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных пользователя"/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={"Создание курса"}
|
title="Создание курса"
|
||||||
open={openCreateCourseModal}
|
open={openCreateCourseModal}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
width={900}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key="cancel" onClick={handleCancel}>
|
<Button key="cancel" onClick={handleCancel}>
|
||||||
Отмена
|
Отмена
|
||||||
@ -39,60 +39,72 @@ const CreateCourseModal = () => {
|
|||||||
<Button
|
<Button
|
||||||
key="submit"
|
key="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={isLoadinging}
|
loading={isLoading}
|
||||||
// onClick={handleOk}
|
onClick={handleOk}
|
||||||
>
|
>
|
||||||
Создать
|
Создать курс
|
||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
width={800}
|
|
||||||
>
|
>
|
||||||
<Form form={form} layout="vertical">
|
{isLoading ? (
|
||||||
<Form.Item
|
<LoadingIndicator/>
|
||||||
name="title"
|
) : (
|
||||||
label="Название курса"
|
<Form form={form} layout="vertical">
|
||||||
rules={[{required: true, message: "Введите название"}]}
|
<Form.Item
|
||||||
>
|
name="title"
|
||||||
<Input size="large" placeholder="Введение в Python"/>
|
label="Название курса"
|
||||||
</Form.Item>
|
rules={[{required: true, message: "Введите название курса"}]}
|
||||||
|
>
|
||||||
|
<Input size="large" placeholder="Введение в React"/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item name="description" label="Описание">
|
<Form.Item name="description" label="Описание (необязательно)">
|
||||||
<TextArea rows={4} placeholder="Курс для начинающих..."/>
|
<TextArea rows={4} placeholder="Курс для начинающих разработчиков..."/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="teacher_ids" label="Преподаватели">
|
<Form.Item name="teacher_ids" label="Преподаватели">
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder="Выберите преподавателей"
|
placeholder="Начните вводить ФИО или логин..."
|
||||||
loading={isLoadinging}
|
loading={isLoading}
|
||||||
>
|
showSearch={{filterOption: filterOption}}
|
||||||
{teachers.map((teacher) => (
|
notFoundContent="Преподаватели не найдены"
|
||||||
<Select.Option key={teacher.id}
|
>
|
||||||
value={teacher.id}>{teacher.last_name} {teacher.first_name} - {teacher.login}</Select.Option>
|
{teachers.map((teacher) => (
|
||||||
))}
|
<Option key={teacher.id} value={teacher.id}>
|
||||||
</Select>
|
{teacher.last_name} {teacher.first_name} ({teacher.login})
|
||||||
</Form.Item>
|
</Option>
|
||||||
</Col>
|
))}
|
||||||
<Col span={12}>
|
</Select>
|
||||||
<Form.Item name="student_ids" label="Студенты">
|
</Form.Item>
|
||||||
<Select
|
</Col>
|
||||||
mode="multiple"
|
|
||||||
placeholder="Выберите студентов"
|
<Col span={12}>
|
||||||
loading={isLoadinging}
|
<Form.Item name="student_ids" label="Студенты">
|
||||||
>
|
<Select
|
||||||
{students.map((student) => (
|
mode="multiple"
|
||||||
<Select.Option key={student.id}
|
showSearch
|
||||||
value={student.id}>{student.last_name} {student.first_name} - {student.login}</Select.Option>
|
placeholder="Начните вводить ФИО или логин..."
|
||||||
))}
|
loading={isLoading}
|
||||||
</Select>
|
filterOption={filterOption}
|
||||||
</Form.Item>
|
optionFilterProp="children"
|
||||||
</Col>
|
notFoundContent="Студенты не найдены"
|
||||||
</Row>
|
>
|
||||||
</Form>
|
{students.map((student) => (
|
||||||
|
<Option key={student.id} value={student.id}>
|
||||||
|
{student.last_name} {student.first_name} ({student.login})
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CreateCourseModal;
|
export default CreateCourseModal;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import {useDispatch, useSelector} from "react-redux";
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
import {setOpenCreateCourseModal} from "../../../../../Redux/Slices/coursesSlice.js";
|
import {setOpenCreateCourseModal} from "../../../../../Redux/Slices/coursesSlice.js";
|
||||||
import {Form} from "antd";
|
import {Form, notification} from "antd";
|
||||||
import {useGetUsersByRoleNameQuery} from "../../../../../Api/usersApi.js";
|
import {useGetUsersByRoleNameQuery} from "../../../../../Api/usersApi.js";
|
||||||
import {
|
import {
|
||||||
useCreateCourseMutation,
|
useCreateCourseMutation,
|
||||||
@ -43,7 +43,54 @@ const useCreateCourseModalForm = () => {
|
|||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
dispatch(setOpenCreateCourseModal(false));
|
dispatch(setOpenCreateCourseModal(false));
|
||||||
}
|
};
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
|
||||||
|
const newCourse = await createCourse({
|
||||||
|
title: values.title,
|
||||||
|
description: values.description || null,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
if (values.teacher_ids?.length > 0) {
|
||||||
|
const teachersPayload = values.teacher_ids.map(id => ({
|
||||||
|
teacher_id: id
|
||||||
|
}));
|
||||||
|
|
||||||
|
await replaceTeachers({
|
||||||
|
courseId: newCourse.id,
|
||||||
|
teachers: teachersPayload
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.student_ids?.length > 0) {
|
||||||
|
const studentsPayload = values.student_ids.map(id => ({
|
||||||
|
student_id: id,
|
||||||
|
}));
|
||||||
|
await replaceStudents({
|
||||||
|
courseId: newCourse.id,
|
||||||
|
students: studentsPayload
|
||||||
|
}).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.success({
|
||||||
|
title: "Успешно",
|
||||||
|
description: "Курс успешно создан!",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
|
||||||
|
form.resetFields();
|
||||||
|
dispatch(setOpenCreateCourseModal(false));
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
title: "Ошибка",
|
||||||
|
description: error?.data?.detail || "Не удалось создать курс.",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
openCreateCourseModal,
|
openCreateCourseModal,
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
import {Button, Col, Form, Input, Modal, Result, Row, Select, Spin} from "antd";
|
||||||
|
import useUpdateCourseModalForm from "./useUpdateCourseModalForm.js";
|
||||||
|
import LoadingIndicator from "../../../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
|
|
||||||
|
const {Option} = Select;
|
||||||
|
const {TextArea} = Input;
|
||||||
|
|
||||||
|
const UpdateCourseModal = () => {
|
||||||
|
const {
|
||||||
|
isModalOpen,
|
||||||
|
openUpdateCourseModal,
|
||||||
|
handleCancel,
|
||||||
|
handleOk,
|
||||||
|
form,
|
||||||
|
teachers,
|
||||||
|
students,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
} = useUpdateCourseModalForm();
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return (
|
||||||
|
<Modal open={openUpdateCourseModal} footer={null} onCancel={handleCancel}>
|
||||||
|
<Result status="500" title="Ошибка загрузки"/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterOption = (input, option) =>
|
||||||
|
option.children.toString().toLowerCase().includes(input.toLowerCase());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Редактирование курса"
|
||||||
|
open={isModalOpen}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
width={900}
|
||||||
|
footer={[
|
||||||
|
<Button key="cancel" onClick={handleCancel}>
|
||||||
|
Отмена
|
||||||
|
</Button>,
|
||||||
|
<Button key="submit" type="primary" loading={isLoading} onClick={handleOk}>
|
||||||
|
Сохранить изменения
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<LoadingIndicator />
|
||||||
|
) : (
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
name="title"
|
||||||
|
label="Название курса"
|
||||||
|
rules={[{ required: true, message: "Введите название курса" }]}
|
||||||
|
>
|
||||||
|
<Input size="large" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item name="description" label="Описание">
|
||||||
|
<TextArea rows={4} placeholder="Описание курса..." />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item name="teacher_ids" label="Преподаватели">
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
placeholder="Выберите преподавателей"
|
||||||
|
showSearch
|
||||||
|
filterOption={filterOption}
|
||||||
|
notFoundContent="Преподаватели не найдены"
|
||||||
|
>
|
||||||
|
{teachers.map((teacher) => (
|
||||||
|
<Option key={teacher.id} value={teacher.id}>
|
||||||
|
{teacher.last_name} {teacher.first_name} ({teacher.login})
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item name="student_ids" label="Студенты">
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
placeholder="Выберите студентов"
|
||||||
|
showSearch
|
||||||
|
filterOption={filterOption}
|
||||||
|
notFoundContent="Студенты не найдены"
|
||||||
|
>
|
||||||
|
{students.map((student) => (
|
||||||
|
<Option key={student.id} value={student.id}>
|
||||||
|
{student.last_name} {student.first_name} ({student.login})
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpdateCourseModal;
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
|
import {setSelectedCourseToUpdate} from "../../../../../Redux/Slices/coursesSlice.js";
|
||||||
|
import {Form, notification} from "antd";
|
||||||
|
import {useGetUsersByRoleNameQuery} from "../../../../../Api/usersApi.js";
|
||||||
|
import {
|
||||||
|
useGetCourseStudentsQuery, useGetCourseTeachersQuery,
|
||||||
|
useReplaceCourseStudentsMutation,
|
||||||
|
useReplaceCourseTeachersMutation, useUpdateCourseMutation
|
||||||
|
} from "../../../../../Api/coursesApi.js";
|
||||||
|
import {useEffect} from "react";
|
||||||
|
import {ROLES} from "../../../../../Core/constants.js";
|
||||||
|
|
||||||
|
|
||||||
|
const useUpdateCourseModalForm = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
const {selectedCourseToUpdate} = useSelector((state) => state.courses);
|
||||||
|
const courseId = selectedCourseToUpdate?.id;
|
||||||
|
|
||||||
|
const isModalOpen = selectedCourseToUpdate !== null;
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: allTeachers = [],
|
||||||
|
isLoading: teachersLoading,
|
||||||
|
isError: teachersError,
|
||||||
|
} = useGetUsersByRoleNameQuery(ROLES.TEACHER);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: allStudents = [],
|
||||||
|
isLoading: studentsLoading,
|
||||||
|
isError: studentsError,
|
||||||
|
} = useGetUsersByRoleNameQuery(ROLES.STUDENT);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: currentTeachers = [],
|
||||||
|
isLoading: currentTeachersLoading,
|
||||||
|
} = useGetCourseTeachersQuery(courseId, {skip: !courseId});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: currentStudents = [],
|
||||||
|
isLoading: currentStudentsLoading,
|
||||||
|
} = useGetCourseStudentsQuery(courseId, {skip: !courseId});
|
||||||
|
|
||||||
|
const [updateCourse, {isLoading: updating}] = useUpdateCourseMutation();
|
||||||
|
const [replaceTeachers] = useReplaceCourseTeachersMutation();
|
||||||
|
const [replaceStudents] = useReplaceCourseStudentsMutation();
|
||||||
|
|
||||||
|
const isLoading = teachersLoading || studentsLoading || updating || currentTeachersLoading || currentStudentsLoading;
|
||||||
|
const isError = teachersError || studentsError;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedCourseToUpdate) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
title: selectedCourseToUpdate.title,
|
||||||
|
description: selectedCourseToUpdate.description || "",
|
||||||
|
teacher_ids: currentTeachers.map(t => t.teacher_id),
|
||||||
|
student_ids: currentStudents.map(s => s.student_id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [selectedCourseToUpdate, currentTeachers, currentStudents, form]);
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
form.resetFields();
|
||||||
|
dispatch(setSelectedCourseToUpdate(null));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
|
||||||
|
await updateCourse({
|
||||||
|
courseId: courseId,
|
||||||
|
title: values.title,
|
||||||
|
description: values.description || null,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
const teachersPayload = (values.teacher_ids || []).map(id => ({teacher_id: id}));
|
||||||
|
await replaceTeachers({
|
||||||
|
courseId,
|
||||||
|
teachers: teachersPayload,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
const studentsPayload = (values.student_ids || []).map(id => ({student_id: id}));
|
||||||
|
await replaceStudents({
|
||||||
|
courseId,
|
||||||
|
students: studentsPayload,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
notification.success({
|
||||||
|
message: "Успех",
|
||||||
|
description: "Курс успешно обновлён!",
|
||||||
|
});
|
||||||
|
|
||||||
|
handleCancel();
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
message: "Ошибка",
|
||||||
|
description: error?.data?.detail || "Не удалось обновить курс",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isModalOpen,
|
||||||
|
handleCancel,
|
||||||
|
handleOk,
|
||||||
|
form,
|
||||||
|
teachers: allTeachers,
|
||||||
|
students: allStudents,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useUpdateCourseModalForm;
|
||||||
@ -18,6 +18,7 @@ import {
|
|||||||
import useCoursesPage from "./useCoursesPage.js";
|
import useCoursesPage from "./useCoursesPage.js";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import CreateCourseModalForm from "./Components/CreateCourseModalForm/CreateCourseModalForm.jsx";
|
import CreateCourseModalForm from "./Components/CreateCourseModalForm/CreateCourseModalForm.jsx";
|
||||||
|
import UpdateCourseModalForm from "./Components/UpdateCourseModalForm/UpdateCourseModalForm.jsx";
|
||||||
|
|
||||||
const {Title, Text} = Typography;
|
const {Title, Text} = Typography;
|
||||||
|
|
||||||
@ -103,27 +104,11 @@ const CoursesPage = () => {
|
|||||||
course.description || <Text type="secondary">Без описания</Text>
|
course.description || <Text type="secondary">Без описания</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Space direction="vertical" style={{width: "100%", marginTop: 16}}>
|
|
||||||
<Space>
|
|
||||||
<TeamOutlined/>
|
|
||||||
<Text>
|
|
||||||
{course.teachers?.length || 0} учител
|
|
||||||
{course.teachers?.length === 1 ? "ь" : "ей"}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
<Space>
|
|
||||||
<UserOutlined/>
|
|
||||||
<Text>
|
|
||||||
{course.enrollments?.length || 0} студент
|
|
||||||
{course.enrollments?.length === 1 ? "" : "ов"}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
{course.teachers?.length > 0 && (
|
{course.teachers?.length > 0 && (
|
||||||
<div style={{marginTop: 16}}>
|
<div style={{marginTop: 16}}>
|
||||||
<Text type="secondary">Преподаватели:</Text>
|
<Text type="secondary">Преподаватели:</Text>
|
||||||
<Avatar.Group maxCount={3} style={{marginTop: 8}}>
|
<Avatar.Group max={{count: 3}} style={{marginTop: 8}}>
|
||||||
{course.teachers.map((t) => (
|
{course.teachers.map((t) => (
|
||||||
<Avatar key={t.teacher_id} style={{backgroundColor: "#1890ff"}}>
|
<Avatar key={t.teacher_id} style={{backgroundColor: "#1890ff"}}>
|
||||||
{t.teacher?.first_name?.[0] || "У"}
|
{t.teacher?.first_name?.[0] || "У"}
|
||||||
@ -147,6 +132,7 @@ const CoursesPage = () => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<CreateCourseModalForm/>
|
<CreateCourseModalForm/>
|
||||||
|
<UpdateCourseModalForm/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,14 +3,16 @@ import {useGetAllCoursesQuery} from "../../../Api/coursesApi.js";
|
|||||||
import CONFIG from "../../../Core/сonfig.js";
|
import CONFIG from "../../../Core/сonfig.js";
|
||||||
import {ROLES} from "../../../Core/constants.js";
|
import {ROLES} from "../../../Core/constants.js";
|
||||||
import {useDispatch} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
import {setOpenCreateCourseModal} from "../../../Redux/Slices/coursesSlice.js";
|
import {setOpenCreateCourseModal, setSelectedCourseToUpdate} from "../../../Redux/Slices/coursesSlice.js";
|
||||||
|
|
||||||
|
|
||||||
const useCoursesPage = () => {
|
const useCoursesPage = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const {data: userData, isLoading: isUserLoading} = useGetAuthenticatedUserDataQuery();
|
const {data: userData, isLoading: isUserLoading} = useGetAuthenticatedUserDataQuery();
|
||||||
const {data: courses = [], isLoading, isCoursesLoading, isError} = useGetAllCoursesQuery();
|
const {data: courses = [], isLoading, isCoursesLoading, isError} = useGetAllCoursesQuery(undefined, {
|
||||||
|
pollingInterval: 20000,
|
||||||
|
});
|
||||||
|
|
||||||
const isAdmin = userData?.role?.title === CONFIG.ROOT_ROLE_NAME;
|
const isAdmin = userData?.role?.title === CONFIG.ROOT_ROLE_NAME;
|
||||||
const isTeacher = [CONFIG.ROOT_ROLE_NAME, ROLES.TEACHER].includes(userData?.role?.title);
|
const isTeacher = [CONFIG.ROOT_ROLE_NAME, ROLES.TEACHER].includes(userData?.role?.title);
|
||||||
@ -19,8 +21,8 @@ const useCoursesPage = () => {
|
|||||||
dispatch(setOpenCreateCourseModal(true));
|
dispatch(setOpenCreateCourseModal(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeCreateModal = () => {
|
const openEditModal = (course) => {
|
||||||
dispatch(setOpenCreateCourseModal(false));
|
dispatch(setSelectedCourseToUpdate(course));
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -30,7 +32,7 @@ const useCoursesPage = () => {
|
|||||||
isAdmin,
|
isAdmin,
|
||||||
isTeacher,
|
isTeacher,
|
||||||
openCreateModal,
|
openCreateModal,
|
||||||
closeModal: closeCreateModal,
|
openEditModal,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user