сделал просмотр задачи
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 UpdateLessonModalForm from "./Components/UpdateLessonModalForm/UpdateLessonModalForm.jsx";
|
||||||
import CreateTaskModalForm from "./Components/CreateTaskModalForm/CreateTaskModalForm.jsx";
|
import CreateTaskModalForm from "./Components/CreateTaskModalForm/CreateTaskModalForm.jsx";
|
||||||
import UpdateTaskModalForm from "./Components/UpdateTaskModalForm/UpdateTaskModalForm.jsx";
|
import UpdateTaskModalForm from "./Components/UpdateTaskModalForm/UpdateTaskModalForm.jsx";
|
||||||
|
import ViewTaskModal from "./Components/ViewTaskModalForm/ViewTaskModal.jsx";
|
||||||
|
|
||||||
|
|
||||||
const {Title, Text} = Typography;
|
const {Title, Text} = Typography;
|
||||||
@ -218,6 +219,9 @@ const CourseDetailPage = () => {
|
|||||||
<CreateTaskModalForm
|
<CreateTaskModalForm
|
||||||
courseId={courseId}
|
courseId={courseId}
|
||||||
/>
|
/>
|
||||||
|
<ViewTaskModal
|
||||||
|
courseId={courseId}
|
||||||
|
/>
|
||||||
<UpdateTaskModalForm/>
|
<UpdateTaskModalForm/>
|
||||||
{[CONFIG.ROOT_ROLE_NAME, ROLES.TEACHER].includes(userData.role.title) && (
|
{[CONFIG.ROOT_ROLE_NAME, ROLES.TEACHER].includes(userData.role.title) && (
|
||||||
<FloatButton.Group
|
<FloatButton.Group
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user