Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 93 additions & 10 deletions frontend/src/FileManager/Actions/UploadFile/UploadFile.action.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useRef, useState } from "react";
import PropTypes from "prop-types";
import Button from "../../../components/Button/Button";
import { AiOutlineCloudUpload } from "react-icons/ai";
import UploadItem from "./UploadItem";
import Loader from "../../../components/Loader/Loader";
import OverwriteConfirmDialog from "../../../components/OverwriteConfirmDialog/OverwriteConfirmDialog";
import { useFileNavigation } from "../../../contexts/FileNavigationContext";
import { getFileExtension } from "../../../utils/getFileExtension";
import { getDataSize } from "../../../utils/getDataSize";
Expand All @@ -20,6 +22,9 @@ const UploadFileAction = ({
const [files, setFiles] = useState([]);
const [isDragging, setIsDragging] = useState(false);
const [isUploading, setIsUploading] = useState({});
const [showOverwriteDialog, setShowOverwriteDialog] = useState(false);
const [pendingFile, setPendingFile] = useState(null);
const [pendingFilesCallback, setPendingFilesCallback] = useState(null);
const { currentFolder, currentPathFiles } = useFileNavigation();
const { onError } = useFiles();
const fileInputRef = useRef(null);
Expand All @@ -41,31 +46,93 @@ const UploadFileAction = ({
const fileExists = currentPathFiles.some(
(item) => item.name.toLowerCase() === file.name.toLowerCase() && !item.isDirectory
);
if (fileExists) return t("fileAlreadyExist");
if (fileExists) {
const allowOverwrite = fileUploadConfig?.allowOverwrite || false;
if (!allowOverwrite) return t("fileAlreadyExist");
// If allowOverwrite is true, return special marker to trigger dialog
return "CONFIRM_OVERWRITE";
}

const sizeError = maxFileSize && file.size > maxFileSize;
if (sizeError) return `${t("maxUploadSize")} ${getDataSize(maxFileSize, 0)}.`;
};

const setSelectedFiles = (selectedFiles) => {
selectedFiles = selectedFiles.filter(
(item) =>
!files.some((fileData) => fileData.file.name.toLowerCase() === item.name.toLowerCase())
);
const handleOverwriteConfirm = (selectedFiles) => {
const newFiles = selectedFiles.map((file) => {
const appendData = onFileUploading(file, currentFolder);
return {
file: file,
appendData: appendData,
};
});
setFiles((prev) => [...prev, ...newFiles]);
setShowOverwriteDialog(false);
setPendingFile(null);
};

if (selectedFiles.length > 0) {
const handleOverwriteCancel = () => {
const selectedFiles = pendingFilesCallback;
if (selectedFiles) {
const newFiles = selectedFiles.map((file) => {
const appendData = onFileUploading(file, currentFolder);
const error = checkFileError(file);
error && onError({ type: "upload", message: error }, file);
onError({ type: "upload", message: t("uploadCanceled") }, file);
return {
file: file,
appendData: appendData,
...(error && { error: error }),
error: t("uploadCanceled"),
};
});
setFiles((prev) => [...prev, ...newFiles]);
}
setShowOverwriteDialog(false);
setPendingFile(null);
setPendingFilesCallback(null);
};

const setSelectedFiles = (selectedFiles) => {
selectedFiles = selectedFiles.filter(
(item) =>
!files.some((fileData) => fileData.file.name.toLowerCase() === item.name.toLowerCase())
);

if (selectedFiles.length > 0) {
const filesNeedingConfirmation = [];
const filesWithoutErrors = [];

selectedFiles.forEach((file) => {
const error = checkFileError(file);
if (error === "CONFIRM_OVERWRITE") {
filesNeedingConfirmation.push(file);
} else if (!error) {
filesWithoutErrors.push(file);
} else {
// File has other errors, add with error state
const appendData = onFileUploading(file, currentFolder);
onError({ type: "upload", message: error }, file);
filesWithoutErrors.push({ file, appendData, error });
}
});

// Add files without errors immediately
if (filesWithoutErrors.length > 0) {
const newFiles = filesWithoutErrors.map((fileData) => {
const appendData = onFileUploading(fileData.file || fileData, currentFolder);
return {
file: fileData.file || fileData,
appendData: appendData,
...(fileData.error && { error: fileData.error }),
};
});
setFiles((prev) => [...prev, ...newFiles]);
}

// Show confirmation dialog if there are files needing confirmation
if (filesNeedingConfirmation.length > 0) {
setPendingFile(filesNeedingConfirmation[0]);
setPendingFilesCallback(filesNeedingConfirmation);
setShowOverwriteDialog(true);
}
}
};

// Todo: Also validate allowed file extensions on drop
Expand Down Expand Up @@ -102,6 +169,14 @@ const UploadFileAction = ({

return (
<div className={`fm-upload-file ${files.length > 0 ? "file-selcted" : ""}`}>
<OverwriteConfirmDialog
show={showOverwriteDialog}
fileName={pendingFile?.name}
onConfirm={() =>
handleOverwriteConfirm(pendingFilesCallback)
}
onCancel={handleOverwriteCancel}
/>
<div className="select-files">
<div
className={`draggable-file-input ${isDragging ? "dragging" : ""}`}
Expand Down Expand Up @@ -162,4 +237,12 @@ const UploadFileAction = ({
);
};

UploadFileAction.propTypes = {
fileUploadConfig: PropTypes.object,
maxFileSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
acceptedFileTypes: PropTypes.string,
onFileUploading: PropTypes.func,
onFileUploaded: PropTypes.func,
};

export default UploadFileAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import PropTypes from "prop-types";
import { useTranslation } from "../../contexts/TranslationProvider";
import Button from "../Button/Button";
import "./OverwriteConfirmDialog.scss";

const OverwriteConfirmDialog = ({ show, fileName, onConfirm, onCancel }) => {
const t = useTranslation();

if (!show) return null;

return (
<div className="overwrite-confirm-overlay">
<div className="overwrite-confirm-dialog">
<div className="dialog-header">
<h2>{t("replaceExistingFile")}</h2>
</div>
<div className="dialog-content">
<p>{t("confirmOverwrite")}</p>
{fileName && <p className="filename">{fileName}</p>}
</div>
<div className="dialog-actions">
<Button type="secondary" onClick={onCancel}>
{t("cancel")}
</Button>
<Button type="primary" onClick={onConfirm}>
{t("yes")}
</Button>
</div>
</div>
</div>
);
};

OverwriteConfirmDialog.propTypes = {
show: PropTypes.bool.isRequired,
fileName: PropTypes.string,
onConfirm: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
};

export default OverwriteConfirmDialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
@import "../../styles/variables";

.overwrite-confirm-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}

.overwrite-confirm-dialog {
border-radius: 4px;
min-width: 350px;
max-width: 500px;
overflow: hidden;
}

.dialog-header {
padding: 16px 20px;
border-bottom: 1px solid $border-color;

h2 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
}

.dialog-content {
padding: 20px;

p {
margin: 0 0 12px 0;
font-size: $fm-font-size;
line-height: 1.5;
word-break: break-word;

&:last-child {
margin-bottom: 0;
}

&.filename {
font-weight: 600;
padding: 8px;
border-radius: 3px;
}
}
}

.dialog-actions {
padding: 16px 20px;
border-top: 1px solid $border-color;
display: flex;
gap: 8px;
justify-content: flex-end;
}
3 changes: 3 additions & 0 deletions frontend/src/locales/ar-SA.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "هل أنت متأكد أنك تريد حذف هذه العناصر {{count}}؟",
"percentDone": "{{percent}}% تم",
"canceled": "تم الإلغاء",
"replaceExistingFile": "استبدال الملف الموجود",
"confirmOverwrite": "يوجد ملف بهذا الاسم بالفعل. هل تريد استبداله؟",
"uploadCanceled": "تم إلغاء الرفع.",
"invalidFileName": "لا يمكن أن يحتوي اسم الملف على أي من الحروف التالية: \\ / : * ? \" < > |",
"folderExists": "هذا الموقع يحتوي بالفعل على مجلد باسم \"{{renameFile}}\".",
"collapseNavigationPane": "طي لوحة التنقل",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/da-DK.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Er du sikker på, at du vil slette disse {{count}} elementer?",
"percentDone": "{{percent}}% færdig",
"canceled": "Annulleret",
"replaceExistingFile": "Erstat eksisterende fil",
"confirmOverwrite": "En fil med dette navn eksisterer allerede. Vil du erstatte det?",
"uploadCanceled": "Upload annulleret.",
"invalidFileName": "Et filnavn må ikke indeholde følgende tegn: \\ / : * ? \" < > |",
"folderExists": "Denne destination indeholder allerede en mappe med navnet \"{{renameFile}}\".",
"collapseNavigationPane": "Skjul navigationsrude",
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
"deleteItemsConfirm": "Möchten Sie diese {{count}} Elemente wirklich löschen?",
"percentDone": "{{percent}}% erledigt",
"canceled": "Abgebrochen",
"invalidFileName": "Ein Dateiname darf keines der folgenden Zeichen enthalten: \\ / : * ? \" < > |",
"replaceExistingFile": "Vorhandene Datei ersetzen",
"confirmOverwrite": "Eine Datei mit diesem Namen existiert bereits. Möchten Sie sie ersetzen?",
"uploadCanceled": "Upload abgebrochen.", "invalidFileName": "Ein Dateiname darf keines der folgenden Zeichen enthalten: \\ / : * ? \" < > |",
"folderExists": "In diesem Zielordner gibt es bereits einen Ordner namens \"{{renameFile}}\".",
"collapseNavigationPane": "Navigationsbereich einklappen",
"expandNavigationPane": "Navigationsbereich erweitern"
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Are you sure you want to delete these {{count}} items?",
"percentDone": "{{percent}}% done",
"canceled": "Canceled",
"replaceExistingFile": "Replace Existing File",
"confirmOverwrite": "A file with this name already exists. Do you want to replace it?",
"uploadCanceled": "Upload canceled.",
"invalidFileName": "A file name can't contain any of the following characters: \\ / : * ? \" < > |",
"folderExists": "This destination already contains a folder named \"{{renameFile}}\".",
"collapseNavigationPane": "Collapse Navigation Pane",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "¿Está seguro de que desea eliminar estos {{count}} elementos?",
"percentDone": "{{percent}}% completado",
"canceled": "Cancelado",
"replaceExistingFile": "Reemplazar archivo existente",
"confirmOverwrite": "Un archivo con este nombre ya existe. ¿Desea reemplazarlo?",
"uploadCanceled": "Subida cancelada.",
"invalidFileName": "Un nombre de archivo no puede contener ninguno de los siguientes caracteres: \\ / : * ? \" < > |",
"folderExists": "Ya existe una carpeta llamada \"{{renameFile}}\" en este destino.",
"collapseNavigationPane": "Contraer panel de navegación",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/fa-IR.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "آیا مطمئن هستید که می‌خواهید این {{count}} مورد را حذف کنید؟",
"percentDone": "{{percent}}% انجام شد",
"canceled": "لغو شد",
"replaceExistingFile": "جایگزینی فایل موجود",
"confirmOverwrite": "فایلی با این نام از قبل وجود دارد. آیا می‌خواهید آن را جایگزین کنید؟",
"uploadCanceled": "بارگذاری لغو شد.",
"invalidFileName": "نام فایل نمی‌تواند شامل هیچ یک از کاراکترهای زیر باشد: \\ / : * ? \" < > |",
"folderExists": "این مقصد از قبل شامل پوشه‌ای به نام \"{{renameFile}}\" است.",
"collapseNavigationPane": "بستن پنل ناوبری",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/fi-FI.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Haluatko varmasti poistaa nämä {{count}} kohdetta?",
"percentDone": "{{percent}}% valmis",
"canceled": "Peruutettu",
"replaceExistingFile": "Korvaa olemassa oleva tiedosto",
"confirmOverwrite": "Tiedosto tällä nimellä on jo olemassa. Haluatko korvata sen?",
"uploadCanceled": "Lataus peruutettu.",
"invalidFileName": "Tiedostonimessä ei voi olla seuraavia merkkejä: \\ / : * ? \" < > |",
"folderExists": "Kohteessa on jo kansio nimeltä \"{{renameFile}}\".",
"collapseNavigationPane": "Pienennä navigointipaneeli",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Êtes-vous sûr de vouloir supprimer ces {{count}} éléments ?",
"percentDone": "{{percent}}% terminé",
"canceled": "Annulé",
"replaceExistingFile": "Remplacer le fichier existant",
"confirmOverwrite": "Un fichier portant ce nom existe déjà. Voulez-vous le remplacer ?",
"uploadCanceled": "Téléversement annulé.",
"invalidFileName": "Un nom de fichier ne peut pas contenir les caractères suivants : \\ / : * ? \" < > |",
"folderExists": "Cette destination contient déjà un dossier nommé \"{{renameFile}}\".",
"collapseNavigationPane": "Réduire le panneau de navigation",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/he-IL.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "האם אתה בטוח שברצונך למחוק את {{count}} הפריטים האלו?",
"percentDone": "{{percent}}% הושלמו",
"canceled": "בוטל",
"replaceExistingFile": "החלפת קובץ קיים",
"confirmOverwrite": "קובץ בשם זה כבר קיים. האם ברצונך להחליף אותו?",
"uploadCanceled": "ההעלאה בוטלה.",
"invalidFileName": "שם קובץ לא יכול להכיל את התווים הבאים: \\ / : * ? \" < > |",
"folderExists": "כבר קיימת תיקייה בשם \"{{renameFile}}\" במיקום זה.",
"collapseNavigationPane": "כווץ את לוח הניווט",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/hi-IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "क्या आप वाकई इन {{count}} आइटम्स को हटाना चाहते हैं?",
"percentDone": "{{percent}}% पूर्ण",
"canceled": "रद्द किया गया",
"replaceExistingFile": "मौजूदा फ़ाइल को बदलें",
"confirmOverwrite": "इस नाम की फ़ाइल पहले से मौजूद है। क्या आप इसे बदलना चाहते हैं?",
"uploadCanceled": "अपलोड रद्द किया गया।",
"invalidFileName": "फ़ाइल नाम में निम्नलिखित वर्ण नहीं हो सकते: \\ / : * ? \" < > |",
"folderExists": "इस स्थान पर \"{{renameFile}}\" नाम का एक फ़ोल्डर पहले से मौजूद है।",
"collapseNavigationPane": "नेविगेशन पैन को संकुचित करें",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Sei sicuro di voler eliminare questi {{count}} elementi?",
"percentDone": "{{percent}}% completato",
"canceled": "Annullato",
"replaceExistingFile": "Sostituisci file esistente",
"confirmOverwrite": "Un file con questo nome esiste già. Desideri sostituirlo?",
"uploadCanceled": "Caricamento annullato.",
"invalidFileName": "Un nome di file non può contenere nessuno dei seguenti caratteri: \\ / : * ? \" < > |",
"folderExists": "Questa destinazione contiene già una cartella chiamata \"{{renameFile}}\".",
"collapseNavigationPane": "Comprimi pannello di navigazione",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "{{count}} 件のアイテムを削除してもよろしいですか?",
"percentDone": "{{percent}}% 完了",
"canceled": "キャンセルしました",
"replaceExistingFile": "既存ファイルを置き換える",
"confirmOverwrite": "このファイル名は既に存在します。置き換えますか?",
"uploadCanceled": "アップロードが中止されました。",
"invalidFileName": "ファイル名には以下の文字を含めることはできません:\\ / : * ? \" < > |",
"folderExists": "この宛先には \"{{renameFile}}\" という名前のフォルダーがすでに存在します。",
"collapseNavigationPane": "ナビゲーションペインを折りたたむ",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "{{count}}개의 항목을 삭제하시겠습니까?",
"percentDone": "{{percent}}% 완료",
"canceled": "취소됨",
"replaceExistingFile": "기존 파일 바꾸기",
"confirmOverwrite": "이 이름의 파일이 이미 있습니다. 바꾸시겠습니까?",
"uploadCanceled": "업로드가 취소되었습니다.",
"invalidFileName": "파일 이름에는 다음 문자를 사용할 수 없습니다: \\ / : * ? \" < > |",
"folderExists": "해당 위치에 \"{{renameFile}}\" 폴더가 이미 있습니다.",
"collapseNavigationPane": "탐색 창 축소",
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/locales/nb-NO.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
"deleteItemsConfirm": "Er du sikker på at du vil slette disse {{count}} elementene?",
"percentDone": "{{percent}}% ferdig",
"canceled": "Avbrutt",
"replaceExistingFile": "Erstatt eksisterende fil",
"confirmOverwrite": "En fil med dette navnet eksisterer allerede. Vil du erstatte den?",
"uploadCanceled": "Opplasting avbrutt.",
"invalidFileName": "Et filnavn kan ikke inneholde noen av følgende tegn: \\ / : * ? \" < > |",
"folderExists": "Denne destinasjonen inneholder allerede en mappe med navnet «{{renameFile}}».",
"collapseNavigationPane": "Skjul navigasjonsrute",
Expand Down
Loading