frontAnswerer/lib/components/ViewPublicationPage/questions/Images/OwnImage.tsx
Nastya 38bd30ce35
Some checks failed
Deploy / CreateImage (push) Failing after 3m49s
Deploy / DeployService (push) Failing after 25s
держится контекст о выбранной картинке у Images
2025-06-20 20:07:46 +03:00

186 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Box, ButtonBase, IconButton, Typography, useTheme } from "@mui/material";
import { useState, useRef } from "react";
import CloseIcon from "@mui/icons-material/Close";
import { useTranslation } from "react-i18next";
import { useQuizStore } from "@/stores/useQuizStore";
import { useQuizViewStore } from "@/stores/quizView";
import { useSnackbar } from "notistack";
import { Skeleton } from "@mui/material";
import UploadIcon from "@/assets/icons/UploadIcon";
import { sendFile } from "@/api/quizRelase";
import { ACCEPT_SEND_FILE_TYPES_MAP, MAX_FILE_SIZE } from "../../tools/fileUpload";
// Пропсы компонента
export type OwnImageProps = {
imageUrl?: string;
questionId: string;
variantId: string;
onValidationError: (error: "size" | "type") => void;
};
export const OwnImage = ({ imageUrl, questionId, variantId, onValidationError }: OwnImageProps) => {
const theme = useTheme();
const { t } = useTranslation();
const { quizId, preview } = useQuizStore();
const { ownVariants, updateOwnVariant } = useQuizViewStore((state) => state);
const { enqueueSnackbar } = useSnackbar();
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [isUploading, setIsUploading] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
// Получаем ownVariant для этого варианта
const ownVariantData = ownVariants.find((v) => v.id === variantId);
// Загрузка файла
const uploadImage = async (file: File) => {
if (isUploading) return;
if (!file) return;
if (file.size > MAX_FILE_SIZE) {
onValidationError("size");
return;
}
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP.picture.some((fileType) =>
file.name.toLowerCase().endsWith(fileType)
);
if (!isFileTypeAccepted) {
onValidationError("type");
return;
}
setIsUploading(true);
try {
const data = await sendFile({
questionId,
body: { file, name: file.name, preview },
qid: quizId,
});
const fileId = data?.data.fileIDMap[questionId];
const localImageUrl = URL.createObjectURL(file);
updateOwnVariant(variantId, "", "", fileId, localImageUrl);
setSelectedFile(file);
} catch (error) {
console.error("Error uploading image:", error);
enqueueSnackbar(t("The answer was not counted"));
} finally {
setIsUploading(false);
}
};
// Обработчик выбора файла
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
uploadImage(file);
}
};
// Открытие диалога выбора файла
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
if (fileInputRef.current) fileInputRef.current.value = "";
fileInputRef.current?.click();
};
// Удаление изображения
const handleRemoveImage = (e: React.MouseEvent) => {
e.stopPropagation();
setSelectedFile(null);
updateOwnVariant(variantId, "", "", "", "");
};
// Определяем, что показывать
let imageToDisplay: string | null = null;
if (selectedFile) {
imageToDisplay = URL.createObjectURL(selectedFile);
} else if (ownVariantData?.variant.localImageUrl) {
imageToDisplay = ownVariantData.variant.localImageUrl;
} else if (imageUrl) {
imageToDisplay = imageUrl;
}
if (isUploading) {
return (
<Skeleton
variant="rounded"
sx={{ width: "100%", height: "100%", borderRadius: "12px" }}
/>
);
}
return (
<ButtonBase
component="div"
onClick={handleClick}
disabled={isUploading}
sx={{
width: "100%",
height: "100%",
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "12px",
transition: "border-color 0.3s, background-color 0.3s",
overflow: "hidden",
position: "relative",
opacity: isUploading ? 0.7 : 1,
}}
>
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
accept={ACCEPT_SEND_FILE_TYPES_MAP.picture.join(",")}
hidden
/>
{imageToDisplay ? (
<>
<Box sx={{ width: "100%", height: "100%", position: "relative" }}>
<img
src={imageToDisplay}
alt="Preview"
style={{ width: "100%", height: "100%", objectFit: "cover" }}
/>
</Box>
<IconButton
onClick={handleRemoveImage}
sx={{
position: "absolute",
top: 8,
right: 8,
zIndex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
color: "white",
height: "25px",
width: "25px",
"&:hover": {
backgroundColor: "rgba(0, 0, 0, 0.7)",
},
}}
>
<CloseIcon />
</IconButton>
</>
) : (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
opacity: 0.5,
}}
>
<UploadIcon />
<Typography
variant="body2"
color="text.secondary"
sx={{ p: 2, textAlign: "center" }}
>
добавьте свою картинку
</Typography>
</Box>
)}
</ButtonBase>
);
};