frontAnswerer/lib/components/ViewPublicationPage/questions/File/UploadFile.tsx
Nastya 12a1aab506
Some checks failed
Deploy / DeployService (push) Failing after 28s
Deploy / CreateImage (push) Has been cancelled
Обработка ошибки, когда вопросы для аудитории ещё не созданы
2025-06-12 15:30:27 +03:00

163 lines
5.1 KiB
TypeScript

import { useState, Dispatch, SetStateAction } from "react";
import { Box, ButtonBase, Skeleton, Typography, useTheme } from "@mui/material";
import { enqueueSnackbar } from "notistack";
import { sendAnswer, sendFile } from "@api/quizRelase";
import { useRootContainerSize } from "@contexts/RootContainerWidthContext";
import { useQuizViewStore } from "@stores/quizView";
import {
ACCEPT_SEND_FILE_TYPES_MAP,
MAX_FILE_SIZE,
UPLOAD_FILE_DESCRIPTIONS_MAP,
} from "@/components/ViewPublicationPage/tools/fileUpload";
import Info from "@icons/Info";
import UploadIcon from "@icons/UploadIcon";
import type { QuizQuestionFile } from "@model/questionTypes/file";
import { useQuizStore } from "@/stores/useQuizStore";
import { useTranslation } from "react-i18next";
export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null;
type UploadFileProps = {
currentQuestion: QuizQuestionFile;
setModalWarningType: Dispatch<SetStateAction<ModalWarningType>>;
isSending: boolean;
setIsSending: Dispatch<SetStateAction<boolean>>;
onFileUpload: (file: File) => Promise<void>;
isUploading: boolean;
};
export const UploadFile = ({
currentQuestion,
setModalWarningType,
isSending,
setIsSending,
onFileUpload,
isUploading,
}: UploadFileProps) => {
const { quizId, preview } = useQuizStore();
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
const theme = useTheme();
const { t } = useTranslation();
const answers = useQuizViewStore((state) => state.answers);
const { updateAnswer } = useQuizViewStore((state) => state);
const isMobile = useRootContainerSize() < 500;
const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string;
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
const fileType = file.type.split("/")[0];
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (!ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].includes(`.${fileExtension}`)) {
setModalWarningType("errorType");
return;
}
if (file.size > 50 * 1024 * 1024) {
setModalWarningType("errorSize");
return;
}
setIsSending(true);
try {
await onFileUpload(file);
} finally {
setIsSending(false);
}
};
const onDrop = async (event: React.DragEvent<HTMLLabelElement>) => {
event.preventDefault();
setIsDropzoneHighlighted(false);
const file = event.dataTransfer.files[0];
if (!file) return;
const fileType = file.type.split("/")[0];
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (!ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].includes(`.${fileExtension}`)) {
setModalWarningType("errorType");
return;
}
if (file.size > 50 * 1024 * 1024) {
setModalWarningType("errorSize");
return;
}
setIsSending(true);
try {
await onFileUpload(file);
} finally {
setIsSending(false);
}
};
return (
<Box sx={{ display: "flex", alignItems: "center" }}>
{isSending ? (
<Skeleton
variant="rounded"
sx={{ width: "100%", height: "120px", maxWidth: "560px" }}
/>
) : (
<Box
component="label"
sx={{
width: "100%",
height: "300px",
border: "2px dashed",
borderColor: isDropzoneHighlighted ? "primary.main" : "grey.300",
borderRadius: "12px",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.2s",
backgroundColor: isDropzoneHighlighted ? "action.hover" : "background.paper",
opacity: isSending || isUploading ? 0.7 : 1,
pointerEvents: isSending || isUploading ? "none" : "auto",
"&:hover": {
borderColor: "primary.main",
backgroundColor: "action.hover",
},
}}
onDragEnter={() => setIsDropzoneHighlighted(true)}
onDragLeave={() => setIsDropzoneHighlighted(false)}
onDragOver={(event) => event.preventDefault()}
onDrop={onDrop}
>
<input
onChange={handleFileChange}
hidden
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
type="file"
/>
<UploadIcon color={isDropzoneHighlighted ? "primary" : "grey"} />
<Typography
variant="body1"
color={isDropzoneHighlighted ? "primary" : "text.secondary"}
sx={{ mt: 2 }}
>
{isUploading ? t("Uploading...") : t("Drop file here or click to upload")}
</Typography>
</Box>
)}
<Info
sx={{ width: "40px", height: "40px" }}
color={theme.palette.primary.main}
onClick={() => setModalWarningType(currentQuestion.content.type)}
/>
</Box>
);
};