исправил различные функции
This commit is contained in:
parent
7bd89686a9
commit
f4e3cbf3be
4
api/.dockerignore
Normal file
4
api/.dockerignore
Normal file
@ -0,0 +1,4 @@
|
||||
.venv
|
||||
k8s
|
||||
.idea
|
||||
.env
|
||||
18
api/app/Dockerfile
Normal file
18
api/app/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
FROM python:3.10-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libpq-dev \
|
||||
libmagic1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY req.txt .
|
||||
|
||||
RUN pip install --no-cache-dir -r req.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host=0.0.0.0", "--port=8000"]
|
||||
@ -42,6 +42,7 @@ class SolutionCommentCreate(SolutionCommentBase):
|
||||
class SolutionCommentRead(SolutionCommentBase):
|
||||
comment_autor_id: int
|
||||
solution_id: int
|
||||
created_at: datetime
|
||||
|
||||
comment_autor: UserRead
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ def start_app():
|
||||
|
||||
api_app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=['*'],
|
||||
allow_origins=['https://api.lectio.numerum.team', 'https://lectio.numerum.team', 'http://localhost:5173'],
|
||||
allow_credentials=True,
|
||||
allow_methods=['*'],
|
||||
allow_headers=['*'],
|
||||
|
||||
5
api/k8s/helm/lectio-api/Chart.yaml
Normal file
5
api/k8s/helm/lectio-api/Chart.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: lectio-api-app
|
||||
description: Lectio API project
|
||||
version: 0.1.0
|
||||
appVersion: "1.0"
|
||||
52
api/k8s/helm/lectio-api/templates/deployment.yaml
Normal file
52
api/k8s/helm/lectio-api/templates/deployment.yaml
Normal file
@ -0,0 +1,52 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Chart.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Chart.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Chart.Name }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.service.port }}
|
||||
env:
|
||||
- name: SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: lectio-api-secret
|
||||
key: SECRET_KEY
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: lectio-api-secret
|
||||
key: DB_PASSWORD
|
||||
- name: DB_DRIVER
|
||||
value: "{{ .Values.env.DB_DRIVER }}"
|
||||
- name: DB_HOST
|
||||
value: "{{ .Values.env.DB_HOST }}"
|
||||
- name: DB_PORT
|
||||
value: "{{ .Values.env.DB_PORT }}"
|
||||
- name: DB_USER
|
||||
value: "{{ .Values.env.DB_USER }}"
|
||||
- name: DB_NAME
|
||||
value: "{{ .Values.env.DB_NAME }}"
|
||||
- name: DB_SCHEMA
|
||||
value: "{{ .Values.env.DB_SCHEMA }}"
|
||||
volumeMounts:
|
||||
- name: uploads-volume
|
||||
mountPath: {{ .Values.persistence.uploads.containerPath }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: uploads-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: "{{ .Release.Name }}-uploads-pvc"
|
||||
23
api/k8s/helm/lectio-api/templates/ingress.yaml
Normal file
23
api/k8s/helm/lectio-api/templates/ingress.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: lectio-api-ingress
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: lets-encrypt
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.ingress.domain }}
|
||||
secretName: {{ .Values.ingress.secretTLSName }}
|
||||
ingressClassName: public
|
||||
rules:
|
||||
- host: {{ .Values.ingress.domain }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ .Values.ingress.path }}
|
||||
pathType: {{ .Values.ingress.pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Chart.Name }}-service
|
||||
port:
|
||||
number: {{ .Values.service.port }}
|
||||
28
api/k8s/helm/lectio-api/templates/pvc.yaml
Normal file
28
api/k8s/helm/lectio-api/templates/pvc.yaml
Normal file
@ -0,0 +1,28 @@
|
||||
{{- if .Values.persistence.uploads.enabled }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-uploads-pv
|
||||
spec:
|
||||
capacity:
|
||||
storage: {{ .Values.persistence.uploads.size }}
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: microk8s-hostpath
|
||||
hostPath:
|
||||
path: {{ .Values.persistence.path }}/uploads
|
||||
type: DirectoryOrCreate
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-uploads-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.uploads.size }}
|
||||
volumeName: {{ .Release.Name }}-uploads-pv
|
||||
{{- end }}
|
||||
11
api/k8s/helm/lectio-api/templates/service.yaml
Normal file
11
api/k8s/helm/lectio-api/templates/service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Chart.Name }}-service
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.port }}
|
||||
selector:
|
||||
app: {{ .Chart.Name }}
|
||||
38
api/k8s/helm/lectio-api/values.yaml
Normal file
38
api/k8s/helm/lectio-api/values.yaml
Normal file
@ -0,0 +1,38 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: andreiduvakin/lectio-api
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8000
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 128Mi
|
||||
cpu: 200m
|
||||
|
||||
persistence:
|
||||
path: /mnt/k8s_storage/lectio-api
|
||||
uploads:
|
||||
enabled: true
|
||||
size: 7Gi
|
||||
containerPath: /app/uploads
|
||||
|
||||
ingress:
|
||||
secretTLSName: lectio-api-tls-secret
|
||||
|
||||
domain: api.lectio.numerum.team
|
||||
|
||||
path: /
|
||||
pathType: Prefix
|
||||
|
||||
env:
|
||||
DB_DRIVER: postgresql+asyncpg
|
||||
DB_HOST: db.numerum.team
|
||||
DB_PORT: 30000
|
||||
DB_USER: lectio
|
||||
DB_NAME: lectio
|
||||
DB_SCHEMA: public
|
||||
@ -8,3 +8,5 @@ pyjwt==2.9.0
|
||||
fastapi==0.115.0
|
||||
pydantic[email]==2.11.4
|
||||
aiofiles==25.1.0
|
||||
uvicorn==0.34.0
|
||||
python-multipart==0.0.12
|
||||
7
web/.dockerignore
Normal file
7
web/.dockerignore
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
build
|
||||
.dockerignore
|
||||
.git
|
||||
k8s
|
||||
.env
|
||||
27
web/Dockerfile
Normal file
27
web/Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
ARG VITE_BASE_URL
|
||||
ENV VITE_BASE_URL=https://api.lectio.numerum.team/api/v1
|
||||
ARG VITE_ROOT_ROLE_NAME
|
||||
ENV VITE_ROOT_ROLE_NAME=root
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install -g serve
|
||||
|
||||
COPY --from=builder /dist /app
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["serve", "-s", ".", "-l", "3000"]
|
||||
5
web/k8s/helm/lectio-web/Chart.yaml
Normal file
5
web/k8s/helm/lectio-web/Chart.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
apiVersion: v2
|
||||
name: lectio-web-app
|
||||
description: Lectio WEB project
|
||||
version: 0.1.0
|
||||
appVersion: "1.0"
|
||||
22
web/k8s/helm/lectio-web/templates/deployment.yaml
Normal file
22
web/k8s/helm/lectio-web/templates/deployment.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Chart.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Chart.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Chart.Name }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.service.port }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
23
web/k8s/helm/lectio-web/templates/ingress.yaml
Normal file
23
web/k8s/helm/lectio-web/templates/ingress.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: visus-api-ingress
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: lets-encrypt
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.ingress.domain }}
|
||||
secretName: {{ .Values.ingress.secretTLSName }}
|
||||
ingressClassName: public
|
||||
rules:
|
||||
- host: {{ .Values.ingress.domain }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ .Values.ingress.path }}
|
||||
pathType: {{ .Values.ingress.pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Chart.Name }}-service
|
||||
port:
|
||||
number: {{ .Values.service.port }}
|
||||
11
web/k8s/helm/lectio-web/templates/service.yaml
Normal file
11
web/k8s/helm/lectio-web/templates/service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Chart.Name }}-service
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.port }}
|
||||
selector:
|
||||
app: {{ .Chart.Name }}
|
||||
23
web/k8s/helm/lectio-web/values.yaml
Normal file
23
web/k8s/helm/lectio-web/values.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: andreiduvakin/lectio-web
|
||||
tag: latest
|
||||
pullPolicy: Always
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3000
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 128Mi
|
||||
cpu: 200m
|
||||
|
||||
ingress:
|
||||
secretTLSName: lectio-web-tls-secret
|
||||
|
||||
domain: lectio.numerum.team
|
||||
|
||||
path: /
|
||||
pathType: Prefix
|
||||
@ -8,6 +8,7 @@ import ProfilePage from "../Components/Pages/ProfilePage/ProfilePage.jsx";
|
||||
import AdminPage from "../Components/Pages/AdminPage/AdminPage.jsx";
|
||||
import CourseDetailPage from "../Components/Pages/CourseDetailPage/CourseDetailPage.jsx";
|
||||
import CoursesPage from "../Components/Pages/CoursesPage/CoursesPage.jsx";
|
||||
import GradebookPage from "../Components/Pages/GradebookPage/GradebookPage.jsx";
|
||||
|
||||
|
||||
const AppRouter = () => (
|
||||
@ -20,6 +21,7 @@ const AppRouter = () => (
|
||||
<Route path={"/courses"} element={<CoursesPage/>}/>
|
||||
<Route path={"/profile"} element={<ProfilePage/>}/>
|
||||
<Route path="/courses/:courseId" element={<CourseDetailPage />} />
|
||||
<Route path="/courses/:courseId/gradebook" element={<GradebookPage />} />
|
||||
<Route path={"*"} element={<Navigate to={"/courses"}/>}/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
@ -52,7 +52,9 @@ const ViewTaskModal = () => {
|
||||
onAssessmentFinish,
|
||||
assessmentForm,
|
||||
onCommentSubmit,
|
||||
commentForm
|
||||
commentForm,
|
||||
setComment,
|
||||
comment,
|
||||
} = useViewTaskModal();
|
||||
|
||||
return (
|
||||
@ -230,7 +232,7 @@ const ViewTaskModal = () => {
|
||||
) : (
|
||||
<Text type="secondary">Файлы не прикреплены</Text>
|
||||
)}
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div style={{marginTop: 32}}>
|
||||
<Title level={4}>Комментарии к решению</Title>
|
||||
|
||||
<div
|
||||
@ -247,10 +249,13 @@ const ViewTaskModal = () => {
|
||||
<List
|
||||
dataSource={solution.solution_comments}
|
||||
renderItem={(comment) => (
|
||||
<List.Item style={{ padding: "12px 16px", borderBottom: "1px solid #f0f0f0" }}>
|
||||
<List.Item style={{
|
||||
padding: "12px 16px",
|
||||
borderBottom: "1px solid #f0f0f0"
|
||||
}}>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar style={{ backgroundColor: "#1890ff" }}>
|
||||
<Avatar style={{backgroundColor: "#1890ff"}}>
|
||||
{comment.comment_autor.first_name[0]}
|
||||
{comment.comment_autor.last_name[0]}
|
||||
</Avatar>
|
||||
@ -261,12 +266,13 @@ const ViewTaskModal = () => {
|
||||
{comment.comment_autor.first_name} {comment.comment_autor.last_name}
|
||||
</Text>
|
||||
{comment.comment_autor.role?.title === "teacher" && (
|
||||
<Tag color="gold" size="small">Преподаватель</Tag>
|
||||
<Tag color="gold"
|
||||
size="small">Преподаватель</Tag>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
<Text type="secondary" style={{fontSize: 12}}>
|
||||
{new Date(comment.created_at || Date.now()).toLocaleString("ru-RU")}
|
||||
</Text>
|
||||
}
|
||||
@ -278,7 +284,7 @@ const ViewTaskModal = () => {
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: comment.comment_text}}
|
||||
dangerouslySetInnerHTML={{__html: comment.comment_text}}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
@ -287,33 +293,23 @@ const ViewTaskModal = () => {
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="Пока нет комментариев"
|
||||
style={{ margin: "20px 0" }}
|
||||
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
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
style={{ marginTop: 20, marginBottom: 20}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
<Button onClick={() => onCommentSubmit(solution.id)} type="primary"
|
||||
htmlType="submit">
|
||||
Отправить комментарий
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Panel>
|
||||
))}
|
||||
@ -447,7 +443,7 @@ const ViewTaskModal = () => {
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div style={{marginTop: 32}}>
|
||||
<Title level={4}>Комментарии к решению</Title>
|
||||
|
||||
<div
|
||||
@ -464,10 +460,13 @@ const ViewTaskModal = () => {
|
||||
<List
|
||||
dataSource={solution.solution_comments}
|
||||
renderItem={(comment) => (
|
||||
<List.Item style={{ padding: "12px 16px", borderBottom: "1px solid #f0f0f0" }}>
|
||||
<List.Item style={{
|
||||
padding: "12px 16px",
|
||||
borderBottom: "1px solid #f0f0f0"
|
||||
}}>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar style={{ backgroundColor: "#1890ff" }}>
|
||||
<Avatar style={{backgroundColor: "#1890ff"}}>
|
||||
{comment.comment_autor.first_name[0]}
|
||||
{comment.comment_autor.last_name[0]}
|
||||
</Avatar>
|
||||
@ -478,12 +477,13 @@ const ViewTaskModal = () => {
|
||||
{comment.comment_autor.first_name} {comment.comment_autor.last_name}
|
||||
</Text>
|
||||
{comment.comment_autor.role?.title === "teacher" && (
|
||||
<Tag color="gold" size="small">Преподаватель</Tag>
|
||||
<Tag color="gold"
|
||||
size="small">Преподаватель</Tag>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
description={
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
<Text type="secondary" style={{fontSize: 12}}>
|
||||
{new Date(comment.created_at || Date.now()).toLocaleString("ru-RU")}
|
||||
</Text>
|
||||
}
|
||||
@ -495,7 +495,7 @@ const ViewTaskModal = () => {
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: comment.comment_text}}
|
||||
dangerouslySetInnerHTML={{__html: comment.comment_text}}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
@ -504,33 +504,25 @@ const ViewTaskModal = () => {
|
||||
<Empty
|
||||
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
||||
description="Пока нет комментариев"
|
||||
style={{ margin: "20px 0" }}
|
||||
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
|
||||
value={comment}
|
||||
onChange={(e) => setComment(e.target.value)}
|
||||
style={{ marginTop: 20, marginBottom: 20}}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
<Button onClick={() => onCommentSubmit(solution.id)} type="primary"
|
||||
htmlType="submit">
|
||||
Отправить комментарий
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Panel>
|
||||
))}
|
||||
|
||||
@ -23,6 +23,7 @@ const useViewTaskModal = () => {
|
||||
} = useSelector((state) => state.tasks);
|
||||
|
||||
const [assessmentForm] = Form.useForm();
|
||||
const [comment, setComment] = useState("");
|
||||
|
||||
const [
|
||||
createSolution,
|
||||
@ -43,34 +44,35 @@ const useViewTaskModal = () => {
|
||||
isError: isErrorCreatingComment
|
||||
}] = useCreateCommentMutation();
|
||||
|
||||
const onCommentSubmit = async (solutionId, commentText) => {
|
||||
if (!commentText?.trim()) return;
|
||||
const onCommentSubmit = async (solutionId) => {
|
||||
if (!comment?.trim()) return;
|
||||
|
||||
try {
|
||||
await createComment({
|
||||
solutionId: solutionId,
|
||||
comment: {
|
||||
comment_text: commentText.trim()
|
||||
comment_text: comment.trim()
|
||||
}
|
||||
}).unwrap();
|
||||
commentForm.resetFields();
|
||||
setComment("");
|
||||
notification.success({
|
||||
message: "Комментарий отправлен",
|
||||
title: "Комментарий отправлен",
|
||||
description: "Ваш комментарий успешно добавлен",
|
||||
});
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: "Ошибка",
|
||||
title: "Ошибка",
|
||||
description: error?.data?.detail || "Не удалось отправить комментарий",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleAddFile = (file) => {
|
||||
const maxSize = 50 * 1024 * 1024; // 50 мегабайт
|
||||
if (file.size > maxSize) {
|
||||
notification.error({
|
||||
message: "Ошибка вставки",
|
||||
title: "Ошибка вставки",
|
||||
description: "Файл слишком большой.",
|
||||
placement: "topRight",
|
||||
});
|
||||
@ -387,6 +389,8 @@ const useViewTaskModal = () => {
|
||||
onAssessmentFinish,
|
||||
assessmentForm,
|
||||
onCommentSubmit,
|
||||
setComment,
|
||||
comment,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ const CoursesPage = () => {
|
||||
<Divider/>
|
||||
<Text type="secondary">Прогресс:</Text>
|
||||
{courseProgress[course.id] !== undefined && (
|
||||
<Progress percent={courseProgress[course.id]} size={[300, 20]}/>
|
||||
<Progress showInfo={true} status={"active"} percent={courseProgress[course.id]} size={[300, 20]}/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@ -166,6 +166,7 @@ const CoursesPage = () => {
|
||||
</Row>
|
||||
)}
|
||||
|
||||
{(isTeacher || isAdmin) && (
|
||||
<Tooltip title="Создать курс">
|
||||
<FloatButton
|
||||
icon={<PlusOutlined/>}
|
||||
@ -173,6 +174,7 @@ const CoursesPage = () => {
|
||||
type="primary"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
<CreateCourseModalForm/>
|
||||
<UpdateCourseModalForm/>
|
||||
|
||||
@ -4,4 +4,7 @@ import react from '@vitejs/plugin-react'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
build: {
|
||||
outDir: '../dist',
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user