import { jsxs, jsx } from "react/jsx-runtime"; import { B as Button } from "./button-CdJZJLGw.js"; import { I as Input } from "./input-BsvJqbcd.js"; import axios from "axios"; import { CheckCircle, Loader2 } from "lucide-react"; import { useState, useRef, useEffect } from "react"; import { toast } from "sonner"; import { I as InputError } from "./input-error-D1JIzedA.js"; const ChunkedUploaderInput = ({ storage, isSubmit, courseId, sectionId, filetype, delayUpload = false, onError, onCancelUpload, onFileSelected, onFileUploaded }) => { const [file, setFile] = useState(null); const [uploadId, setUploadId] = useState(null); const [errorMessage, setErrorMessage] = useState(""); const [uploadProgress, setUploadProgress] = useState(0); const [uploadStatus, setUploadStatus] = useState("idle"); const fileInputRef = useRef(null); const abortControllerRef = useRef(null); const maxFileSize = 1024 * 1024 * 1024; const chunkSize = 5 * 1024 * 1024; useEffect(() => { var _a, _b; axios.defaults.withCredentials = true; const token = (_a = document.cookie.split("; ").find((row) => row.startsWith("XSRF-TOKEN="))) == null ? void 0 : _a.split("=")[1]; if (token) { axios.defaults.headers.common["X-XSRF-TOKEN"] = decodeURIComponent(token); } else { const metaToken = (_b = document.querySelector('meta[name="csrf-token"]')) == null ? void 0 : _b.getAttribute("content"); if (metaToken) { axios.defaults.headers.common["X-CSRF-TOKEN"] = metaToken; } } }, []); useEffect(() => { if (isSubmit) { initiateUpload(); } }, [isSubmit]); const handleFileChange = (event) => { if (event.target.files && event.target.files.length > 0) { const selectedFile = event.target.files[0]; if (selectedFile.size > maxFileSize) { setErrorMessage(`File is too large. Maximum file size is ${maxFileSize / (1024 * 1024)} MB`); return; } setFile(selectedFile); setErrorMessage(""); setUploadStatus("idle"); setUploadProgress(0); if (delayUpload) { if (onFileSelected) { onFileSelected(selectedFile); } } else { initiateUpload(); } } }; const initiateUpload = async () => { var _a, _b, _c, _d; if (!file) return; setUploadStatus("initializing"); setErrorMessage(""); try { const totalChunks = Math.ceil(file.size / chunkSize); const response = await axios.post( "/dashboard/uploads/chunked/initialize", { storage, filename: file.name, mimetype: file.type, filesize: (file.size || 0) / 1024, filetype, total_chunks: totalChunks, course_id: courseId, course_section_id: sectionId }, { timeout: 12e4 // 2 minutes timeout for initialization request } ); if (response.data.success) { setUploadId(response.data.upload_id); await uploadChunks(response.data.upload_id, totalChunks); } else { throw new Error(response.data.message || "Failed to initialize upload"); } } catch (error) { setUploadStatus("error"); setErrorMessage(((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Failed to initialize upload"); if (onError) onError(((_d = (_c = error.response) == null ? void 0 : _c.data) == null ? void 0 : _d.message) || error.message || "Failed to initialize upload"); } }; const uploadChunks = async (uploadId2, totalChunks) => { var _a, _b, _c, _d; if (!file) return; setUploadStatus("uploading"); abortControllerRef.current = new AbortController(); const signal = abortControllerRef.current.signal; try { const uploadedParts = []; let uploadedChunks = 0; for (let chunkIndex = 0; chunkIndex < totalChunks && !signal.aborted; chunkIndex++) { const start = chunkIndex * chunkSize; const end = Math.min(start + chunkSize, file.size); const chunk = file.slice(start, end); const reader = new FileReader(); const readChunk = new Promise((resolve, reject) => { reader.onloadend = () => { const base64data2 = reader.result; resolve(base64data2); }; reader.onerror = () => reject(reader.error); reader.readAsDataURL(chunk); }); const base64data = await readChunk; const response = await axios.post( `/dashboard/uploads/chunked/${uploadId2}/chunk`, { storage, chunk_data: base64data, part_number: chunkIndex + 1, filename: file.name, mimetype: file.type }, { signal, timeout: 12e4, // 2 minute timeout for chunk uploads maxContentLength: Infinity, // Allow large content maxBodyLength: Infinity // Allow large body } ); if (response.data.success) { uploadedChunks++; const progress = Math.round(uploadedChunks / totalChunks * 100); setUploadProgress(progress); uploadedParts.push({ PartNumber: chunkIndex + 1, ETag: response.data.etag || "" // The server should return the ETag }); } else { throw new Error(response.data.message || "Failed to upload chunk"); } } if (signal.aborted) { return; } await completeUpload(uploadId2); } catch (error) { if (signal.aborted) { setUploadStatus("idle"); setUploadProgress(0); return; } setUploadStatus("error"); setErrorMessage(((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Failed to upload file chunks"); if (onError) onError(((_d = (_c = error.response) == null ? void 0 : _c.data) == null ? void 0 : _d.message) || error.message || "Failed to upload file chunks"); } }; const completeUpload = async (uploadId2) => { var _a, _b, _c, _d; setUploadStatus("completing"); try { const response = await axios.post( `/dashboard/uploads/chunked/${uploadId2}/complete`, { storage }, { timeout: 12e4 // 2 minute timeout for completion request } ); if (response.data.success) { setUploadStatus("completed"); const fileData = { file_path: response.data.file_path, file_url: response.data.file_url, signed_url: response.data.signed_url, mime_type: response.data.mime_type, file_name: response.data.file_name, file_size: response.data.file_size }; onFileUploaded == null ? void 0 : onFileUploaded(fileData); setTimeout(() => { if (fileInputRef.current) fileInputRef.current.value = ""; setFile(null); setUploadId(null); setUploadProgress(0); setUploadStatus("idle"); }, 3e3); } else { throw new Error(response.data.message || "Failed to complete upload"); } } catch (error) { setUploadStatus("error"); setErrorMessage(((_b = (_a = error.response) == null ? void 0 : _a.data) == null ? void 0 : _b.message) || error.message || "Failed to complete upload"); if (onError) onError(((_d = (_c = error.response) == null ? void 0 : _c.data) == null ? void 0 : _d.message) || error.message || "Failed to complete upload"); } }; const cancelUpload = async () => { if (uploadId && uploadStatus !== "idle" && uploadStatus !== "completed") { onCancelUpload == null ? void 0 : onCancelUpload(); if (abortControllerRef.current) { abortControllerRef.current.abort(); } try { await axios.delete(`/dashboard/uploads/chunked/${uploadId}/abort`); } catch (error) { toast.error("Error aborting upload:" + error); } setUploadStatus("idle"); setUploadProgress(0); if (fileInputRef.current) fileInputRef.current.value = ""; setFile(null); } }; const renderStatus = () => { switch (uploadStatus) { case "initializing": return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Initializing upload..." ] }); case "uploading": return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Uploading file chunks..." ] }); case "completing": return /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [ /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Finalizing upload..." ] }); case "completed": return /* @__PURE__ */ jsxs("div", { className: "text-secondary-foreground flex items-center", children: [ /* @__PURE__ */ jsx(CheckCircle, { className: "mr-2 h-4 w-4" }), "Completed upload" ] }); // case 'error': // return ( //
// // Error: {errorMessage} //
// ); default: return null; } }; return /* @__PURE__ */ jsxs("div", { children: [ /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-sm", children: [ /* @__PURE__ */ jsx( Input, { type: "file", name: "file", onChange: (e) => { var _a; handleFileChange(e); const file2 = (_a = e.target.files) == null ? void 0 : _a[0]; if (file2) { setFile(file2); onFileSelected == null ? void 0 : onFileSelected(file2); } } } ), uploadStatus === "uploading" && file && /* @__PURE__ */ jsxs("div", { className: "absolute top-0 left-0 z-10 flex h-full w-full items-center justify-between", children: [ /* @__PURE__ */ jsxs("div", { className: "relative h-full w-full overflow-hidden bg-gray-200", children: [ /* @__PURE__ */ jsx( "div", { className: "bg-secondary absolute top-0 left-0 h-full transition-all duration-300 ease-in-out", style: { width: `${uploadProgress}%` } } ), /* @__PURE__ */ jsxs("div", { className: "relative z-10 flex h-full items-center justify-between gap-2 px-2 text-xs", children: [ /* @__PURE__ */ jsxs("span", { children: [ uploadProgress, "%" ] }), renderStatus(), /* @__PURE__ */ jsxs("span", { className: "text-gray-800", children: [ "Size: (", (file ? file.size / (1024 * 1024) : 0).toFixed(2), " MB)" ] }) ] }) ] }), /* @__PURE__ */ jsx("div", { className: "bg-gray-200", children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", onClick: cancelUpload, className: "text-xs", children: "Cancel" }) }) ] }) ] }), errorMessage && /* @__PURE__ */ jsx(InputError, { message: errorMessage }) ] }); }; export { ChunkedUploaderInput as C };