mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(ui): use mutation for attachment delete
Adopt a React Query mutation for policy attachment deletion and add a pending-state test on the policies index panel. This removes local delete-loading state and keeps modal loading tied to mutation status. Made-with: Cursor
This commit is contained in:
parent
727a6f21b8
commit
0ba8adffed
2 changed files with 55 additions and 11 deletions
|
|
@ -182,4 +182,39 @@ describe("PoliciesPanel attachment delete", () => {
|
|||
expect(networkingMocks.deletePolicyAttachmentCall).toHaveBeenCalledWith("test-token", EXPECTED_ATTACHMENT_ID);
|
||||
});
|
||||
});
|
||||
|
||||
it("should show mutation pending state while attachment delete is in flight", async () => {
|
||||
let resolveDelete: (() => void) | undefined;
|
||||
const deletePromise = new Promise<void>((resolve) => {
|
||||
resolveDelete = resolve;
|
||||
});
|
||||
networkingMocks.deletePolicyAttachmentCall.mockImplementationOnce(() => deletePromise);
|
||||
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<PoliciesPanel accessToken="test-token" userRole="Admin" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(networkingMocks.getPolicyAttachmentsList).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole("tab", { name: /^attachments$/i }));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("test-policy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /TrashIcon/i }));
|
||||
const dialog = await screen.findByRole("dialog", {}, { timeout: 5000 });
|
||||
|
||||
const deleteButton = within(dialog).getByRole("button", { name: /^delete$/i });
|
||||
await user.click(deleteButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(within(dialog).getByRole("button", { name: /deleting/i })).toBeDisabled();
|
||||
});
|
||||
|
||||
resolveDelete?.();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
|
||||
import { Alert } from "antd";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import MessageManager from "@/components/molecules/message_manager";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
|
|
@ -57,7 +58,6 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
|
|||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [policyToDelete, setPolicyToDelete] = useState<Policy | null>(null);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [isDeletingAttachment, setIsDeletingAttachment] = useState(false);
|
||||
const [attachmentToDelete, setAttachmentToDelete] = useState<PolicyAttachment | null>(null);
|
||||
const [isDeleteAttachmentModalOpen, setIsDeleteAttachmentModalOpen] = useState(false);
|
||||
const [isGuardrailSelectionModalOpen, setIsGuardrailSelectionModalOpen] = useState(false);
|
||||
|
|
@ -169,6 +169,22 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
|
|||
setPolicyToDelete(null);
|
||||
};
|
||||
|
||||
const deleteAttachmentMutation = useMutation({
|
||||
mutationFn: async (attachmentId: string) => {
|
||||
if (!accessToken) {
|
||||
throw new Error("Access token is required");
|
||||
}
|
||||
return deletePolicyAttachmentCall(accessToken, attachmentId);
|
||||
},
|
||||
onSuccess: async () => {
|
||||
MessageManager.success("Attachment deleted successfully");
|
||||
await fetchAttachments();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error deleting attachment:", error);
|
||||
MessageManager.error("Failed to delete attachment");
|
||||
},
|
||||
});
|
||||
const handleDeleteAttachmentClick = (attachmentId: string) => {
|
||||
const attachment = attachmentsList.find((a) => a.attachment_id === attachmentId) || null;
|
||||
setAttachmentToDelete(attachment);
|
||||
|
|
@ -181,17 +197,10 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
|
|||
};
|
||||
|
||||
const handleAttachmentDeleteConfirm = async () => {
|
||||
if (!attachmentToDelete || !accessToken) return;
|
||||
setIsDeletingAttachment(true);
|
||||
if (!attachmentToDelete) return;
|
||||
try {
|
||||
await deletePolicyAttachmentCall(accessToken, attachmentToDelete.attachment_id);
|
||||
MessageManager.success("Attachment deleted successfully");
|
||||
await fetchAttachments();
|
||||
} catch (error) {
|
||||
console.error("Error deleting attachment:", error);
|
||||
MessageManager.error("Failed to delete attachment");
|
||||
await deleteAttachmentMutation.mutateAsync(attachmentToDelete.attachment_id);
|
||||
} finally {
|
||||
setIsDeletingAttachment(false);
|
||||
setIsDeleteAttachmentModalOpen(false);
|
||||
setAttachmentToDelete(null);
|
||||
}
|
||||
|
|
@ -621,7 +630,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
|
|||
]}
|
||||
onCancel={handleAttachmentDeleteCancel}
|
||||
onOk={handleAttachmentDeleteConfirm}
|
||||
confirmLoading={isDeletingAttachment}
|
||||
confirmLoading={deleteAttachmentMutation.isPending}
|
||||
/>
|
||||
|
||||
<AiSuggestionModal
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue