mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-19 02:18:37 +00:00
Normalize table action columns (#16657)
This commit is contained in:
+1
-11
@@ -1,14 +1,4 @@
|
||||
import {
|
||||
Button,
|
||||
Icon,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
Text,
|
||||
} from "@tremor/react";
|
||||
import { Button, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react";
|
||||
import { Tooltip } from "antd";
|
||||
import { formatNumberWithCommas } from "@/utils/dataUtils";
|
||||
import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
|
||||
|
||||
@@ -755,6 +755,7 @@ const Teams: React.FC<TeamProps> = ({
|
||||
<TableHeaderCell>Models</TableHeaderCell>
|
||||
<TableHeaderCell>Organization</TableHeaderCell>
|
||||
<TableHeaderCell>Info</TableHeaderCell>
|
||||
<TableHeaderCell>Actions</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
@@ -937,20 +938,28 @@ const Teams: React.FC<TeamProps> = ({
|
||||
<TableCell>
|
||||
{userRole == "Admin" ? (
|
||||
<>
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setSelectedTeamId(team.team_id);
|
||||
setEditTeam(true);
|
||||
}}
|
||||
/>
|
||||
<Icon
|
||||
onClick={() => handleDelete(team.team_id)}
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
data-testid="delete-team-button"
|
||||
/>
|
||||
<Tooltip title="Edit team">
|
||||
{" "}
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
className="cursor-pointer hover:text-blue-600"
|
||||
onClick={() => {
|
||||
setSelectedTeamId(team.team_id);
|
||||
setEditTeam(true);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete team">
|
||||
{" "}
|
||||
<Icon
|
||||
onClick={() => handleDelete(team.team_id)}
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
className="cursor-pointer hover:text-red-600"
|
||||
data-testid="delete-team-button"
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
) : null}
|
||||
</TableCell>
|
||||
|
||||
@@ -159,7 +159,7 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "",
|
||||
header: "Actions",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
const isConfigGuardrail = guardrail.guardrail_definition_location === GuardrailDefinitionLocation.CONFIG;
|
||||
@@ -177,16 +177,17 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
|
||||
/>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
guardrail.guardrail_id &&
|
||||
onDeleteClick(guardrail.guardrail_id, guardrail.guardrail_name || "Unnamed Guardrail")
|
||||
}
|
||||
className="cursor-pointer hover:text-red-500"
|
||||
tooltip="Delete guardrail"
|
||||
/>
|
||||
<Tooltip title="Delete guardrail">
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
guardrail.guardrail_id &&
|
||||
onDeleteClick(guardrail.guardrail_id, guardrail.guardrail_name || "Unnamed Guardrail")
|
||||
}
|
||||
className="cursor-pointer hover:text-red-500"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -290,23 +290,25 @@ export const columns = (
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "",
|
||||
header: () => <span className="text-sm font-semibold">Actions</span>,
|
||||
cell: ({ row }) => {
|
||||
const model = row.original;
|
||||
const canEditModel = userRole === "Admin" || model.model_info?.created_by === userID;
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2 pr-4">
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (canEditModel) {
|
||||
setSelectedModelId(model.model_info.id);
|
||||
setEditModel(false);
|
||||
}
|
||||
}}
|
||||
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
||||
/>
|
||||
<Tooltip title="Delete model">
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (canEditModel) {
|
||||
setSelectedModelId(model.model_info.id);
|
||||
setEditModel(false);
|
||||
}
|
||||
}}
|
||||
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:text-red-600"}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import React from "react";
|
||||
|
||||
vi.mock("./vector_store_management/VectorStoreSelector", () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}));
|
||||
vi.mock("./mcp_server_management/MCPServerSelector", () => ({
|
||||
__esModule: true,
|
||||
default: () => null,
|
||||
}));
|
||||
|
||||
import OrganizationsTable from "./organizations";
|
||||
|
||||
describe("OrganizationsTable", () => {
|
||||
it("should render the OrganizationsTable component", () => {
|
||||
const setOrganizations = vi.fn();
|
||||
|
||||
const { getByText } = render(
|
||||
<OrganizationsTable
|
||||
organizations={[]}
|
||||
userRole="Admin"
|
||||
userModels={[]}
|
||||
accessToken={null}
|
||||
setOrganizations={setOrganizations}
|
||||
premiumUser={true}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(getByText("+ Create New Organization")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -370,19 +370,27 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
|
||||
<TableCell>
|
||||
{userRole === "Admin" && (
|
||||
<>
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setSelectedOrgId(org.organization_id);
|
||||
setEditOrg(true);
|
||||
}}
|
||||
/>
|
||||
<Icon
|
||||
onClick={() => handleDelete(org.organization_id)}
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
/>
|
||||
<Tooltip title="Edit organization">
|
||||
{" "}
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
className="cursor-pointer hover:text-blue-600"
|
||||
onClick={() => {
|
||||
setSelectedOrgId(org.organization_id);
|
||||
setEditOrg(true);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete organization">
|
||||
{" "}
|
||||
<Icon
|
||||
onClick={() => handleDelete(org.organization_id)}
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
className="cursor-pointer hover:text-red-600"
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
@@ -109,17 +109,32 @@ export const columns = (
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "",
|
||||
header: "Actions",
|
||||
cell: ({ row }) => (
|
||||
<div className="flex gap-2">
|
||||
<Tooltip title="Edit user details" zIndex={9999}>
|
||||
<Icon icon={PencilAltIcon} size="sm" onClick={() => handleUserClick(row.original.user_id, true)} />
|
||||
<Tooltip title="Edit user details">
|
||||
<Icon
|
||||
icon={PencilAltIcon}
|
||||
size="sm"
|
||||
onClick={() => handleUserClick(row.original.user_id, true)}
|
||||
className="cursor-pointer hover:text-blue-600"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Delete user" zIndex={9999}>
|
||||
<Icon icon={TrashIcon} size="sm" onClick={() => handleDelete(row.original.user_id)} />
|
||||
<Tooltip title="Delete user">
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() => handleDelete(row.original.user_id)}
|
||||
className="cursor-pointer hover:text-red-600"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip title="Reset Password" zIndex={9999}>
|
||||
<Icon icon={RefreshIcon} size="sm" onClick={() => handleResetPassword(row.original.user_id)} />
|
||||
<Tooltip title="Reset Password">
|
||||
<Icon
|
||||
icon={RefreshIcon}
|
||||
size="sm"
|
||||
onClick={() => handleResetPassword(row.original.user_id)}
|
||||
className="cursor-pointer hover:text-green-600"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import React from "react";
|
||||
|
||||
import { UserDataTable } from "./table";
|
||||
|
||||
describe("UserDataTable", () => {
|
||||
it("should render the UserDataTable component", () => {
|
||||
const filters = {
|
||||
email: "",
|
||||
user_id: "",
|
||||
user_role: "",
|
||||
sso_user_id: "",
|
||||
team: "",
|
||||
model: "",
|
||||
min_spend: null,
|
||||
max_spend: null,
|
||||
sort_by: "",
|
||||
sort_order: "asc" as const,
|
||||
};
|
||||
|
||||
const updateFilters = vi.fn();
|
||||
|
||||
const { getByText } = render(
|
||||
<UserDataTable
|
||||
data={[]}
|
||||
columns={[]}
|
||||
accessToken={null}
|
||||
userRole={"Admin"}
|
||||
possibleUIRoles={null}
|
||||
filters={filters}
|
||||
updateFilters={updateFilters}
|
||||
initialFilters={filters}
|
||||
teams={[]}
|
||||
handleEdit={vi.fn()}
|
||||
handleDelete={vi.fn()}
|
||||
handleResetPassword={vi.fn()}
|
||||
userListResponse={{ users: [], total: 0, page: 1, page_size: 25, total_pages: 1 }}
|
||||
currentPage={1}
|
||||
handlePageChange={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(getByText("Filters")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user