mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-10 22:24:51 +00:00
Change modals to reusable component
This commit is contained in:
@@ -30,8 +30,7 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
} from "@tremor/react";
|
||||
import { Button as Button2, Form, Input, Modal, Select as Select2, Tooltip, Typography } from "antd";
|
||||
import { AlertTriangleIcon, XIcon } from "lucide-react";
|
||||
import { Button as Button2, Form, Input, Modal, Select as Select2, Switch, Tooltip, Typography } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { formatNumberWithCommas } from "../utils/dataUtils";
|
||||
import { fetchTeams } from "./common_components/fetch_teams";
|
||||
@@ -77,6 +76,7 @@ interface EditTeamModalProps {
|
||||
}
|
||||
|
||||
import { updateExistingKeys } from "@/utils/dataUtils";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
import { Member, teamCreateCall, v2TeamListCall } from "./networking";
|
||||
|
||||
interface TeamInfo {
|
||||
|
||||
@@ -57,10 +57,6 @@ export default function DeleteResourceModal({
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{alertMessage && <Alert message={alertMessage} type="warning" />}
|
||||
<div>
|
||||
<Text>{message}</Text>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 p-4 bg-red-50 rounded-lg border border-red-200">
|
||||
<Title level={5} className="mb-3 text-gray-900">
|
||||
{resourceInformationTitle}
|
||||
@@ -74,18 +70,23 @@ export default function DeleteResourceModal({
|
||||
))}
|
||||
</Descriptions>
|
||||
</div>
|
||||
<div>
|
||||
<Text>{message}</Text>
|
||||
</div>
|
||||
{requiredConfirmation && (
|
||||
<div className="mb-5">
|
||||
<div className="mb-6 mt-4 pt-4 border-t border-gray-200">
|
||||
<Text className="block text-base font-medium text-gray-700 mb-2">
|
||||
{`Type `}
|
||||
<span className="underline">{requiredConfirmation}</span>
|
||||
{` to confirm deletion:`}
|
||||
<Text>Type </Text>
|
||||
<Text strong type="danger">
|
||||
{requiredConfirmation}
|
||||
</Text>
|
||||
<Text> to confirm deletion:</Text>
|
||||
</Text>
|
||||
<Input
|
||||
value={requiredConfirmationInput}
|
||||
onChange={(e) => setRequiredConfirmationInput(e.target.value)}
|
||||
placeholder={requiredConfirmation}
|
||||
className="rounded-md"
|
||||
className="rounded-md text-base border-gray-200"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import GuardrailsPanel from "./guardrails";
|
||||
import { getGuardrailsList } from "./networking";
|
||||
|
||||
vi.mock("./networking", () => ({
|
||||
getGuardrailsList: vi.fn(),
|
||||
deleteGuardrailCall: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("./guardrails/add_guardrail_form", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>Mock Add Guardrail Form</div>,
|
||||
}));
|
||||
|
||||
vi.mock("./guardrails/guardrail_table", () => ({
|
||||
__esModule: true,
|
||||
default: ({ guardrailsList, onDeleteClick }: any) => (
|
||||
<div>
|
||||
<div>Mock Guardrail Table</div>
|
||||
{guardrailsList.length > 0 && (
|
||||
<button
|
||||
data-testid="delete-button"
|
||||
onClick={() => onDeleteClick(guardrailsList[0].guardrail_id, guardrailsList[0].guardrail_name)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./guardrails/guardrail_info", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>Mock Guardrail Info View</div>,
|
||||
}));
|
||||
|
||||
vi.mock("./guardrails/GuardrailTestPlayground", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>Mock Guardrail Test Playground</div>,
|
||||
}));
|
||||
|
||||
vi.mock("@/utils/roles", () => ({
|
||||
isAdminRole: vi.fn((role: string) => role === "admin"),
|
||||
}));
|
||||
|
||||
vi.mock("./guardrails/guardrail_info_helpers", () => ({
|
||||
getGuardrailLogoAndName: vi.fn(() => ({
|
||||
logo: null,
|
||||
displayName: "Test Provider",
|
||||
})),
|
||||
}));
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(window, "matchMedia", {
|
||||
writable: true,
|
||||
value: vi.fn().mockImplementation((query: string) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
})),
|
||||
});
|
||||
});
|
||||
|
||||
describe("GuardrailsPanel", () => {
|
||||
const defaultProps = {
|
||||
accessToken: "test-token",
|
||||
userRole: "admin",
|
||||
};
|
||||
|
||||
const mockGetGuardrailsList = vi.mocked(getGuardrailsList);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockGetGuardrailsList.mockResolvedValue({
|
||||
guardrails: [
|
||||
{
|
||||
guardrail_id: "test-guardrail-1",
|
||||
guardrail_name: "Test Guardrail",
|
||||
litellm_params: {
|
||||
guardrail: "test-provider",
|
||||
mode: "async",
|
||||
default_on: true,
|
||||
},
|
||||
guardrail_info: null,
|
||||
created_at: "2024-01-01T00:00:00Z",
|
||||
updated_at: "2024-01-01T00:00:00Z",
|
||||
guardrail_definition_location: "database" as any,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the component", async () => {
|
||||
render(<GuardrailsPanel {...defaultProps} />);
|
||||
expect(screen.getByText("Guardrails")).toBeInTheDocument();
|
||||
expect(screen.getByText("+ Add New Guardrail")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react";
|
||||
import { Modal } from "antd";
|
||||
import { getGuardrailsList, deleteGuardrailCall } from "./networking";
|
||||
import AddGuardrailForm from "./guardrails/add_guardrail_form";
|
||||
import GuardrailTable from "./guardrails/guardrail_table";
|
||||
@@ -9,6 +8,8 @@ import GuardrailInfoView from "./guardrails/guardrail_info";
|
||||
import GuardrailTestPlayground from "./guardrails/GuardrailTestPlayground";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
import { Guardrail, GuardrailDefinitionLocation } from "./guardrails/types";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
import { getGuardrailLogoAndName } from "./guardrails/guardrail_info_helpers";
|
||||
|
||||
interface GuardrailsPanelProps {
|
||||
accessToken: string | null;
|
||||
@@ -38,7 +39,8 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
||||
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [guardrailToDelete, setGuardrailToDelete] = useState<{ id: string; name: string } | null>(null);
|
||||
const [guardrailToDelete, setGuardrailToDelete] = useState<Guardrail | null>(null);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [selectedGuardrailId, setSelectedGuardrailId] = useState<string | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<number>(0);
|
||||
|
||||
@@ -81,7 +83,9 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
||||
};
|
||||
|
||||
const handleDeleteClick = (guardrailId: string, guardrailName: string) => {
|
||||
setGuardrailToDelete({ id: guardrailId, name: guardrailName });
|
||||
const guardrail = guardrailsList.find((g) => g.guardrail_id === guardrailId) || null;
|
||||
setGuardrailToDelete(guardrail);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
@@ -90,22 +94,29 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
||||
// Log removed to maintain clean production code
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteGuardrailCall(accessToken, guardrailToDelete.id);
|
||||
NotificationsManager.success(`Guardrail "${guardrailToDelete.name}" deleted successfully`);
|
||||
fetchGuardrails(); // Refresh the list
|
||||
await deleteGuardrailCall(accessToken, guardrailToDelete.guardrail_id);
|
||||
NotificationsManager.success(`Guardrail "${guardrailToDelete.guardrail_name}" deleted successfully`);
|
||||
await fetchGuardrails(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error("Error deleting guardrail:", error);
|
||||
NotificationsManager.fromBackend("Failed to delete guardrail");
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
setGuardrailToDelete(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setGuardrailToDelete(null);
|
||||
};
|
||||
|
||||
const providerDisplayName =
|
||||
guardrailToDelete && guardrailToDelete.litellm_params
|
||||
? getGuardrailLogoAndName(guardrailToDelete.litellm_params.guardrail).displayName
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
|
||||
<TabGroup index={activeTab} onIndexChange={setActiveTab}>
|
||||
@@ -148,20 +159,25 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
||||
onSuccess={handleSuccess}
|
||||
/>
|
||||
|
||||
{guardrailToDelete && (
|
||||
<Modal
|
||||
title="Delete Guardrail"
|
||||
open={guardrailToDelete !== null}
|
||||
onOk={handleDeleteConfirm}
|
||||
onCancel={handleDeleteCancel}
|
||||
confirmLoading={isDeleting}
|
||||
okText="Delete"
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<p>Are you sure you want to delete guardrail: {guardrailToDelete.name} ?</p>
|
||||
<p>This action cannot be undone.</p>
|
||||
</Modal>
|
||||
)}
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Guardrail"
|
||||
message={`Are you sure you want to delete guardrail: ${guardrailToDelete?.guardrail_name}? This action cannot be undone.`}
|
||||
resourceInformationTitle="Guardrail Information"
|
||||
resourceInformation={[
|
||||
{ label: "Name", value: guardrailToDelete?.guardrail_name },
|
||||
{ label: "ID", value: guardrailToDelete?.guardrail_id, code: true },
|
||||
{ label: "Provider", value: providerDisplayName },
|
||||
{ label: "Mode", value: guardrailToDelete?.litellm_params.mode },
|
||||
{
|
||||
label: "Default On",
|
||||
value: guardrailToDelete?.litellm_params.default_on ? "Yes" : "No",
|
||||
},
|
||||
]}
|
||||
onCancel={handleDeleteCancel}
|
||||
onOk={handleDeleteConfirm}
|
||||
confirmLoading={isDeleting}
|
||||
/>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
|
||||
@@ -32,6 +32,7 @@ import VectorStoreSelector from "./vector_store_management/VectorStoreSelector";
|
||||
import MCPServerSelector from "./mcp_server_management/MCPServerSelector";
|
||||
import { formatNumberWithCommas } from "../utils/dataUtils";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
|
||||
interface OrganizationsTableProps {
|
||||
organizations: Organization[];
|
||||
@@ -70,6 +71,7 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
||||
const [editOrg, setEditOrg] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [orgToDelete, setOrgToDelete] = useState<string | null>(null);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [isOrgModalVisible, setIsOrgModalVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [expandedAccordions, setExpandedAccordions] = useState<Record<string, boolean>>({});
|
||||
@@ -91,15 +93,18 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
||||
if (!orgToDelete || !accessToken) return;
|
||||
|
||||
try {
|
||||
setIsDeleting(true);
|
||||
await organizationDeleteCall(accessToken, orgToDelete);
|
||||
NotificationsManager.success("Organization deleted successfully");
|
||||
|
||||
setIsDeleteModalOpen(false);
|
||||
setOrgToDelete(null);
|
||||
// Refresh organizations list
|
||||
fetchOrganizations(accessToken, setOrganizations);
|
||||
await fetchOrganizations(accessToken, setOrganizations);
|
||||
} catch (error) {
|
||||
console.error("Error deleting organization:", error);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -506,40 +511,16 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
{isDeleteModalOpen ? (
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 transition-opacity" aria-hidden="true">
|
||||
<div className="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
|
||||
<div className="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
|
||||
<div className="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Delete Organization</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">Are you sure you want to delete this organization?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<Button onClick={confirmDelete} color="red" className="ml-2">
|
||||
Delete
|
||||
</Button>
|
||||
<Button onClick={cancelDelete}>Cancel</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Organization?"
|
||||
message="Are you sure you want to delete this organization? This action cannot be undone."
|
||||
resourceInformationTitle="Organization Information"
|
||||
resourceInformation={[{ label: "Organization ID", value: orgToDelete, code: true }]}
|
||||
onCancel={cancelDelete}
|
||||
onOk={confirmDelete}
|
||||
confirmLoading={isDeleting}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
import { LoggingCallbacksTable } from "./Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable";
|
||||
import { AlertingObject } from "./Settings/LoggingAndAlerts/LoggingCallbacks/types";
|
||||
import { parseErrorMessage } from "./shared/errorUtils";
|
||||
import DeleteResourceModal from "./common_components/DeleteResourceModal";
|
||||
interface SettingsPageProps {
|
||||
accessToken: string | null;
|
||||
userRole: string | null;
|
||||
@@ -240,9 +241,10 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
const [showEditCallback, setShowEditCallback] = useState(false);
|
||||
const [selectedEditCallback, setSelectedEditCallback] = useState<any | null>(null);
|
||||
const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
|
||||
const [callbackToDelete, setCallbackToDelete] = useState<string | null>(null);
|
||||
const [callbackToDelete, setCallbackToDelete] = useState<any | null>(null);
|
||||
const [isUpdatingCallback, setIsUpdatingCallback] = useState(false);
|
||||
const [isAddingCallback, setIsAddingCallback] = useState(false);
|
||||
const [isDeletingCallback, setIsDeletingCallback] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!accessToken) {
|
||||
@@ -525,8 +527,8 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteCallback = (callbackName: string) => {
|
||||
setCallbackToDelete(callbackName);
|
||||
const handleDeleteCallback = (callback: any) => {
|
||||
setCallbackToDelete(callback);
|
||||
setShowDeleteConfirmModal(true);
|
||||
};
|
||||
|
||||
@@ -536,8 +538,9 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteCallback(accessToken, callbackToDelete);
|
||||
NotificationsManager.success(`Callback ${callbackToDelete} deleted successfully`);
|
||||
setIsDeletingCallback(true);
|
||||
await deleteCallback(accessToken, callbackToDelete.name);
|
||||
NotificationsManager.success(`Callback ${callbackToDelete.name} deleted successfully`);
|
||||
|
||||
// Refresh the callbacks list
|
||||
if (userID && userRole) {
|
||||
@@ -550,6 +553,8 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
} catch (error) {
|
||||
console.error("Failed to delete callback:", error);
|
||||
NotificationsManager.fromBackend(error);
|
||||
} finally {
|
||||
setIsDeletingCallback(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -577,7 +582,7 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
setSelectedEditCallback(cb);
|
||||
setShowEditCallback(true);
|
||||
}}
|
||||
onDelete={(cb) => handleDeleteCallback(cb.name)}
|
||||
onDelete={(cb) => handleDeleteCallback(cb)}
|
||||
onTest={async (cb) => {
|
||||
try {
|
||||
await serviceHealthCheck(accessToken, cb.name);
|
||||
@@ -804,20 +809,22 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="Confirm Delete"
|
||||
open={showDeleteConfirmModal}
|
||||
onOk={confirmDeleteCallback}
|
||||
<DeleteResourceModal
|
||||
isOpen={showDeleteConfirmModal}
|
||||
title="Delete Callback"
|
||||
message="Are you sure you want to delete this callback? This action cannot be undone."
|
||||
resourceInformationTitle="Callback Information"
|
||||
resourceInformation={[
|
||||
{ label: "Callback Name", value: callbackToDelete?.name },
|
||||
{ label: "Mode", value: callbackToDelete?.mode || "success" },
|
||||
]}
|
||||
onCancel={() => {
|
||||
setShowDeleteConfirmModal(false);
|
||||
setCallbackToDelete(null);
|
||||
}}
|
||||
okText="Delete"
|
||||
cancelText="Cancel"
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<p>Are you sure you want to delete the callback - {callbackToDelete}? This action cannot be undone.</p>
|
||||
</Modal>
|
||||
onOk={confirmDeleteCallback}
|
||||
confirmLoading={isDeletingCallback}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
teamUpdateCall,
|
||||
getGuardrailsList,
|
||||
} from "@/components/networking";
|
||||
import { Button, Form, Input, Select, Switch, message, Modal, Tooltip } from "antd";
|
||||
import { Button, Form, Input, Select, Switch, message, Tooltip } from "antd";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { ArrowLeftIcon } from "@heroicons/react/outline";
|
||||
import MemberModal from "./edit_membership";
|
||||
@@ -44,6 +44,7 @@ import { copyToClipboard as utilCopyToClipboard } from "../../utils/dataUtils";
|
||||
import NotificationsManager from "../molecules/notifications_manager";
|
||||
import PassThroughRoutesSelector from "../common_components/PassThroughRoutesSelector";
|
||||
import { mapEmptyStringToNull } from "@/utils/keyUpdateUtils";
|
||||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
||||
|
||||
export interface TeamMembership {
|
||||
user_id: string;
|
||||
@@ -139,6 +140,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
||||
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
|
||||
const [guardrailsList, setGuardrailsList] = useState<string[]>([]);
|
||||
const [memberToDelete, setMemberToDelete] = useState<Member | null>(null);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
console.log("userModels in team info", userModels);
|
||||
@@ -272,6 +274,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
||||
|
||||
const handleMemberDelete = (member: Member) => {
|
||||
setMemberToDelete(member);
|
||||
setIsDeleteModalOpen(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
@@ -294,11 +297,13 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
||||
console.error("Error removing team member:", error);
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
setMemberToDelete(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setIsDeleteModalOpen(false);
|
||||
setMemberToDelete(null);
|
||||
};
|
||||
|
||||
@@ -924,28 +929,21 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
|
||||
/>
|
||||
|
||||
{/* Delete Member Confirmation Modal */}
|
||||
{memberToDelete && (
|
||||
<Modal
|
||||
title="Delete Team Member"
|
||||
open={memberToDelete !== null}
|
||||
onOk={handleDeleteConfirm}
|
||||
onCancel={handleDeleteCancel}
|
||||
confirmLoading={isDeleting}
|
||||
okText={isDeleting ? "Deleting..." : "Delete"}
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<p>Are you sure you want to remove this member from the team?</p>
|
||||
<p className="mt-2">
|
||||
<strong>User ID:</strong> {memberToDelete.user_id}
|
||||
</p>
|
||||
{memberToDelete.user_email && (
|
||||
<p>
|
||||
<strong>Email:</strong> {memberToDelete.user_email}
|
||||
</p>
|
||||
)}
|
||||
<p className="mt-2 text-red-600">This action cannot be undone.</p>
|
||||
</Modal>
|
||||
)}
|
||||
<DeleteResourceModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
title="Delete Team Member"
|
||||
alertMessage="Removing team members will also delete any keys created by or created for this member."
|
||||
message="Are you sure you want to remove this member from the team? This action cannot be undone."
|
||||
resourceInformationTitle="Team Member Information"
|
||||
resourceInformation={[
|
||||
{ label: "User ID", value: memberToDelete?.user_id, code: true },
|
||||
{ label: "Email", value: memberToDelete?.user_email },
|
||||
{ label: "Role", value: memberToDelete?.role },
|
||||
]}
|
||||
onCancel={handleDeleteCancel}
|
||||
onOk={handleDeleteConfirm}
|
||||
confirmLoading={isDeleting}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user