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
40 changes: 35 additions & 5 deletions backend/prompt_studio/prompt_studio_core_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,22 @@ def fetch_contents_ide(self, request: HttpRequest, pk: Any = None) -> Response:
file_name = (
f"{FileViewTypes.SUMMARIZE.lower()}/{filename_without_extension}.txt"
)

# For ORIGINAL view, check if a converted PDF exists for preview
if view_type == FileViewTypes.ORIGINAL and file_converter_plugin:
converted_name = f"converted/{filename_without_extension}.pdf"
try:
contents = PromptStudioFileHelper.fetch_file_contents(
file_name=converted_name,
org_id=UserSessionUtils.get_organization_id(request),
user_id=custom_tool.created_by.user_id,
tool_id=str(custom_tool.tool_id),
allowed_content_types=allowed_content_types,
)
return Response({"data": contents}, status=status.HTTP_200_OK)
except (FileNotFoundError, FileNotFound):
pass # No converted file — fall through to return original

try:
contents = PromptStudioFileHelper.fetch_file_contents(
file_name=file_name,
Expand All @@ -557,15 +573,29 @@ def upload_for_ide(self, request: HttpRequest, pk: Any = None) -> Response:
file_name = uploaded_file.name
file_data = uploaded_file
file_type = uploaded_file.content_type
# Convert non-PDF files

if file_converter_plugin and file_type != "application/pdf":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full context around line 577 in views.py
find . -name "views.py" -path "*prompt_studio_core_v2*" -type f

Repository: Zipstack/unstract

Length of output: 115


🏁 Script executed:

# Get the full context around line 577
wc -l backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 117


🏁 Script executed:

# Read the context around line 577
sed -n '550,610p' backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 3014


🏁 Script executed:

# Search for should_convert_to_pdf implementation
rg -n "should_convert_to_pdf" --type=py -B3 -A5

Repository: Zipstack/unstract

Length of output: 1108


🏁 Script executed:

# Look for file_converter plugin definition
rg -n "file_converter" --type=py -A2 | head -100

Repository: Zipstack/unstract

Length of output: 2773


🏁 Script executed:

# Check for MIME type validation or magic byte checking
rg -n "content_type|magic|sniff" --type=py | grep -v test | grep -v migration | head -50

Repository: Zipstack/unstract

Length of output: 5750


🏁 Script executed:

# Look for allowed_content_types usage
rg -n "allowed_content_types" --type=py -B2 -A2

Repository: Zipstack/unstract

Length of output: 3946


🏁 Script executed:

# Find the file_converter plugin implementation
find . -path "*file_converter*" -name "*.py" -type f | grep -v test | grep -v migration | head -20

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Check FileKey.FILE_UPLOAD_ALLOWED_MIME to understand supported formats
rg -n "FILE_UPLOAD_ALLOWED_MIME|ExtentedFileInformationKey" --type=py -A10 | head -80

Repository: Zipstack/unstract

Length of output: 4511


🏁 Script executed:

# Look for should_convert_to_pdf implementation in file_converter
find . -path "*file_converter*" -name "*.py" -type f -exec grep -l "should_convert_to_pdf" {} \;

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Check the upload_for_ide method more completely to see if there's validation
sed -n '568,620p' backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 2535


🏁 Script executed:

# Search for where file_converter plugin is loaded and its definition
rg -n "get_plugin.*file_converter" --type=py -B2 -A2

Repository: Zipstack/unstract

Length of output: 1079


🏁 Script executed:

# Check if there's any file extension or magic byte validation in upload methods
rg -n "upload_for_ide|upload_converted_for_ide" --type=py -B3 -A8 | head -60

Repository: Zipstack/unstract

Length of output: 4941


🏁 Script executed:

# Look for FileViewTypes to understand the upload logic better
rg -n "FileViewTypes" --type=py -B2 -A2

Repository: Zipstack/unstract

Length of output: 2391


🏁 Script executed:

# Check FileUploadIdeSerializer to see if there's MIME validation
rg -n "FileUploadIdeSerializer" --type=py -A15

Repository: Zipstack/unstract

Length of output: 5493


🏁 Script executed:

# Look at the file_converter plugin to understand should_convert_to_pdf behavior
find . -path "*plugins*" -name "*.py" -type f | xargs grep -l "should_convert_to_pdf" 2>/dev/null

Repository: Zipstack/unstract

Length of output: 45


🏁 Script executed:

# Check if there's any magic byte detection or file extension validation
rg -n "magic\.|mimetypes|sniff" backend/prompt_studio/prompt_studio_core_v2/ --type=py

Repository: Zipstack/unstract

Length of output: 191


🏁 Script executed:

# Get the FILE_UPLOAD_ALLOWED_EXT and details about FileKey constants
rg -n "FILE_UPLOAD_ALLOWED" --type=py -B2 -A2 | head -100

Repository: Zipstack/unstract

Length of output: 2646


🏁 Script executed:

# Check if ExtentedFileInformationKey is from a plugin
find . -path "*file_converter*" -name "constants.py" -type f

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Search for ExtentedFileInformationKey definition
rg -n "ExtentedFileInformationKey\|class.*FileInformationKey" --type=py -A15 | head -150

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Try to find ExtentedFileInformationKey in the entire codebase
rg -n "ExtentedFileInformationKey" --type=py -B5 -A15 | head -120

Repository: Zipstack/unstract

Length of output: 1953


🏁 Script executed:

# Look for plugins directory and file_converter
find . -type d -name "plugins" | head -5

Repository: Zipstack/unstract

Length of output: 138


🏁 Script executed:

# Search for get_extented_file_information_key implementation
rg -n "get_extented_file_information_key" --type=py -A8

Repository: Zipstack/unstract

Length of output: 973


🏁 Script executed:

# Look for the file_converter plugin constants
find ./backend/plugins -path "*file_converter*" -name "constants.py" -type f

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Look at the structure of the plugins directory
ls -la ./backend/plugins/ | head -30

Repository: Zipstack/unstract

Length of output: 661


🏁 Script executed:

# Search for file_converter in the plugins directory
find ./backend/plugins -path "*file_converter*" -type f -name "*.py" | head -20

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Look for file_converter in the workers plugins
find ./workers/plugins -path "*file_converter*" -type f -name "*.py" | head -20

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Search the entire codebase for file_converter plugin
rg -n "file_converter" --type=py | grep -i "constant\|mime\|extension" | head -30

Repository: Zipstack/unstract

Length of output: 183


🏁 Script executed:

# Check if file_converter is an external plugin by searching plugin references
rg -n "should_convert_to_pdf\|get_extented_file_information_key" --type=py -B3 -A10

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Look for FileValidator implementation
rg -n "class FileValidator" --type=py -A20

Repository: Zipstack/unstract

Length of output: 1521


🏁 Script executed:

# Check if there's any file extension checking alongside MIME type
rg -n "FILE_UPLOAD_ALLOWED_EXT" --type=py -B2 -A2

Repository: Zipstack/unstract

Length of output: 1787


🏁 Script executed:

# Look for any additional validation in the upload_for_ide or before line 577
sed -n '560,577p' backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 838


🏁 Script executed:

# Look at the FileValidator implementation more closely
cat -n backend/utils/FileValidator.py | head -100

Repository: Zipstack/unstract

Length of output: 4505


🏁 Script executed:

# Check what happens in should_convert_to_pdf - does it do its own MIME checking?
rg -n "process_file|should_convert_to_pdf" --type=py -A5 | head -60

Repository: Zipstack/unstract

Length of output: 4561


🏁 Script executed:

# Look for any file extension-based checks alongside MIME type checks
rg -n "\.split.*\." backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Check what FILE_UPLOAD_ALLOWED_EXT contains (look for ExtentedFileInformationKey constant definition)
rg -n "FILE_UPLOAD_ALLOWED_EXT\|FILE_UPLOAD_ALLOWED_MIME" --type=py -B3 -A3

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Verify the file extension is being checked
sed -n '192,204p' backend/prompt_studio/prompt_studio_core_v2/serializers.py

Repository: Zipstack/unstract

Length of output: 509


🏁 Script executed:

# Check if file_name contains the actual extension being used
sed -n '571,580p' backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 567


🏁 Script executed:

# Look at what happens with the file extension in the rest of the code
rg -n "file_name\|\.split\|splitext" backend/prompt_studio/prompt_studio_core_v2/views.py | head -20

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Verify what constants FileKey resolves to by checking imports and fallback
cat -n backend/prompt_studio/prompt_studio_core_v2/serializers.py | sed -n '28,34p'

Repository: Zipstack/unstract

Length of output: 321


🏁 Script executed:

# Check the FileValidator more thoroughly - especially the magic.from_buffer call
sed -n '72,85p' backend/utils/FileValidator.py

Repository: Zipstack/unstract

Length of output: 670


🏁 Script executed:

# Key insight: Check if file_type is being used for conversion decision after validation
# Let's see if should_convert_to_pdf receives the browser-supplied MIME or actual file MIME
sed -n '575,590p' backend/prompt_studio/prompt_studio_core_v2/views.py

Repository: Zipstack/unstract

Length of output: 968


🏁 Script executed:

# Search for ExtentedFileInformationKey to understand what file types are actually supported
rg -n "FILE_UPLOAD_ALLOWED" --type=py -B5 -A5 | grep -A5 -B5 "EXT\|MIME" | head -80

Repository: Zipstack/unstract

Length of output: 4765


🏁 Script executed:

# Check what attributes are available on uploaded_file after validation
rg -n "class InMemoryUploadedFile\|class UploadedFile" --type=py -A10 | head -40

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Confirm: does FileValidator's magic.from_buffer result get stored anywhere?
rg -n "magic\.from_buffer\|\.content_type" backend/utils/FileValidator.py

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Check if there's a way to get the actual detected MIME type at line 577
rg -n "file_type\|content_type" backend/prompt_studio/prompt_studio_core_v2/views.py | head -20

Repository: Zipstack/unstract

Length of output: 43


🏁 Script executed:

# Get the last few lines of FileValidator to see the full validation flow
sed -n '72,110p' backend/utils/FileValidator.py

Repository: Zipstack/unstract

Length of output: 1672


🏁 Script executed:

# Check if magic.from_buffer result is stored anywhere
grep -n "magic\|_check_file" backend/utils/FileValidator.py

Repository: Zipstack/unstract

Length of output: 481


🏁 Script executed:

# Understand the complete validation: check the __call__ method
sed -n '100,120p' backend/utils/FileValidator.py

Repository: Zipstack/unstract

Length of output: 656


file_type at line 577 relies on browser-supplied MIME, not actual file content detection.

The FileValidator class performs server-side MIME sniffing using magic.from_buffer() to validate uploads, which correctly identifies actual file types. However, after validation passes, this detected MIME type is discarded. The conversion logic at line 577 then uses uploaded_file.content_type (browser-supplied), not the server-detected MIME.

This creates an inconsistency: a CSV file with content_type="application/octet-stream" (common browser behavior) will pass validation (actual MIME detected as text/csv), but the conversion decision will use the unreliable browser value. If the file converter plugin supports multiple formats beyond PDF, the conversion-vs-direct-upload logic could behave unexpectedly. Consider either storing the detected MIME type during validation or re-detecting it before the conversion decision.

🤖 Prompt for AI Agents
In `@backend/prompt_studio/prompt_studio_core_v2/views.py` at line 577, The
conversion branch uses the browser-supplied uploaded_file.content_type
(referenced as file_type at the if with file_converter_plugin) instead of the
server-detected MIME from FileValidator, causing inconsistent behavior; fix by
persisting the server-detected MIME (e.g., add a property or return value like
detected_mime on FileValidator or its validate() result) or re-run
magic.from_buffer() in the view before the conversion decision, then use that
server-detected MIME (detected_mime) in the conditional (the same place that
checks file_converter_plugin and file_type) so conversion vs direct upload is
based on the validated content type.

file_converter_service = file_converter_plugin["service_class"]()
file_data, file_name = file_converter_service.process_file(
uploaded_file, file_name
)
if file_converter_service.should_convert_to_pdf(file_type):
# Convert and store in converted/ subdir for preview
converted_data, converted_name = file_converter_service.process_file(
uploaded_file, file_name
)
PromptStudioFileHelper.upload_converted_for_ide(
org_id=UserSessionUtils.get_organization_id(request),
user_id=custom_tool.created_by.user_id,
tool_id=str(custom_tool.tool_id),
file_name=converted_name,
file_data=converted_data,
)
# Reset uploaded_file for storing original in main dir
uploaded_file.seek(0)
file_data = uploaded_file
# else: CSV/TXT/Excel — file_data stays as original, no conversion

logger.info(f"Uploading file: {file_name}" if file_name else "Uploading file")

# Store original file in main dir (always the original)
PromptStudioFileHelper.upload_for_ide(
org_id=UserSessionUtils.get_organization_id(request),
user_id=custom_tool.created_by.user_id,
Expand All @@ -574,7 +604,7 @@ def upload_for_ide(self, request: HttpRequest, pk: Any = None) -> Response:
file_data=file_data,
)

# Create a record in the db for the file
# Create a record in the db for the file (document_name = original filename)
document = PromptStudioDocumentHelper.create(
tool_id=str(custom_tool.tool_id), document_name=file_name
)
Expand Down
47 changes: 45 additions & 2 deletions backend/utils/file_storage/helpers/prompt_studio_file_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def get_or_create_prompt_studio_subdirectory(
file_path = str(Path(base_path) / org_id / user_id / tool_id)
extract_file_path = str(Path(file_path) / "extract")
summarize_file_path = str(Path(file_path) / "summarize")
converted_file_path = str(Path(file_path) / "converted")
if is_create:
fs_instance = EnvHelper.get_storage(
storage_type=StorageType.PERMANENT,
Expand All @@ -46,6 +47,7 @@ def get_or_create_prompt_studio_subdirectory(
fs_instance.mkdir(file_path, create_parents=True)
fs_instance.mkdir(extract_file_path, create_parents=True)
fs_instance.mkdir(summarize_file_path, create_parents=True)
fs_instance.mkdir(converted_file_path, create_parents=True)
return str(file_path)

@staticmethod
Expand Down Expand Up @@ -81,6 +83,38 @@ def upload_for_ide(
data=file_data if isinstance(file_data, bytes) else file_data.read(),
)

@staticmethod
def upload_converted_for_ide(
org_id: str, user_id: str, tool_id: str, file_data: Any, file_name: str
) -> None:
"""Stores converted PDF in the converted/ subdirectory for preview.

Args:
org_id (str): Organization ID
user_id (str): User ID
tool_id (str): ID of the prompt studio tool
file_data (Any): File data (bytes or file-like object)
file_name (str): Name of the converted file
"""
fs_instance = EnvHelper.get_storage(
storage_type=StorageType.PERMANENT,
env_name=FileStorageKeys.PERMANENT_REMOTE_STORAGE,
)
file_system_path = (
PromptStudioFileHelper.get_or_create_prompt_studio_subdirectory(
org_id=org_id,
is_create=True,
user_id=user_id,
tool_id=str(tool_id),
)
)
converted_path = str(Path(file_system_path) / "converted" / file_name)
fs_instance.write(
path=converted_path,
mode="wb",
data=file_data if isinstance(file_data, bytes) else file_data.read(),
)

@staticmethod
def fetch_file_contents(
org_id: str,
Expand Down Expand Up @@ -141,13 +175,22 @@ def fetch_file_contents(
encoded_string = base64.b64encode(bytes(text_content_bytes))
return {"data": encoded_string, "mime_type": file_content_type}

elif file_content_type == "text/plain":
elif file_content_type in ("text/plain", "text/csv"):
text_content_string: str = fs_instance.read(
path=file_path,
mode="r",
legacy_storage_path=legacy_file_path,
encoding="utf-8",
)
elif file_content_type in (
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-excel.sheet.macroenabled.12",
):
text_content_string = (
"Preview not available for Excel files. "
"Please index the document and view content in the Raw View tab."
)
# Check if the file type is in the allowed list
elif file_content_type not in allowed_content_types:
raise InvalidFileType(f"File type '{file_content_type}' is not allowed.")
Expand Down Expand Up @@ -178,7 +221,7 @@ def delete_for_ide(org_id: str, user_id: str, tool_id: str, file_name: str) -> b
# Delete the source file
fs_instance.rm(str(Path(file_system_path) / file_name))
# Delete all related files for cascade delete
directories = ["extract/", "extract/metadata/", "summarize/"]
directories = ["extract/", "extract/metadata/", "summarize/", "converted/"]
base_file_name, _ = os.path.splitext(file_name)
# Delete related files
file_paths = PromptStudioFileHelper._find_files(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { ManageDocsModal } from "../manage-docs-modal/ManageDocsModal";
import { PdfViewer } from "../pdf-viewer/PdfViewer";
import { TextViewerPre } from "../text-viewer-pre/TextViewerPre";
import usePostHogEvents from "../../../hooks/usePostHogEvents";
import { TextViewer } from "../text-viewer/TextViewer";

let items = [
{
Expand Down Expand Up @@ -247,17 +246,27 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) {

const processGetDocsResponse = (data, viewType, mimeType) => {
if (viewType === viewTypes.original) {
const base64String = data || "";
const blob = base64toBlobWithMime(base64String, mimeType);
setFileData({ blob, mimeType });
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = () => {
setFileUrl(reader.result);
};
reader.onerror = () => {
throw new Error("Fail to load the file");
};
if (mimeType === "application/pdf") {
// Existing flow: base64 → blob → PdfViewer
const base64String = data || "";
const blob = base64toBlobWithMime(base64String, mimeType);
setFileData({ blob, mimeType });
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = () => {
setFileUrl(reader.result);
};
reader.onerror = () => {
throw new Error("Fail to load the file");
};
Comment on lines +259 to +261
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unhandled throw inside reader.onerror callback.

Throwing inside an async callback (FileReader.onerror) won't be caught by any surrounding try/catch or .catch() — it results in an unhandled error. This should set the error state instead.

Proposed fix
        reader.onerror = () => {
-          throw new Error("Fail to load the file");
+          setFileErrMsg("Failed to load the file");
        };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reader.onerror = () => {
throw new Error("Fail to load the file");
};
reader.onerror = () => {
setFileErrMsg("Failed to load the file");
};
🤖 Prompt for AI Agents
In `@frontend/src/components/custom-tools/document-manager/DocumentManager.jsx`
around lines 259 - 261, The FileReader.onerror handler currently throws an
exception inside the async callback (reader.onerror) which becomes unhandled;
instead, update the component's error state inside that callback (e.g., call
your state setter such as setError or setFileError with a descriptive message
and the event/error details) and avoid throwing — locate reader.onerror in
DocumentManager.jsx and replace the throw new Error("Fail to load the file")
with a call that sets the component error state and optionally logs the event so
the UI can render the error.

} else {
// Non-PDF file (CSV, TXT, Excel, or non-convertible)
// data is text, not base64
setFileUrl("");
setFileData({ blob: null, mimeType });
// Auto-switch to Raw View for non-PDF files
setActiveKey("2");
}
} else if (viewType === viewTypes.extract) {
setExtractTxt(data?.data);
}
Expand Down Expand Up @@ -345,16 +354,19 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) {
};

const renderDoc = (docName, fileUrl, highlightData) => {
const fileType = docName?.split(".").pop().toLowerCase(); // Get the file extension
switch (fileType) {
case "pdf":
return <PdfViewer fileUrl={fileUrl} highlightData={highlightData} />;
case "txt":
case "md":
return <TextViewer fileUrl={fileUrl} />;
default:
return <div>Unsupported file type: {fileType}</div>;
// Use mimeType from response for rendering decisions
if (fileData.mimeType === "application/pdf") {
return <PdfViewer fileUrl={fileUrl} highlightData={highlightData} />;
}
// Non-PDF: show placeholder message
return (
<div className="text-viewer-layout">
<Typography.Text type="secondary">
Document preview is not available for this file type. Please index the
document and switch to Raw View.
</Typography.Text>
</div>
);
};

return (
Expand Down Expand Up @@ -467,7 +479,10 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) {
<DocumentViewer
doc={selectedDoc?.document_name}
isLoading={isDocLoading}
isContentAvailable={fileUrl?.length > 0}
isContentAvailable={
fileUrl?.length > 0 ||
(fileData.mimeType && fileData.mimeType !== "application/pdf")
}
Comment on lines +482 to +485
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

isContentAvailable can be true even when document fetch fails.

This condition considers content available when fileData.mimeType is set to a non-PDF value. However, since fileData isn't cleared on fetch errors (see handleGetDocsError), a previously loaded non-PDF document's mimeType can leak into the next document's state. This ties back to the stale fileData issue above — once that's fixed, this logic should work correctly.

🤖 Prompt for AI Agents
In `@frontend/src/components/custom-tools/document-manager/DocumentManager.jsx`
around lines 482 - 485, The isContentAvailable prop can remain as written but
you must prevent stale fileData from making it true after a failed fetch: update
handleGetDocsError to clear the previous fileData (e.g., reset fileData to
null/empty object) and/or set an explicit fetch-failed flag (e.g.,
fileFetchError) and include that in the isContentAvailable check; specifically,
modify handleGetDocsError to reset the state that holds the prior mimeType
(refer to fileData and handleGetDocsError) so that isContentAvailable (which
depends on fileUrl and fileData.mimeType) cannot be true after a fetch failure.

setOpenManageDocsModal={setOpenManageDocsModal}
errMsg={fileErrMsg}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,18 @@ function ManageDocsModal({
return; // Stop further execution
}

// If the file is not a PDF, show the modal for confirmation
if (fileType !== "application/pdf") {
// File types that can be uploaded directly without conversion
const DIRECT_UPLOAD_TYPES = new Set([
"application/pdf",
"text/plain",
"text/csv",
"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-excel.sheet.macroenabled.12",
]);

if (!DIRECT_UPLOAD_TYPES.has(fileType)) {
// Non-direct types: show ConfirmMultiDoc modal or error
if (!ConfirmMultiDoc) {
setAlertDetails({
type: "error",
Expand All @@ -600,7 +610,7 @@ function ManageDocsModal({
setFileToUpload(file); // Store the file to be uploaded
setIsModalVisible(true); // Show the modal
} else {
// If the file is a PDF, proceed with the upload immediately
// PDF, CSV, TXT, Excel — proceed with the upload immediately
resolve(file);
}
};
Expand Down