Skip to content

Commit

Permalink
feat(ui-commons): create UploadFileButton
Browse files Browse the repository at this point in the history
  • Loading branch information
skamril committed Sep 16, 2024
1 parent 82db033 commit 5a0946a
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
2 changes: 2 additions & 0 deletions webapp/public/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"global.import": "Import",
"global.import.fromFile": "From a file",
"global.import.fromDatabase": "From database",
"global.import.success": "Import successful",
"global.import.error": "Import failed",
"global.launch": "Launch",
"global.jobs": "Jobs",
"global.unknown": "Unknown",
Expand Down
2 changes: 2 additions & 0 deletions webapp/public/locales/fr/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
"global.import": "Importer",
"global.import.fromFile": "Depuis un fichier",
"global.import.fromDatabase": "Depuis la base de donnée",
"global.import.success": "Importation réussie",
"global.import.error": "Échec de l'importation",
"global.launch": "Lancer",
"global.jobs": "Tâches",
"global.unknown": "Inconnu",
Expand Down
112 changes: 112 additions & 0 deletions webapp/src/components/common/buttons/UploadFileButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { LoadingButton } from "@mui/lab";
import FileDownloadIcon from "@mui/icons-material/FileDownload";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import useEnqueueErrorSnackbar from "../../../hooks/useEnqueueErrorSnackbar";
import { toError } from "../../../utils/fnUtils";
import { Accept, useDropzone } from "react-dropzone";
import { StudyMetadata } from "../../../common/types";
import { useSnackbar } from "notistack";
import { importFile } from "../../../services/api/studies/raw";

type ValidateResult = boolean | null | undefined;
type Validate = (file: File) => ValidateResult | Promise<ValidateResult>;

export interface UploadFileButtonProps {
studyId: StudyMetadata["id"];
path: string | ((file: File) => string);
children?: React.ReactNode;
accept?: Accept;
disabled?: boolean;
onUploadSuccessful?: (file: File) => void;
validate?: Validate;
}

function UploadFileButton(props: UploadFileButtonProps) {
const { t } = useTranslation();
const {
studyId,
path,
accept,
disabled,
onUploadSuccessful,
children: label = t("global.import"),
} = props;

const enqueueErrorSnackbar = useEnqueueErrorSnackbar();
const { enqueueSnackbar } = useSnackbar();
const [isUploading, setIsUploading] = useState(false);
const { open } = useDropzone({ onDropAccepted: handleDropAccepted, accept });

// Prevent the user from accidentally leaving the page while uploading
useEffect(() => {
if (isUploading) {
const listener = (e: BeforeUnloadEvent) => {
// eslint-disable-next-line no-param-reassign
e.returnValue = t("global.import");
};

window.addEventListener("beforeunload", listener);

return () => {
window.removeEventListener("beforeunload", listener);
};
}
}, [isUploading, t]);

////////////////////////////////////////////////////////////////
// Event Handlers
////////////////////////////////////////////////////////////////

async function handleDropAccepted(acceptedFiles: File[]) {
setIsUploading(true);

const fileToUpload = acceptedFiles[0];

try {
const isValid = (await props.validate?.(fileToUpload)) ?? true;

if (!isValid) {
return;
}

const filePath = typeof path === "function" ? path(fileToUpload) : path;

await importFile({
studyId,
path: filePath,
file: fileToUpload,
createMissing: true,
});

enqueueSnackbar(t("global.import.success"), { variant: "success" });
} catch (err) {
enqueueErrorSnackbar(t("global.import.error"), toError(err));
return;
} finally {
setIsUploading(false);
}

onUploadSuccessful?.(fileToUpload);
}

////////////////////////////////////////////////////////////////
// JSX
////////////////////////////////////////////////////////////////

return (
<LoadingButton
variant="outlined"
size="small"
onClick={open}
startIcon={<FileDownloadIcon />}
loadingPosition="start"
loading={isUploading}
disabled={disabled}
>
{label}
</LoadingButton>
);
}

export default UploadFileButton;

0 comments on commit 5a0946a

Please sign in to comment.