fix(dashboard): policy attachment delete button and confirm modal

Use a native button for the trash action so clicks work with Tooltip.
Replace Modal.confirm with DeleteResourceModal for a consistent confirm step.
Update attachment table tests for the new accessible control.

Made-with: Cursor
This commit is contained in:
shivam 2026-04-10 18:08:12 -07:00
parent 4e12d3c562
commit 8eb5f4eea5
No known key found for this signature in database
3 changed files with 62 additions and 31 deletions

View file

@ -111,14 +111,14 @@ describe("AttachmentTable", () => {
const attachment = makeAttachment({ attachment_id: "att-del-me1" });
const user = userEvent.setup();
renderWithProviders(<AttachmentTable {...defaultProps} attachments={[attachment]} />);
await user.click(screen.getByRole("button", { name: /TrashIcon/i }));
await user.click(screen.getByRole("button", { name: /delete attachment/i }));
expect(defaultProps.onDeleteClick).toHaveBeenCalledWith("att-del-me1");
});
it("should not show the delete icon for non-admins", () => {
const attachment = makeAttachment();
renderWithProviders(<AttachmentTable {...defaultProps} attachments={[attachment]} isAdmin={false} />);
expect(screen.queryByRole("button", { name: /TrashIcon/i })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: /delete attachment/i })).not.toBeInTheDocument();
});
it("should show a truncated attachment ID in the table", () => {

View file

@ -202,12 +202,17 @@ const AttachmentTable: React.FC<AttachmentTableProps> = ({
<ImpactPopover attachment={attachment} accessToken={accessToken} />
{isAdmin && (
<Tooltip title="Delete attachment">
<Icon
icon={TrashIcon}
size="sm"
onClick={() => onDeleteClick(attachment.attachment_id)}
className="cursor-pointer hover:text-red-500"
/>
<button
type="button"
aria-label="Delete attachment"
onClick={(e) => {
e.stopPropagation();
onDeleteClick(attachment.attachment_id);
}}
className="inline-flex items-center justify-center p-0 border-0 bg-transparent text-inherit cursor-pointer hover:text-red-500"
>
<Icon icon={TrashIcon} size="sm" />
</button>
</Tooltip>
)}
</div>

View file

@ -1,8 +1,8 @@
import React, { useState, useEffect, useCallback } from "react";
import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
import { Modal, Alert } from "antd";
import { Alert } from "antd";
import MessageManager from "@/components/molecules/message_manager";
import { ExclamationCircleOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { InfoCircleOutlined } from "@ant-design/icons";
import { isAdminRole } from "@/utils/roles";
import PolicyTable from "./policy_table";
import PolicyInfoView from "./policy_info";
@ -57,6 +57,9 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
const [isDeleting, setIsDeleting] = useState(false);
const [policyToDelete, setPolicyToDelete] = useState<Policy | null>(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [attachmentToDelete, setAttachmentToDelete] = useState<PolicyAttachment | null>(null);
const [isAttachmentDeleteModalOpen, setIsAttachmentDeleteModalOpen] = useState(false);
const [isDeletingAttachment, setIsDeletingAttachment] = useState(false);
const [isGuardrailSelectionModalOpen, setIsGuardrailSelectionModalOpen] = useState(false);
const [selectedTemplate, setSelectedTemplate] = useState<any>(null);
const [existingGuardrailNames, setExistingGuardrailNames] = useState<Set<string>>(new Set());
@ -166,26 +169,34 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
setPolicyToDelete(null);
};
const handleDeleteAttachment = (attachmentId: string) => {
Modal.confirm({
title: "Delete Attachment",
icon: <ExclamationCircleOutlined />,
content: "Are you sure you want to delete this attachment? This action cannot be undone.",
okText: "Delete",
okType: "danger",
cancelText: "Cancel",
onOk: async () => {
if (!accessToken) return;
try {
await deletePolicyAttachmentCall(accessToken, attachmentId);
MessageManager.success("Attachment deleted successfully");
fetchAttachments();
} catch (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;
if (!attachment) return;
setAttachmentToDelete(attachment);
setIsAttachmentDeleteModalOpen(true);
};
const handleAttachmentDeleteConfirm = async () => {
if (!attachmentToDelete || !accessToken) return;
setIsDeletingAttachment(true);
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");
} finally {
setIsDeletingAttachment(false);
setIsAttachmentDeleteModalOpen(false);
setAttachmentToDelete(null);
}
};
const handleAttachmentDeleteCancel = () => {
setIsAttachmentDeleteModalOpen(false);
setAttachmentToDelete(null);
};
const handleAttachmentSuccess = () => {
@ -579,11 +590,26 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
<AttachmentTable
attachments={attachmentsList}
isLoading={isAttachmentsLoading}
onDeleteClick={handleDeleteAttachment}
onDeleteClick={handleDeleteAttachmentClick}
isAdmin={isAdmin}
accessToken={accessToken}
/>
<DeleteResourceModal
isOpen={isAttachmentDeleteModalOpen}
title="Delete Attachment"
message="Are you sure you want to delete this policy attachment? This action cannot be undone."
resourceInformationTitle="Attachment Information"
resourceInformation={[
{ label: "Policy", value: attachmentToDelete?.policy_name },
{ label: "Attachment ID", value: attachmentToDelete?.attachment_id, code: true },
{ label: "Scope", value: attachmentToDelete?.scope || "-" },
]}
onCancel={handleAttachmentDeleteCancel}
onOk={handleAttachmentDeleteConfirm}
confirmLoading={isDeletingAttachment}
/>
<AddAttachmentForm
visible={isAddAttachmentModalVisible}
onClose={() => setIsAddAttachmentModalVisible(false)}