import { useCallback, useState } from "react"; import { useRouter } from "next/router"; // mobx import { useMobxStore } from "lib/mobx/store-provider"; import { observer } from "mobx-react-lite"; // react-dropzone import { useDropzone } from "react-dropzone"; // services import fileService from "services/file.service"; // hooks import useWorkspaceDetails from "hooks/use-workspace-details"; import useToast from "hooks/use-toast"; // components // ui import { Loader, Tooltip } from "components/ui"; // icons import { Plus, Trash2, X } from "lucide-react"; import { getFileIcon } from "components/icons"; // helpers import { getFileExtension } from "helpers/attachment.helper"; // types import { ICustomAttribute } from "types"; type Props = { entityId: string; issueId: string; onChange: (attributeId: string, val: string | string[] | undefined) => void; projectId: string; values: { [key: string]: string[] }; }; type FileUploadProps = { attributeDetails: ICustomAttribute; className?: string; issueId: string; projectId: string; value: string | undefined; onChange: (val: string | undefined) => void; }; const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB const UploadFile: React.FC = (props) => { const { attributeDetails, className = "", onChange, value } = props; const [isUploading, setIsUploading] = useState(false); const router = useRouter(); const { workspaceSlug } = router.query; const { workspaceDetails } = useWorkspaceDetails(); const { setToastAlert } = useToast(); const onDrop = useCallback( (acceptedFiles: File[]) => { if (!acceptedFiles[0] || !workspaceSlug) return; const extension = getFileExtension(acceptedFiles[0].name); if (!attributeDetails.extra_settings?.file_formats?.includes(`.${extension}`)) { setToastAlert({ type: "error", title: "Error!", message: `File format not accepted. Accepted file formats- ${attributeDetails.extra_settings?.file_formats?.join( ", " )}`, }); return; } const formData = new FormData(); formData.append("asset", acceptedFiles[0]); formData.append( "attributes", JSON.stringify({ name: acceptedFiles[0].name, size: acceptedFiles[0].size, }) ); setIsUploading(true); fileService .uploadFile(workspaceSlug.toString(), formData) .then((res) => { const imageUrl = res.asset; onChange(imageUrl); if (value && value !== "" && workspaceDetails) fileService.deleteFile(workspaceDetails.id, value); }) .finally(() => setIsUploading(false)); }, [ attributeDetails.extra_settings?.file_formats, onChange, setToastAlert, value, workspaceDetails, workspaceSlug, ] ); const handleRemoveFile = () => { if (!workspaceDetails || !value || value === "") return; onChange(undefined); fileService.deleteFile(workspaceDetails.id, value); }; const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({ onDrop, maxSize: MAX_FILE_SIZE, multiple: false, disabled: isUploading, }); const fileError = fileRejections.length > 0 ? `Invalid file type or size (max ${MAX_FILE_SIZE / 1024 / 1024} MB)` : null; return (
{value && value !== "" ? (
{getFileIcon(getFileExtension(value))} {value.split("/")[value.split("/").length - 1].split("-")[1]}
) : (
{isDragActive ? ( Drop here ) : fileError ? ( File error ) : isUploading ? ( Uploading... ) : ( Drag and drop files )}
)}
); }; export const CustomAttributesFileUploads: React.FC = observer((props) => { const { entityId, onChange, issueId, projectId, values } = props; const { customAttributes } = useMobxStore(); const attributes = customAttributes.entityAttributes[entityId] ?? {}; const fileUploadFields = Object.values(attributes).filter((a) => a.type === "file"); return ( <> {customAttributes.fetchEntityDetailsLoader ? ( ) : (
File uploads
{Object.entries(fileUploadFields).map(([attributeId, attribute]) => (

{attribute.display_name}

onChange(attribute.id, val)} projectId={projectId} value={values[attribute.id]?.[0] ? values[attribute.id]?.[0] : undefined} />
))}
)} ); });