feat(admin): multi-select bulk delete in media library
Per-file checkboxes + "حذف موارد انتخابشده (N)" bar that deletes all selected files in parallel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,17 @@ export function FileManager() {
|
||||
const [copied, setCopied] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const [type, setType] = useState("");
|
||||
const [selected, setSelected] = useState<Set<string>>(new Set());
|
||||
const inputRef = useRef<HTMLInputElement>(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 (
|
||||
<div className="space-y-4" dir="rtl">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
@@ -81,6 +98,11 @@ export function FileManager() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{selected.size > 0 && (
|
||||
<button onClick={bulkDelete} className="rounded-lg border border-red-500/40 bg-red-500/10 px-3 py-1.5 text-xs text-red-300 hover:bg-red-500/20">
|
||||
حذف موارد انتخابشده ({selected.size.toLocaleString("fa-IR")})
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
className="ms-auto w-56 rounded-lg border border-[#262b40] bg-[#0c0e1a] px-3 py-2 text-sm text-gray-100 outline-none focus:border-indigo-500"
|
||||
placeholder="جستجوی نام فایل…" value={search} onChange={(e) => setSearch(e.target.value)}
|
||||
@@ -103,7 +125,13 @@ export function FileManager() {
|
||||
{files.map((f) => {
|
||||
const url = fileUrl(f);
|
||||
return (
|
||||
<div key={f.id} className="group rounded-lg border border-[#262b40] bg-[#0c0e1a] p-2">
|
||||
<div key={f.id} className={`group relative rounded-lg border bg-[#0c0e1a] p-2 ${selected.has(f.id) ? "border-indigo-500" : "border-[#262b40]"}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selected.has(f.id)}
|
||||
onChange={() => toggleSel(f.id)}
|
||||
className="absolute end-3 top-3 z-10 h-4 w-4 cursor-pointer accent-indigo-500"
|
||||
/>
|
||||
<div className="flex aspect-square items-center justify-center overflow-hidden rounded-md bg-[#070811]">
|
||||
{url && isImage(f) ? (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
|
||||
Reference in New Issue
Block a user