From c7694a9bbf6302a75c825d3a0da4f07e1bf2c44c Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Wed, 3 Jun 2026 01:29:22 +0330 Subject: [PATCH] feat(admin): multi-select bulk delete in media library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-file checkboxes + "حذف موارد انتخاب‌شده (N)" bar that deletes all selected files in parallel. Co-Authored-By: Claude Opus 4.8 --- src/components/admin/FileManager.tsx | 30 +++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/components/admin/FileManager.tsx b/src/components/admin/FileManager.tsx index 5cdcb6a..1d5219b 100644 --- a/src/components/admin/FileManager.tsx +++ b/src/components/admin/FileManager.tsx @@ -15,8 +15,17 @@ export function FileManager() { const [copied, setCopied] = useState(null); const [search, setSearch] = useState(""); const [type, setType] = useState(""); + const [selected, setSelected] = useState>(new Set()); const inputRef = useRef(null); + const toggleSel = (id: string) => + setSelected((s) => { + const n = new Set(s); + if (n.has(id)) n.delete(id); + else n.add(id); + return n; + }); + const reload = useCallback(async () => { setLoading(true); try { @@ -59,6 +68,14 @@ export function FileManager() { setTimeout(() => setCopied(null), 1500); }; + const bulkDelete = async () => { + if (selected.size === 0) return; + if (!confirm(`حذف ${selected.size.toLocaleString("fa-IR")} فایل انتخاب‌شده؟`)) return; + await Promise.all(Array.from(selected).map((id) => fetch(`/api/admin/resource/files/${id}`, { method: "DELETE" }))); + setSelected(new Set()); + reload(); + }; + return (
@@ -81,6 +98,11 @@ export function FileManager() { ))}
+ {selected.size > 0 && ( + + )} setSearch(e.target.value)} @@ -103,7 +125,13 @@ export function FileManager() { {files.map((f) => { const url = fileUrl(f); return ( -
+
+ toggleSel(f.id)} + className="absolute end-3 top-3 z-10 h-4 w-4 cursor-pointer accent-indigo-500" + />
{url && isImage(f) ? ( // eslint-disable-next-line @next/next/no-img-element