сделал комментарии к решению
This commit is contained in:
parent
9195f4eacc
commit
be01b5fc22
39
api/app/application/solution_comments_repository.py
Normal file
39
api/app/application/solution_comments_repository.py
Normal file
@ -0,0 +1,39 @@
|
||||
from typing import Optional, List
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.domain.models import SolutionComment
|
||||
|
||||
|
||||
class SolutionCommentsRepository:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
|
||||
async def get_by_id(self, comment_id: int) -> Optional[SolutionComment]:
|
||||
query = (
|
||||
select(SolutionComment)
|
||||
.filter_by(id=comment_id)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
return result.scalars().first()
|
||||
|
||||
async def get_by_solution_id(self, solution_id: int) -> Optional[List[SolutionComment]]:
|
||||
query = (
|
||||
select(SolutionComment)
|
||||
.filter_by(solution_id=solution_id)
|
||||
.order_by(SolutionComment.created_at)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def create(self, comment: SolutionComment) -> SolutionComment:
|
||||
self.db.add(comment)
|
||||
await self.db.commit()
|
||||
await self.db.refresh(comment)
|
||||
return comment
|
||||
|
||||
async def delete(self, comment: SolutionComment) -> SolutionComment:
|
||||
await self.db.delete(comment)
|
||||
await self.db.commit()
|
||||
return comment
|
||||
@ -25,6 +25,7 @@ class SolutionsRepository:
|
||||
.filter_by(task_id=task_id)
|
||||
.options(
|
||||
selectinload(Solution.files),
|
||||
selectinload(Solution.solution_comments),
|
||||
)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
@ -36,6 +37,7 @@ class SolutionsRepository:
|
||||
.filter_by(task_id=task_id, student_id=student_id)
|
||||
.options(
|
||||
selectinload(Solution.files),
|
||||
selectinload(Solution.solution_comments),
|
||||
)
|
||||
)
|
||||
result = await self.db.execute(query)
|
||||
|
||||
55
api/app/controllers/solution_comments_router.py
Normal file
55
api/app/controllers/solution_comments_router.py
Normal file
@ -0,0 +1,55 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, status, Response
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database.session import get_db
|
||||
from app.domain.entities.solutions import SolutionCommentRead, SolutionCommentCreate
|
||||
from app.domain.models import SolutionComment, User
|
||||
from app.infrastructure.dependencies import require_auth_user
|
||||
from app.infrastructure.solution_comments_service import SolutionCommentsService
|
||||
|
||||
solution_comments_router = APIRouter()
|
||||
|
||||
|
||||
@solution_comments_router.get(
|
||||
'/solution/{solution_id}/',
|
||||
response_model=List[SolutionCommentRead],
|
||||
summary='Returns all comments for solution',
|
||||
description='Returns all comments for solution',
|
||||
)
|
||||
async def get_solution_comments(
|
||||
solution_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(require_auth_user),
|
||||
):
|
||||
solution_comments_service = SolutionCommentsService(db)
|
||||
return await solution_comments_service.get_by_solution_id(solution_id)
|
||||
|
||||
@solution_comments_router.post(
|
||||
'/solution/{solution_id}/',
|
||||
response_model=SolutionCommentRead,
|
||||
summary='Creates a new solution comment',
|
||||
description='Creates a new solution comment',
|
||||
)
|
||||
async def create_solution_comment(
|
||||
solution_id: int,
|
||||
solution_comment: SolutionCommentCreate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(require_auth_user),
|
||||
):
|
||||
solution_comments_service = SolutionCommentsService(db)
|
||||
return await solution_comments_service.create(solution_comment, user, solution_id)
|
||||
|
||||
@solution_comments_router.delete(
|
||||
'/{comment_id}/',
|
||||
summary='Deletes a solution comment',
|
||||
description='Deletes a solution comment',
|
||||
)
|
||||
async def delete_solution_comment(
|
||||
comment_id: int,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(require_auth_user),
|
||||
):
|
||||
solution_comments_service = SolutionCommentsService(db)
|
||||
return await solution_comments_service.delete(comment_id)
|
||||
@ -27,14 +27,33 @@ class SolutionAfterCreate(SolutionBase):
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SolutionRead(SolutionAfterCreate):
|
||||
created_at: datetime
|
||||
class AssessmentCreate(BaseModel):
|
||||
assessment: int = Field(...)
|
||||
|
||||
files: Optional[List[ReadSolutionFile]] = []
|
||||
|
||||
class SolutionCommentBase(BaseModel):
|
||||
comment_text: str = Field(...)
|
||||
|
||||
|
||||
class SolutionCommentCreate(SolutionCommentBase):
|
||||
pass
|
||||
|
||||
|
||||
class SolutionCommentRead(SolutionCommentBase):
|
||||
comment_autor_id: int
|
||||
solution_id: int
|
||||
|
||||
comment_autor: UserRead
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class AssessmentCreate(BaseModel):
|
||||
assessment: int = Field(...)
|
||||
class SolutionRead(SolutionAfterCreate):
|
||||
created_at: datetime
|
||||
|
||||
files: Optional[List[ReadSolutionFile]] = []
|
||||
solution_comments: Optional[List[SolutionCommentRead]] = []
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
from typing import List
|
||||
|
||||
from sqlalchemy import String, ForeignKey
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
from app.domain.models.base import RootTable
|
||||
|
||||
|
||||
@ -15,5 +12,5 @@ class SolutionComment(RootTable):
|
||||
comment_autor_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=False)
|
||||
solution_id: Mapped[int] = mapped_column(ForeignKey('solutions.id'), nullable=False)
|
||||
|
||||
comment_autor: Mapped['User'] = relationship('User', back_populates='solution_comments')
|
||||
comment_autor: Mapped['User'] = relationship('User', back_populates='solution_comments', lazy='joined')
|
||||
solution: Mapped['Solution'] = relationship('Solution', back_populates='solution_comments')
|
||||
75
api/app/infrastructure/solution_comments_service.py
Normal file
75
api/app/infrastructure/solution_comments_service.py
Normal file
@ -0,0 +1,75 @@
|
||||
from http.client import HTTPException
|
||||
from typing import Optional, List
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.application.solution_comments_repository import SolutionCommentsRepository
|
||||
from app.application.solutions_repository import SolutionsRepository
|
||||
from app.domain.entities.solutions import SolutionCommentRead, SolutionCommentCreate
|
||||
from app.domain.models import User, SolutionComment
|
||||
|
||||
|
||||
class SolutionCommentsService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.solution_comments_repository = SolutionCommentsRepository(db)
|
||||
self.solutions_repository = SolutionsRepository(db)
|
||||
|
||||
async def get_by_id(self, comment_id) -> Optional[SolutionCommentRead]:
|
||||
comment_model = await self.solution_comments_repository.get_by_id(comment_id)
|
||||
|
||||
if comment_model is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Комментарий не найден"
|
||||
)
|
||||
|
||||
return SolutionCommentRead.model_validate(comment_model)
|
||||
|
||||
async def get_by_solution_id(self, solution_id) -> Optional[List[SolutionCommentRead]]:
|
||||
solution_model = await self.solutions_repository.get_by_id(solution_id)
|
||||
|
||||
if solution_model is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Решение не найдено"
|
||||
)
|
||||
|
||||
comments = await self.solution_comments_repository.get_by_solution_id(solution_id)
|
||||
response = []
|
||||
for comment in comments:
|
||||
response.append(SolutionCommentRead.model_validate(comment))
|
||||
|
||||
return response
|
||||
|
||||
async def create(
|
||||
self, comment: SolutionCommentCreate, user: User, solution_id: int
|
||||
) -> Optional[SolutionCommentRead]:
|
||||
solution_model = await self.solutions_repository.get_by_id(solution_id)
|
||||
|
||||
if solution_model is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Решение не найдено"
|
||||
)
|
||||
|
||||
comment_model = SolutionComment(
|
||||
comment_text=comment.comment_text,
|
||||
comment_autor_id=user.id,
|
||||
solution_id=solution_model.id,
|
||||
)
|
||||
|
||||
comment_model = await self.solution_comments_repository.create(comment_model)
|
||||
|
||||
return SolutionCommentRead.model_validate(comment_model)
|
||||
|
||||
async def delete(self, comment_id: int) -> None:
|
||||
comment_model = await self.solution_comments_repository.get_by_id(comment_id)
|
||||
|
||||
if comment_model is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Комментарий не найден"
|
||||
)
|
||||
|
||||
await self.solution_comments_repository.delete(comment_model)
|
||||
@ -6,6 +6,7 @@ from app.controllers.courses_router import courses_router
|
||||
from app.controllers.lessons_router import lessons_router
|
||||
from app.controllers.register_router import register_router
|
||||
from app.controllers.roles_router import roles_router
|
||||
from app.controllers.solution_comments_router import solution_comments_router
|
||||
from app.controllers.solutions_router import solution_router
|
||||
from app.controllers.statuses_router import statuses_router
|
||||
from app.controllers.tasks_router import tasks_router
|
||||
@ -30,6 +31,7 @@ def start_app():
|
||||
api_app.include_router(lessons_router, prefix=f'{settings.prefix}/lessons', tags=['lessons'])
|
||||
api_app.include_router(register_router, prefix=f'{settings.prefix}/register', tags=['register'])
|
||||
api_app.include_router(roles_router, prefix=f'{settings.prefix}/roles', tags=['roles'])
|
||||
api_app.include_router(solution_comments_router, prefix=f'{settings.prefix}/comments', tags=['comments'])
|
||||
api_app.include_router(solution_router, prefix=f'{settings.prefix}/solutions', tags=['solutions'])
|
||||
api_app.include_router(statuses_router, prefix=f'{settings.prefix}/statuses', tags=['statuses'])
|
||||
api_app.include_router(tasks_router, prefix=f'{settings.prefix}/tasks', tags=['tasks'])
|
||||
|
||||
39
web/src/Api/commentsApi.js
Normal file
39
web/src/Api/commentsApi.js
Normal file
@ -0,0 +1,39 @@
|
||||
import {createApi} from "@reduxjs/toolkit/query/react";
|
||||
import {baseQueryWithAuth} from "./baseQuery.js";
|
||||
|
||||
|
||||
export const commentsApi = createApi({
|
||||
reducerPath: "commentsApi",
|
||||
baseQuery: baseQueryWithAuth,
|
||||
tagTypes: ["comments"],
|
||||
endpoints: (builder) => ({
|
||||
getAllCommentsBySolutionId: builder.query({
|
||||
query: (solutionId) => ({
|
||||
url: `/comments/solution/${solutionId}/`,
|
||||
method: "GET"
|
||||
}),
|
||||
providesTags: ["comments"],
|
||||
}),
|
||||
createComment: builder.mutation({
|
||||
query: ({solutionId, comment}) => ({
|
||||
url: `/comments/solution/${solutionId}/`,
|
||||
method: "POST",
|
||||
body: comment,
|
||||
}),
|
||||
invalidatesTags: ["comments"],
|
||||
}),
|
||||
deleteComment: builder.mutation({
|
||||
query: (commentId) => ({
|
||||
url: `/comments/${commentId}/`,
|
||||
method: "DELETE",
|
||||
}),
|
||||
invalidatesTags: ["comments"],
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
export const {
|
||||
useGetAllCommentsBySolutionIdQuery,
|
||||
useCreateCommentMutation,
|
||||
useDeleteCommentMutation,
|
||||
} = commentsApi;
|
||||
@ -5,21 +5,21 @@ import {baseQueryWithAuth} from "./baseQuery.js";
|
||||
export const solutionsApi = createApi({
|
||||
reducerPath: "solutionsApi",
|
||||
baseQuery: baseQueryWithAuth,
|
||||
tagTypes: ["lesson"],
|
||||
tagTypes: ["solution"],
|
||||
endpoints: (builder) => ({
|
||||
getTaskSolutions: builder.query({
|
||||
query: (taskId) => ({
|
||||
url: `/solutions/task/${taskId}/`,
|
||||
method: "GET"
|
||||
}),
|
||||
providesTags: ["lesson"],
|
||||
providesTags: ["solution"],
|
||||
}),
|
||||
getTaskStudentSolutions: builder.query({
|
||||
query: ({taskId, studentId}) => ({
|
||||
url: `/solutions/task/${taskId}/student/${studentId}/`,
|
||||
method: "GET"
|
||||
}),
|
||||
providesTags: ["lesson"],
|
||||
providesTags: ["solution"],
|
||||
}),
|
||||
createSolution: builder.mutation({
|
||||
query: ({taskId, solution}) => ({
|
||||
@ -27,21 +27,21 @@ export const solutionsApi = createApi({
|
||||
method: "POST",
|
||||
body: solution,
|
||||
}),
|
||||
invalidatesTags: ["lesson"],
|
||||
invalidatesTags: ["solution"],
|
||||
}),
|
||||
deleteSolution: builder.mutation({
|
||||
query: (solutionId) => ({
|
||||
url: `/solutions/${solutionId}/`,
|
||||
method: "DELETE",
|
||||
}),
|
||||
invalidatesTags: ["lesson"],
|
||||
invalidatesTags: ["solution"],
|
||||
}),
|
||||
getSolutionFilesList: builder.query({
|
||||
query: (solutionId) => ({
|
||||
url: `/solutions/files/${solutionId}/`,
|
||||
method: "GET"
|
||||
}),
|
||||
providesTags: ["lesson"],
|
||||
providesTags: ["solution"],
|
||||
}),
|
||||
uploadFile: builder.mutation({
|
||||
query: ({solutionId, fileData}) => {
|
||||
@ -57,7 +57,7 @@ export const solutionsApi = createApi({
|
||||
body: formData,
|
||||
};
|
||||
},
|
||||
invalidatesTags: ["task"],
|
||||
invalidatesTags: ["solution"],
|
||||
}),
|
||||
createAssessment: builder.mutation({
|
||||
query: ({solutionId, assessment}) => ({
|
||||
@ -65,7 +65,7 @@ export const solutionsApi = createApi({
|
||||
method: "POST",
|
||||
body: assessment,
|
||||
}),
|
||||
invalidatesTags: ["lesson"],
|
||||
invalidatesTags: ["solution"],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
Col, Collapse,
|
||||
Divider,
|
||||
Empty, Flex,
|
||||
Form, Input, InputNumber,
|
||||
Form, Input, InputNumber, List,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Row,
|
||||
@ -51,6 +51,8 @@ const ViewTaskModal = () => {
|
||||
allSolutions,
|
||||
onAssessmentFinish,
|
||||
assessmentForm,
|
||||
onCommentSubmit,
|
||||
commentForm
|
||||
} = useViewTaskModal();
|
||||
|
||||
return (
|
||||
@ -228,6 +230,91 @@ const ViewTaskModal = () => {
|
||||
) : (
|
||||
<Text type="secondary">Файлы не прикреплены</Text>
|
||||
)}
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<Title level={4}>Комментарии к решению</Title>
|
||||
|
||||
<div
|
||||
style={{
|
||||
maxHeight: 400,
|
||||
overflowY: "auto",
|
||||
padding: "8px 0",
|
||||
border: "1px solid #f0f0f0",
|
||||
borderRadius: 8,
|
||||
background: "#fafafa",
|
||||
}}
|
||||
>
|
||||
{solution.solution_comments && solution.solution_comments.length > 0 ? (
|
||||
<List
|
||||
dataSource={solution.solution_comments}
|
||||
renderItem={(comment) => (
|
||||
<List.Item style={{ padding: "12px 16px", borderBottom: "1px solid #f0f0f0" }}>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar style={{ backgroundColor: "#1890ff" }}>
|
||||
{comment.comment_autor.first_name[0]}
|
||||
{comment.comment_autor.last_name[0]}
|
||||
</Avatar>
|
||||
}
|
||||
title={
|
||||
<Space>
|
||||
<Text strong>
|
||||
{comment.comment_autor.first_name} {comment.comment_autor.last_name}
|
||||
</Text>
|
||||
{comment.comment_autor.role?.title === "teacher" && (
|
||||
<Tag color="gold" size="small">Преподаватель</Tag>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{new Date(comment.created_at || Date.now()).toLocaleString("ru-RU")}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 8,
|
||||
paddingLeft: 56,
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: comment.comment_text}}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="Пока нет комментариев"
|
||||
style={{ margin: "20px 0" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Form
|
||||
onFinish={(values) => onCommentSubmit(solution.id, values.comment)}
|
||||
style={{ marginTop: 16 }}
|
||||
form={commentForm}
|
||||
>
|
||||
<Form.Item
|
||||
name="comment"
|
||||
rules={[{ required: true, message: "Напишите комментарий" }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
placeholder="Напишите комментарий к решению..."
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Отправить комментарий
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Panel>
|
||||
))}
|
||||
</Collapse>
|
||||
@ -336,7 +423,7 @@ const ViewTaskModal = () => {
|
||||
<Text type="secondary">Файлы не прикреплены</Text>
|
||||
)}
|
||||
<Title level={3}>Оценка</Title>
|
||||
<Form form={assessmentForm} name={"assessmentForm"} onFinish={() => {
|
||||
<Form form={assessmentForm} onFinish={() => {
|
||||
onAssessmentFinish(solution.id)
|
||||
}}>
|
||||
<Form.Item
|
||||
@ -360,6 +447,91 @@ const ViewTaskModal = () => {
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<Title level={4}>Комментарии к решению</Title>
|
||||
|
||||
<div
|
||||
style={{
|
||||
maxHeight: 400,
|
||||
overflowY: "auto",
|
||||
padding: "8px 0",
|
||||
border: "1px solid #f0f0f0",
|
||||
borderRadius: 8,
|
||||
background: "#fafafa",
|
||||
}}
|
||||
>
|
||||
{solution.solution_comments && solution.solution_comments.length > 0 ? (
|
||||
<List
|
||||
dataSource={solution.solution_comments}
|
||||
renderItem={(comment) => (
|
||||
<List.Item style={{ padding: "12px 16px", borderBottom: "1px solid #f0f0f0" }}>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar style={{ backgroundColor: "#1890ff" }}>
|
||||
{comment.comment_autor.first_name[0]}
|
||||
{comment.comment_autor.last_name[0]}
|
||||
</Avatar>
|
||||
}
|
||||
title={
|
||||
<Space>
|
||||
<Text strong>
|
||||
{comment.comment_autor.first_name} {comment.comment_autor.last_name}
|
||||
</Text>
|
||||
{comment.comment_autor.role?.title === "teacher" && (
|
||||
<Tag color="gold" size="small">Преподаватель</Tag>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{new Date(comment.created_at || Date.now()).toLocaleString("ru-RU")}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 8,
|
||||
paddingLeft: 56,
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: comment.comment_text}}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="Пока нет комментариев"
|
||||
style={{ margin: "20px 0" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Form
|
||||
onFinish={(values) => onCommentSubmit(solution.id, values.comment)}
|
||||
style={{ marginTop: 16 }}
|
||||
form={commentForm}
|
||||
>
|
||||
<Form.Item
|
||||
name="comment"
|
||||
rules={[{ required: true, message: "Напишите комментарий" }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={3}
|
||||
placeholder="Напишите комментарий к решению..."
|
||||
allowClear
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Отправить комментарий
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Panel>
|
||||
))}
|
||||
</Collapse>
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
useUploadFileMutation
|
||||
} from "../../../../../Api/solutionsApi.js";
|
||||
import {ROLES} from "../../../../../Core/constants.js";
|
||||
import {useCreateCommentMutation} from "../../../../../Api/commentsApi.js";
|
||||
|
||||
|
||||
const useViewTaskModal = () => {
|
||||
@ -37,6 +38,34 @@ const useViewTaskModal = () => {
|
||||
const [uploadFile] = useUploadFileMutation();
|
||||
const [deleteSolution] = useDeleteSolutionMutation();
|
||||
|
||||
const [createComment, {
|
||||
isLoading: isCreatingComment,
|
||||
isError: isErrorCreatingComment
|
||||
}] = useCreateCommentMutation();
|
||||
|
||||
const onCommentSubmit = async (solutionId, commentText) => {
|
||||
if (!commentText?.trim()) return;
|
||||
|
||||
try {
|
||||
await createComment({
|
||||
solutionId: solutionId,
|
||||
comment: {
|
||||
comment_text: commentText.trim()
|
||||
}
|
||||
}).unwrap();
|
||||
commentForm.resetFields();
|
||||
notification.success({
|
||||
message: "Комментарий отправлен",
|
||||
description: "Ваш комментарий успешно добавлен",
|
||||
});
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: "Ошибка",
|
||||
description: error?.data?.detail || "Не удалось отправить комментарий",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddFile = (file) => {
|
||||
const maxSize = 50 * 1024 * 1024; // 50 мегабайт
|
||||
if (file.size > maxSize) {
|
||||
@ -165,6 +194,7 @@ const useViewTaskModal = () => {
|
||||
pollingInterval: 5000,
|
||||
})
|
||||
|
||||
const [commentForm] = Form.useForm();
|
||||
const [downloadingFiles, setDownloadingFiles] = useState({});
|
||||
|
||||
const downloadFile = async (fileId, fileName) => {
|
||||
@ -356,6 +386,7 @@ const useViewTaskModal = () => {
|
||||
allSolutions,
|
||||
onAssessmentFinish,
|
||||
assessmentForm,
|
||||
onCommentSubmit,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import {coursesApi} from "../Api/coursesApi.js";
|
||||
import {lessonsApi} from "../Api/lessonsApi.js";
|
||||
import {tasksApi} from "../Api/tasksApi.js";
|
||||
import {solutionsApi} from "../Api/solutionsApi.js";
|
||||
import {commentsApi} from "../Api/commentsApi.js";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@ -35,6 +36,8 @@ export const store = configureStore({
|
||||
[tasksApi.reducerPath]: tasksApi.reducer,
|
||||
|
||||
[solutionsApi.reducerPath]: solutionsApi.reducer,
|
||||
|
||||
[commentsApi.reducerPath]: commentsApi.reducer,
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => (
|
||||
getDefaultMiddleware().concat(
|
||||
@ -46,6 +49,7 @@ export const store = configureStore({
|
||||
lessonsApi.middleware,
|
||||
tasksApi.middleware,
|
||||
solutionsApi.middleware,
|
||||
commentsApi.middleware,
|
||||
)
|
||||
),
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user