diff --git a/ui/litellm-dashboard/src/components/model_add/CredentialDeleteModal.tsx b/ui/litellm-dashboard/src/components/model_add/CredentialDeleteModal.tsx new file mode 100644 index 00000000000..aacafa9e0af --- /dev/null +++ b/ui/litellm-dashboard/src/components/model_add/CredentialDeleteModal.tsx @@ -0,0 +1,99 @@ +import React, { useState } from "react"; +import { Modal } from "antd"; +import { Button as TremorButton } from "@tremor/react"; +import { ExclamationIcon } from "@heroicons/react/outline"; + +interface CredentialDeleteModalProps { + isVisible: boolean; + onCancel: () => void; + onConfirm: () => void; + credentialName: string; +} + +const CredentialDeleteModal: React.FC = ({ + isVisible, + onCancel, + onConfirm, + credentialName, +}) => { + const [deleteConfirmInput, setDeleteConfirmInput] = useState(""); + const isValid = deleteConfirmInput === credentialName; + + const handleCancel = () => { + setDeleteConfirmInput(""); + onCancel(); + }; + + const handleConfirm = () => { + if (isValid) { + setDeleteConfirmInput(""); + onConfirm(); + } + }; + + return ( + + + Delete Credential + + } + open={isVisible} + footer={null} + onCancel={handleCancel} + closable={true} + destroyOnClose={true} + maskClosable={false} + > +
+
+
+ +
+
+

+ This action cannot be undone and may break existing integrations. +

+
+
+ +
+ + setDeleteConfirmInput(e.target.value)} + placeholder="Enter credential name exactly" + className="w-full px-4 py-3 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-base" + autoFocus + /> +
+ +
+ + Cancel + + + Delete Credential + +
+
+
+ ); +}; + +export default CredentialDeleteModal; \ No newline at end of file diff --git a/ui/litellm-dashboard/src/components/model_add/credentials.tsx b/ui/litellm-dashboard/src/components/model_add/credentials.tsx index 65b5a5ba4d2..2bc2980ad64 100644 --- a/ui/litellm-dashboard/src/components/model_add/credentials.tsx +++ b/ui/litellm-dashboard/src/components/model_add/credentials.tsx @@ -23,6 +23,7 @@ import { UploadProps } from "antd/es/upload"; import { PlusIcon } from "@heroicons/react/solid"; import { credentialListCall, credentialCreateCall, credentialDeleteCall, credentialUpdateCall, CredentialItem, CredentialsResponse } from "@/components/networking"; // Assume this is your networking function import AddCredentialsTab from "./add_credentials_tab"; +import CredentialDeleteModal from "./CredentialDeleteModal"; import { Form, message } from "antd"; interface CredentialsPanelProps { accessToken: string | null; @@ -37,6 +38,7 @@ const CredentialsPanel: React.FC = ({ accessToken, upload const [isAddModalOpen, setIsAddModalOpen] = useState(false); const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false); const [selectedCredential, setSelectedCredential] = useState(null); + const [credentialToDelete, setCredentialToDelete] = useState(null); const [form] = Form.useForm(); const restrictedFields = ['credential_name', 'custom_llm_provider']; @@ -119,9 +121,18 @@ const CredentialsPanel: React.FC = ({ accessToken, upload } const response = await credentialDeleteCall(accessToken, credentialName); message.success('Credential deleted successfully'); + setCredentialToDelete(null); fetchCredentials(accessToken); }; + const openDeleteModal = (credentialName: string) => { + setCredentialToDelete(credentialName); + }; + + const closeDeleteModal = () => { + setCredentialToDelete(null); + }; + return (
@@ -168,7 +179,7 @@ const CredentialsPanel: React.FC = ({ accessToken, upload icon={TrashIcon} variant="light" size="sm" - onClick={() => handleDeleteCredential(credential.credential_name)} + onClick={() => openDeleteModal(credential.credential_name)} /> @@ -208,6 +219,15 @@ const CredentialsPanel: React.FC = ({ accessToken, upload addOrEdit="edit" /> )} + + {credentialToDelete && ( + handleDeleteCredential(credentialToDelete)} + credentialName={credentialToDelete} + /> + )}
); };