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 [copied, setCopied] = useState<string | null>(null);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const [type, setType] = useState("");
|
const [type, setType] = useState("");
|
||||||
|
const [selected, setSelected] = useState<Set<string>>(new Set());
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
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 () => {
|
const reload = useCallback(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
@@ -59,6 +68,14 @@ export function FileManager() {
|
|||||||
setTimeout(() => setCopied(null), 1500);
|
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 (
|
return (
|
||||||
<div className="space-y-4" dir="rtl">
|
<div className="space-y-4" dir="rtl">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
@@ -81,6 +98,11 @@ export function FileManager() {
|
|||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</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
|
<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"
|
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)}
|
placeholder="جستجوی نام فایل…" value={search} onChange={(e) => setSearch(e.target.value)}
|
||||||
@@ -103,7 +125,13 @@ export function FileManager() {
|
|||||||
{files.map((f) => {
|
{files.map((f) => {
|
||||||
const url = fileUrl(f);
|
const url = fileUrl(f);
|
||||||
return (
|
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]">
|
<div className="flex aspect-square items-center justify-center overflow-hidden rounded-md bg-[#070811]">
|
||||||
{url && isImage(f) ? (
|
{url && isImage(f) ? (
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
|
|||||||
Reference in New Issue
Block a user