// Components import { Box, Stack, Typography } from "@mui/material"; import CloudUploadIcon from "@mui/icons-material/CloudUpload"; import Image from "../../../Components/Image"; import TextField from "@mui/material/TextField"; import IconButton from "@mui/material/IconButton"; import ProgressUpload from "../../ProgressBars"; import ImageIcon from "@mui/icons-material/Image"; // Utils import PropTypes from "prop-types"; import { useCallback, useState, useRef, useEffect } from "react"; import { useTheme } from "@emotion/react"; import { useTranslation } from "react-i18next"; /** * ImageUpload component allows users to upload images with drag-and-drop functionality. * It supports file size and format validation. * * @component * @param {Object} props - Component props * @param {boolean} [props.previewIsRound=false] - Determines if the image preview should be round * @param {string} [props.src] - Source URL of the image to display * @param {function} props.onChange - Callback function to handle file change, takes a file as an argument * @param {number} [props.maxSize=3145728] - Maximum file size allowed in bytes (default is 3MB) * @param {Array} [props.accept=['jpg', 'jpeg', 'png']] - Array of accepted file formats * @param {Object} [props.errors] - Object containing error messages * @returns {JSX.Element} The rendered component */ const ImageUpload = ({ previewIsRound = false, src, onChange, maxSize = 3 * 1024 * 1024, accept = ["jpg", "jpeg", "png"], error, }) => { const theme = useTheme(); const { t } = useTranslation(); const [uploadComplete, setUploadComplete] = useState(false); const [completedFile, setCompletedFile] = useState(null); const [file, setFile] = useState(null); const [progress, setProgress] = useState({ value: 0, isLoading: false }); const intervalRef = useRef(null); const [localError, setLocalError] = useState(null); const [isDragging, setIsDragging] = useState(false); const roundStyle = previewIsRound ? { borderRadius: "50%" } : {}; const handleImageChange = useCallback( (file) => { if (!file) return; const isValidType = accept.some((type) => file.type.includes(type)); const isValidSize = file.size <= maxSize; if (!isValidType) { setLocalError(t("invalidFileFormat")); return; } if (!isValidSize) { setLocalError(t("invalidFileSize")); return; } setLocalError(null); const previewFile = { src: URL.createObjectURL(file), name: file.name, file, }; setFile(previewFile); setProgress({ value: 0, isLoading: true }); intervalRef.current = setInterval(() => { setProgress((prev) => { const buffer = 12; if (prev.value + buffer >= 100) { clearInterval(intervalRef.current); setUploadComplete(true); setCompletedFile(previewFile); return { value: 100, isLoading: false }; } return { value: prev.value + buffer, isLoading: true }; }); }, 120); }, [maxSize, accept] ); useEffect(() => { if (uploadComplete && completedFile) { onChange?.(completedFile); setUploadComplete(false); setCompletedFile(null); } }, [uploadComplete, completedFile, onChange]); return ( <> {src ? ( Uploaded preview ) : ( <> setIsDragging(true)} onDragLeave={() => setIsDragging(false)} onDrop={() => setIsDragging(false)} sx={{ position: "relative", height: "fit-content", border: "dashed", borderRadius: theme.shape.borderRadius, borderColor: isDragging ? theme.palette.primary.main : theme.palette.primary.lowContrast, backgroundColor: isDragging ? "hsl(215, 87%, 51%, 0.05)" : "transparent", borderWidth: "2px", transition: "0.2s", "&:hover": { borderColor: theme.palette.primary.main, backgroundColor: "hsl(215, 87%, 51%, 0.05)", }, }} > handleImageChange(e?.target?.files?.[0])} sx={{ width: "100%", "& .MuiInputBase-input[type='file']": { opacity: 0, cursor: "pointer", maxWidth: "500px", minHeight: "175px", zIndex: 1, }, "& fieldset": { padding: 0, border: "none", }, }} /> {t("ClickUpload")} {" "} or {t("DragandDrop")} ({t("MaxSize")}: {Math.round(maxSize / 1024 / 1024)}MB) {(localError || progress.isLoading || progress.value !== 0) && ( } label={file?.name || "Upload failed"} size={file?.size} progress={progress.value} onClick={() => { clearInterval(intervalRef.current); setFile(null); setProgress({ value: 0, isLoading: false }); setLocalError(null); onChange(undefined); }} error={localError || error} /> )} {t("SupportedFormats")}: {accept.join(", ").toUpperCase()} )} ); }; ImageUpload.propTypes = { previewIsRound: PropTypes.bool, src: PropTypes.string, onChange: PropTypes.func, maxSize: PropTypes.number, accept: PropTypes.array, error: PropTypes.string, }; export default ImageUpload;