[Feat] Add CredentialDeleteModal component and integrate with CredentialsPanel (#13550)

* [Feat] Add CredentialDeleteModal component and integrate with CredentialsPanel

* Implement CredentialDeleteModal for confirming credential deletions.
* Update CredentialsPanel to manage delete modal state and handle credential deletion.
* Ensure user confirmation before deleting credentials to prevent accidental removals.

* Update CredentialDeleteModal to use smart quotes for credential name in confirmation message

* Enhance CredentialDeleteModal with confirmation input and validation

* Added input field for user to confirm credential deletion by typing the credential name.
* Updated modal behavior to handle confirmation and cancellation actions.
* Removed unnecessary state management in CredentialsPanel for delete modal visibility.

* Fix HTML entity for apostrophe in CredentialDeleteModal confirmation message
This commit is contained in:
Jugal D. Bhatt 2025-08-12 21:17:39 -07:00 • committed by GitHub
parent 1249385a99
commit e51c15705c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 120 additions and 1 deletions

View file

@ -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<CredentialDeleteModalProps> = ({
isVisible,
onCancel,
onConfirm,
credentialName,
}) => {
const [deleteConfirmInput, setDeleteConfirmInput] = useState("");
const isValid = deleteConfirmInput === credentialName;
const handleCancel = () => {
setDeleteConfirmInput("");
onCancel();
};
const handleConfirm = () => {
if (isValid) {
setDeleteConfirmInput("");
onConfirm();
}
};
return (
<Modal
title={
<div className="flex items-center">
<ExclamationIcon className="h-6 w-6 text-red-600 mr-2" />
Delete Credential
</div>
}
open={isVisible}
footer={null}
onCancel={handleCancel}
closable={true}
destroyOnClose={true}
maskClosable={false}
>
<div className="mt-4">
<div className="flex items-start gap-3 p-4 bg-red-50 border border-red-100 rounded-md mb-5">
<div className="text-red-500 mt-0.5">
<ExclamationIcon className="h-5 w-5" />
</div>
<div>
<p className="text-base font-medium text-red-600">
This action cannot be undone and may break existing integrations.
</p>
</div>
</div>
<div className="mb-5">
<label className="block text-base font-medium text-gray-700 mb-2">
{`Type `}
<span className="underline italic">&apos;{credentialName}&apos;</span>
{` to confirm deletion:`}
</label>
<input
type="text"
value={deleteConfirmInput}
onChange={(e) => 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
/>
</div>
<div className="flex justify-end space-x-2">
<TremorButton
onClick={handleCancel}
variant="secondary"
className="mr-2"
>
Cancel
</TremorButton>
<TremorButton
onClick={handleConfirm}
color="red"
className="focus:ring-red-500"
disabled={!isValid}
>
Delete Credential
</TremorButton>
</div>
</div>
</Modal>
);
};
export default CredentialDeleteModal;

View file

@ -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<CredentialsPanelProps> = ({ accessToken, upload
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const [isUpdateModalOpen, setIsUpdateModalOpen] = useState(false);
const [selectedCredential, setSelectedCredential] = useState<CredentialItem | null>(null);
const [credentialToDelete, setCredentialToDelete] = useState<string | null>(null);
const [form] = Form.useForm();
const restrictedFields = ['credential_name', 'custom_llm_provider'];
@ -119,9 +121,18 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ 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 (
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
<div className="flex justify-between items-center mb-4">
@ -168,7 +179,7 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ accessToken, upload
icon={TrashIcon}
variant="light"
size="sm"
onClick={() => handleDeleteCredential(credential.credential_name)}
onClick={() => openDeleteModal(credential.credential_name)}
/>
</TableCell>
</TableRow>
@ -208,6 +219,15 @@ const CredentialsPanel: React.FC<CredentialsPanelProps> = ({ accessToken, upload
addOrEdit="edit"
/>
)}
{credentialToDelete && (
<CredentialDeleteModal
isVisible={true}
onCancel={closeDeleteModal}
onConfirm={() => handleDeleteCredential(credentialToDelete)}
credentialName={credentialToDelete}
/>
)}
</div>
);
};