Add a delete button to the library detail page
Staff and uploaders (for their own items) get a Delete action on /detail/J-<id> with an inline confirmation; on success it returns to the library and refreshes files, duplicates and storage.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowLeft, Download } from "lucide-react";
|
||||
import { ArrowLeft, Download, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
@@ -296,8 +296,10 @@ function CustomDataCard({ item, canEdit }: { item: MediaItem; canEdit: boolean }
|
||||
|
||||
export function LibraryDetail({ jId }: { jId: string }) {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const ordering = useUi((state) => state.ordering);
|
||||
const user = useAuth((state) => state.user);
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
const rawUrl = `/api/files/${jId}/raw/`;
|
||||
|
||||
const query = useQuery({
|
||||
@@ -313,6 +315,20 @@ export function LibraryDetail({ jId }: { jId: string }) {
|
||||
),
|
||||
});
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: () =>
|
||||
api<{ deleted: string[]; errors: { j_id?: string; error: string }[] }>(
|
||||
"/api/delete/",
|
||||
{ method: "POST", json: { j_ids: [jId] } },
|
||||
),
|
||||
onSuccess: () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ["files"] });
|
||||
void queryClient.invalidateQueries({ queryKey: ["duplicates-md5"] });
|
||||
void queryClient.invalidateQueries({ queryKey: ["storage"] });
|
||||
navigate("/");
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
||||
@@ -361,6 +377,12 @@ export function LibraryDetail({ jId }: { jId: string }) {
|
||||
user.is_staff ||
|
||||
user.role === "staff"),
|
||||
);
|
||||
const canDelete = Boolean(
|
||||
user &&
|
||||
(user.is_superuser ||
|
||||
user.role === "staff" ||
|
||||
user.id === item.uploaded_by_id),
|
||||
);
|
||||
|
||||
return (
|
||||
<DetailShell
|
||||
@@ -381,6 +403,42 @@ export function LibraryDetail({ jId }: { jId: string }) {
|
||||
<a href={`${rawUrl}?download=1`} className={linkButtonClass}>
|
||||
<Download className="h-4 w-4" /> Download
|
||||
</a>
|
||||
{canDelete ? (
|
||||
confirmDelete ? (
|
||||
<>
|
||||
<span className="text-xs text-ctp-subtext0">
|
||||
Delete this item and all its copies?
|
||||
</span>
|
||||
<Button
|
||||
variant="danger"
|
||||
disabled={deleteMutation.isPending}
|
||||
onClick={() => deleteMutation.mutate()}
|
||||
>
|
||||
{deleteMutation.isPending ? "Deleting…" : "Confirm delete"}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setConfirmDelete(false)}
|
||||
disabled={deleteMutation.isPending}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={() => setConfirmDelete(true)}
|
||||
title="Delete this item from the library"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" /> Delete
|
||||
</Button>
|
||||
)
|
||||
) : null}
|
||||
{deleteMutation.isError ? (
|
||||
<p className="text-sm text-ctp-red">
|
||||
{errorMessage(deleteMutation.error)}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
media={
|
||||
|
||||
Reference in New Issue
Block a user