import { useCallback, useMemo, useState } from "react"; /* ================= TYPES ================= */ export interface FileItem { id: string; file: File; path: string; progress: number; status: "idle" | "uploading" | "done" | "error"; url?: string; error?: string; } /* ================= HOOK ================= */ export function useFileUpload() { const [files, setFiles] = useState([]); const [isUploading, setIsUploading] = useState(false); /* ---------- ADD FILES ---------- */ const addFiles = useCallback((input: FileList | File[]) => { const list = Array.from(input).map((file) => ({ id: crypto.randomUUID(), file, path: file.name, // mặc định progress: 0, status: "idle", })); setFiles((prev) => [...prev, ...list]); }, []); /* ---------- REMOVE FILE ---------- */ const removeFile = useCallback((id: string) => { setFiles((prev) => prev.filter((f) => f.id !== id)); }, []); /* ---------- UPDATE PATH ---------- */ const updateFilePath = useCallback((id: string, path: string) => { setFiles((prev) => prev.map((f) => f.id === id ? { ...f, path } : f ) ); }, []); /* ---------- UPLOAD SINGLE ---------- */ const uploadOne = useCallback(async (item: FileItem) => { const form = new FormData(); form.append("file", item.file); form.append("path", item.path); return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("POST", "/api/upload"); xhr.upload.onprogress = (e) => { if (!e.lengthComputable) return; const percent = Math.round((e.loaded / e.total) * 100); setFiles((prev) => prev.map((f) => f.id === item.id ? { ...f, progress: percent, status: "uploading" } : f ) ); }; xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { const res = JSON.parse(xhr.responseText); setFiles((prev) => prev.map((f) => f.id === item.id ? { ...f, progress: 100, status: "done", url: res.url, } : f ) ); resolve(); } else { reject(new Error("Upload failed")); } }; xhr.onerror = () => reject(new Error("Network error")); xhr.send(form); }); }, []); /* ---------- START UPLOAD (App.tsx dùng) ---------- */ const startUpload = useCallback(async () => { if (isUploading) return; setIsUploading(true); try { for (const f of files) { if (f.status === "idle") { await uploadOne(f); } } } finally { setIsUploading(false); } }, [files, uploadOne, isUploading]); /* ---------- RETURN (KHỚP App.tsx) ---------- */ return { files, // ✅ App.tsx dùng isUploading, // ✅ App.tsx dùng addFiles, removeFile, // ✅ App.tsx dùng updateFilePath, // ✅ App.tsx dùng startUpload, // ✅ App.tsx dùng }; }