сделал просмотр задачи
This commit is contained in:
parent
23ba0edf17
commit
1fb12bc7e4
@ -0,0 +1,114 @@
|
||||
import {Avatar, Button, Col, Divider, Modal, Popconfirm, Row, Space, Spin, Typography} from "antd";
|
||||
import {CloseOutlined, UserOutlined} from "@ant-design/icons";
|
||||
import useViewTaskModal from "./useTaskLessonModal.js";
|
||||
|
||||
|
||||
const {Title, Text, Paragraph} = Typography;
|
||||
|
||||
const ViewTaskModal = () => {
|
||||
const {
|
||||
selectedTaskToView,
|
||||
modalIsOpen,
|
||||
handleClose,
|
||||
currentTaskFiles,
|
||||
isCurrentTaskFilesLoading,
|
||||
isCurrentTaskFilesError,
|
||||
downloadFile,
|
||||
downloadingFiles
|
||||
} = useViewTaskModal();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={modalIsOpen}
|
||||
onCancel={handleClose}
|
||||
footer={null}
|
||||
width={1000}
|
||||
closeIcon={<CloseOutlined style={{fontSize: 20}}/>}
|
||||
title={null}
|
||||
centered
|
||||
destroyOnHidden
|
||||
>
|
||||
<Space align="start" style={{marginBottom: 16}}>
|
||||
<Col>
|
||||
<Title level={2} style={{margin: 0, flex: 1}}>
|
||||
{selectedTaskToView?.title}
|
||||
</Title>
|
||||
<Avatar
|
||||
size="small"
|
||||
icon={<UserOutlined/>}
|
||||
style={{backgroundColor: "#1890ff"}}
|
||||
>
|
||||
{selectedTaskToView?.creator?.first_name?.[0] || "У"}
|
||||
</Avatar>
|
||||
<Text type="secondary">
|
||||
Создал: <strong>{selectedTaskToView?.creator?.first_name} {selectedTaskToView?.creator?.last_name}</strong>
|
||||
{selectedTaskToView?.creator?.patronymic && ` ${selectedTaskToView?.creator.patronymic}`}
|
||||
</Text>
|
||||
</Col>
|
||||
|
||||
</Space>
|
||||
|
||||
{selectedTaskToView?.description && (
|
||||
<>
|
||||
<Title level={4} style={{margin: "16px 0 8px"}}>
|
||||
Описание
|
||||
</Title>
|
||||
<Paragraph type="secondary" style={{fontSize: 16, marginBottom: 24}}>
|
||||
{selectedTaskToView?.description}
|
||||
</Paragraph>
|
||||
<Divider style={{margin: "24px 0"}}/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedTaskToView?.text ? (
|
||||
<div
|
||||
className="Task-content"
|
||||
dangerouslySetInnerHTML={{__html: selectedTaskToView?.text}}
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
lineHeight: "1.7",
|
||||
color: "#333",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Paragraph italic type="secondary">
|
||||
Текстовый материал отсутствует
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
<Divider/>
|
||||
<Title level={3}>Прикрепленные файлы</Title>
|
||||
{isCurrentTaskFilesLoading ? (
|
||||
<Spin/>
|
||||
) : currentTaskFiles.length > 0 ? (
|
||||
currentTaskFiles.map((file) => (
|
||||
<Row key={file.id} align="middle" justify="space-between">
|
||||
<span>{file.filename || "Не указан"}</span>
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => downloadFile(file.id, file.filename)}
|
||||
loading={downloadingFiles[file.id] || false}
|
||||
disabled={downloadingFiles[file.id] || false}
|
||||
type={"dashed"}
|
||||
style={{marginRight: 8}}
|
||||
>
|
||||
{downloadingFiles[file.id] ? "Загрузка..." : "Скачать"}
|
||||
</Button>
|
||||
</div>
|
||||
<Divider/>
|
||||
</Row>
|
||||
))
|
||||
) : (
|
||||
<p>Файлы отсутствуют</p>
|
||||
)}
|
||||
|
||||
<div style={{textAlign: "right"}}>
|
||||
<Button onClick={handleClose}>
|
||||
Закрыть
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewTaskModal;
|
||||
@ -0,0 +1,121 @@
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
import {setSelectedTaskToView} from "../../../../../Redux/Slices/tasksSlice.js";
|
||||
import {notification} from "antd";
|
||||
import CONFIG from "../../../../../Core/сonfig.js";
|
||||
import {useState} from "react";
|
||||
import {useGetTaskFilesListQuery} from "../../../../../Api/tasksApi.js";
|
||||
|
||||
|
||||
const useViewTaskModal = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
selectedTaskToView
|
||||
} = useSelector((state) => state.tasks);
|
||||
|
||||
const modalIsOpen = selectedTaskToView !== null;
|
||||
|
||||
const handleClose = () => {
|
||||
dispatch(setSelectedTaskToView(null));
|
||||
};
|
||||
|
||||
const {
|
||||
data: currentTaskFiles = [],
|
||||
isLoading: isCurrentTaskFilesLoading,
|
||||
isError: isCurrentTaskFilesError
|
||||
} = useGetTaskFilesListQuery(selectedTaskToView?.id, {
|
||||
skip: !selectedTaskToView?.id
|
||||
});
|
||||
|
||||
const [downloadingFiles, setDownloadingFiles] = useState({});
|
||||
|
||||
const downloadFile = async (fileId, fileName) => {
|
||||
try {
|
||||
setDownloadingFiles((prev) => ({...prev, [fileId]: true}));
|
||||
|
||||
const token = localStorage.getItem('access_token');
|
||||
if (!token) {
|
||||
notification.error({
|
||||
title: "Ошибка",
|
||||
description: "Токен не найден",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${CONFIG.BASE_URL}/tasks/file/${fileId}/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
notification.error({
|
||||
title: "Ошибка",
|
||||
description: errorText || "Не удалось скачать файл",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (!contentType || contentType.includes('text/html')) {
|
||||
const errorText = await response.text();
|
||||
notification.error({
|
||||
title: "Ошибка",
|
||||
description: errorText || "Не удалось скачать файл",
|
||||
placement: "topRight",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let safeFileName = fileName || "file";
|
||||
if (!safeFileName.match(/\.[a-zA-Z0-9]+$/)) {
|
||||
if (contentType.includes('application/pdf')) {
|
||||
safeFileName += '.pdf';
|
||||
} else if (contentType.includes('image/jpeg')) {
|
||||
safeFileName += '.jpg';
|
||||
} else if (contentType.includes('image/png')) {
|
||||
safeFileName += '.png';
|
||||
} else {
|
||||
safeFileName += '.bin';
|
||||
}
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = downloadUrl;
|
||||
link.setAttribute("download", safeFileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
title: "Ошибка",
|
||||
description: error.message || "Не удалось скачать файл",
|
||||
placement: "topRight",
|
||||
});
|
||||
} finally {
|
||||
setDownloadingFiles((prev) => ({...prev, [fileId]: false}));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
selectedTaskToView,
|
||||
modalIsOpen,
|
||||
handleClose,
|
||||
currentTaskFiles,
|
||||
isCurrentTaskFilesLoading,
|
||||
isCurrentTaskFilesError,
|
||||
downloadFile,
|
||||
downloadingFiles
|
||||
}
|
||||
};
|
||||
|
||||
export default useViewTaskModal;
|
||||
@ -31,6 +31,7 @@ import ViewLessonModal from "./Components/ViewLessonModalForm/ViewLessonModal.js
|
||||
import UpdateLessonModalForm from "./Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx";
|
||||
import CreateTaskModalForm from "./Components/CreateTaskModalForm/CreateTaskModalForm.jsx";
|
||||
import UpdateTaskModalForm from "./Components/UpdateTaskModalForm/UpdateTaskModalForm.jsx";
|
||||
import ViewTaskModal from "./Components/ViewTaskModalForm/ViewTaskModal.jsx";
|
||||
|
||||
|
||||
const {Title, Text} = Typography;
|
||||
@ -218,6 +219,9 @@ const CourseDetailPage = () => {
|
||||
<CreateTaskModalForm
|
||||
courseId={courseId}
|
||||
/>
|
||||
<ViewTaskModal
|
||||
courseId={courseId}
|
||||
/>
|
||||
<UpdateTaskModalForm/>
|
||||
{[CONFIG.ROOT_ROLE_NAME, ROLES.TEACHER].includes(userData.role.title) && (
|
||||
<FloatButton.Group
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user